summaryrefslogtreecommitdiffstats
path: root/kopete/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols')
-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
1546 files changed, 264832 insertions, 0 deletions
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. Wony <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 wewntrzna
+ *
+ * wywietla komunikat o danym poziomie, o ile uytkownik sobie tego yczy.
+ *
+ * - level - poziom wiadomoci
+ * - format... - tre wiadomoci (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 dokadnie to samo, co vsprintf(), tyle e alokuje sobie wczeniej
+ * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj
+ * vsnprintf() zgodn z C99, jak i na wczeniejszych.
+ *
+ * - format - opis wywietlanego tekstu jak dla printf()
+ * - ap - lista argumentw dla printf()
+ *
+ * zaalokowany bufor, ktry naley pniej zwolni, lub NULL
+ * jeli nie udao 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, wic
+ * musimy poda co istniejcego 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 dokadnie to samo, co sprintf(), tyle e alokuje sobie wczeniej
+ * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj
+ * vsnprintf() zgodn z C99, jak i na wczeniejszych.
+ *
+ * - format... - tre taka sama jak w funkcji printf()
+ *
+ * zaalokowany bufor, ktry naley pniej zwolni, lub NULL
+ * jeli nie udao 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, dzielc
+ * na kolejne stringi. zdarza si, nie ma potrzeby pisania funkcji dublujcej
+ * bufor eby tylko mie nieruszone dane wejciowe, skoro i tak nie bd nam
+ * poniej potrzebne. obcina `\r\n'.
+ *
+ * - ptr - wskanik do zmiennej, ktra przechowuje aktualn pozycj
+ * w przemiatanym buforze
+ *
+ * wskanik do kolejnej linii tekstu lub NULL, jeli 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 gupich zalenoci
+ * na dziwnych systemach.
+ *
+ * - addr - adres serwera (struct in_addr *)
+ * - port - port serwera
+ * - async - asynchroniczne poczenie
+ *
+ * deskryptor gniazda lub -1 w przypadku bdu (kod bdu 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 - wskanik do bufora
+ * - length - dugo bufora
+ *
+ * jeli trafi na bd odczytu lub podano nieprawidowe 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 koca linii.
+ *
+ * - line - linia do przycicia
+ */
+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 wewntrzna
+ *
+ * zamienia podany tekst na cig znakw do formularza http. przydaje si
+ * przy rnych usugach katalogu publicznego.
+ *
+ * - str - cig znakw do zakodowania
+ *
+ * zaalokowany bufor, ktry naley pniej zwolni albo NULL
+ * w przypadku bdu.
+ */
+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 wewntrzna
+ *
+ * funkcja liczca hash dla adresu e-mail, hasa i paru innych.
+ *
+ * - format... - format kolejnych parametrw ('s' jeli dany parametr jest
+ * cigiem znakw lub 'u' jeli numerem GG)
+ *
+ * hash wykorzystywany przy rejestracji i wszelkich manipulacjach wasnego
+ * 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() troszczcy si o wspbieno, gdy mamy do
+ * dyspozycji funkcj gethostbyname_r().
+ *
+ * - hostname - nazwa serwera
+ *
+ * zwraca wskanik na struktur in_addr, ktr naley 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, ktre byo ostatnio tworzone dla wtku
+ * o podanym identyfikatorze.
+ *
+ * jeli na win32 przy poczeniach synchronicznych zapamitamy w jakim
+ * wtku uruchomilimy funkcj, ktra si z czymkolwiek czy, to z osobnego
+ * wtku moemy anulowa poczenie poprzez gg_win32_thread_socket(watek, -1);
+ *
+ * - thread_id - id wtku. jeli jest rwne 0, brany jest aktualny wtek,
+ * jeli rwne -1, usuwa wpis o podanym sockecie.
+ * - socket - deskryptor gniazda. jeli rwne 0, zwraca deskryptor gniazda
+ * dla podanego wtku, jeli rwne -1, usuwa wpis, jeli co
+ * innego, ustawia dla podanego wtku dany numer deskryptora.
+ *
+ * jeli socket jest rwne 0, zwraca deskryptor gniazda dla podanego wtku.
+ */
+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 cig znakw w base64.
+ *
+ * - buf - cig znakw.
+ *
+ * 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 cig znakw z base64.
+ *
+ * - buf - cig znakw.
+ *
+ * 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 wewntrzna
+ *
+ * tworzy nagwek autoryzacji dla proxy.
+ *
+ * zaalokowany tekst lub NULL, jeli proxy nie jest wczone 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 wewntrzna
+ */
+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 jeli 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. Wony <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 Chiliski <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 wewntrzna
+ *
+ * wywietla 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()
+ *
+ * wysya informacj o tym, e dany klient powinien si z nami poczy.
+ * wykorzystywane, kiedy druga strona, ktrej chcemy co wysa jest za
+ * maskarad.
+ *
+ * - sess - struktura opisujca 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 wewntrzna
+ *
+ * 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()
+ *
+ * wypenia pola struct gg_dcc niezbdne do wysania pliku.
+ *
+ * - d - struktura opisujca poczenie 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()
+ *
+ * wypenia pola struct gg_dcc niezbdne do wysania pliku.
+ *
+ * - d - struktura opisujca poczenie DCC
+ * - filename - nazwa pliku
+ * - local_filename - nazwa na lokalnym systemie plikw
+ *
+ * 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 wewntrzna
+ *
+ * inicjuje proces wymiany pliku z danym klientem.
+ *
+ * - ip - adres ip odbiorcy
+ * - port - port odbiorcy
+ * - my_uin - wasny numer
+ * - peer_uin - numer obiorcy
+ * - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET)
+ *
+ * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ */
+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 wysa do
+ * nas danie poczenia.
+ *
+ * - ip - adres ip odbiorcy
+ * - port - port odbiorcy
+ * - my_uin - wasny numer
+ * - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ */
+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 wysyania pliku do danego klienta.
+ *
+ * - ip - adres ip odbiorcy
+ * - port - port odbiorcy
+ * - my_uin - wasny numer
+ * - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ */
+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()
+ *
+ * prbuje nawiza poczenie gosowe.
+ *
+ * - ip - adres ip odbiorcy
+ * - port - port odbiorcy
+ * - my_uin - wasny numer
+ * - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ */
+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 naley ustawi typ poczenia za
+ * pomoc tej funkcji.
+ *
+ * - d - struktura opisujca poczenie
+ * - type - typ poczenia (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 wewntrzna
+ *
+ * wywoywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza
+ * rezultat w struct gg_dcc->event.
+ *
+ * - d - structura opisujca poczenie
+ *
+ * 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 bezporedniej komunikacji midzy klientami.
+ *
+ * - uin - wasny numer
+ * - port - preferowany port, jeli rwny 0 lub -1, prbuje domylnego
+ *
+ * zaalokowana struct gg_dcc, ktr poniej naley zwolni funkcj
+ * gg_dcc_free(), albo NULL jeli wystpi bd.
+ */
+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()
+ *
+ * wysya ramk danych dla rozmowy gosowej.
+ *
+ * - d - struktura opisujca poczenie 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, ktr naley wywoa, gdy co si zmieni na gg_dcc->fd.
+ *
+ * - h - struktura zwrcona przez gg_create_dcc_socket()
+ *
+ * zaalokowana struct gg_event lub NULL, jeli zabrako pamici 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, wic 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);
+
+ /* bd */
+ 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;
+ }
+
+ /* jeli wczytalimy wicej, 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);
+
+ /* bd */
+ 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 poczenia 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. Wony <speedy@ziew.org>
+ * Arkadiusz Mikiewicz <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 - wskanik 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 wewntrzna
+ *
+ * parsuje przychodzcy 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 zakoczony \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;
+
+ /* jeli skoczono 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 wewntrzna
+ *
+ * obsuguje pakiet z przychodzc wiadomoci, rozbijajc go na dodatkowe
+ * struktury (konferencje, kolorki) w razie potrzeby.
+ *
+ * - h - nagwek 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 wewntrzna
+ *
+ * patrzy na gniazdo, odbiera pakiet i wypenia struktur zdarzenia.
+ *
+ * - sess - struktura opisujca 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;
+
+ /* jeli odpowied na eksport, wywoaj 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, ktr naley wywoa, gdy co si stanie z obserwowanym
+ * deskryptorem. zwraca klientowi informacj o tym, co si dzieje.
+ *
+ * - sess - opis sesji
+ *
+ * wskanik do struktury gg_event, ktr trzeba zwolni pniej
+ * za pomoc gg_event_free(). jesli rodzaj zdarzenia jest rwny
+ * GG_EVENT_NONE, naley je zignorowa. jeli zwrcio NULL,
+ * stao si co niedobrego -- albo zabrako pamici albo zerwao
+ * poczenie.
+ */
+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;
+ }
+
+ /* jeli jestemy w resolverze i mamy ustawiony port
+ * proxy, znaczy, e resolvowalimy proxy. zatem
+ * wpiszmy jego adres. */
+ if (sess->proxy_port)
+ sess->proxy_addr = addr.s_addr;
+
+ /* zapiszmy sobie adres huba i adres serwera (do
+ * bezporedniego poczenia, jeli hub ley)
+ * 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, zalenie
+ * od tego, co resolvowalimy. */
+ if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
+ /* jeli w trybie asynchronicznym gg_connect()
+ * zwrci bd, nie ma sensu prbowa dalej. */
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+ goto fail_connecting;
+ }
+
+ /* jeli podano serwer i czmy si przez proxy,
+ * jest to bezporednie poczenie, 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");
+
+ /* jeli asynchroniczne, sprawdzamy, czy nie wystpi
+ * przypadkiem jaki bd. */
+ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ /* no tak, nie udao si poczy z proxy. nawet
+ * nie prbujemy 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 bdach socket(),
+ * ioctl(), braku routingu itd. dlatego
+ * nawet nie prbujemy 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 krtkie, wic zawsze zmieci si
+ * do bufora gniazda. jeli write() zwrci mniej,
+ * stao si co zego. */
+ 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 porzdku. */
+ 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);
+
+ /* jeli otrzymalimy jakie dziwne informacje,
+ * prbujemy si czy z pominiciem huba. */
+ if (sess->proxy_addr && sess->proxy_port) {
+ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
+ /* trudno. nie wyszo. */
+ 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 nagwka. */
+ 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);
+
+ /* jeli pierwsza liczba w linii nie jest rwna 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) {
+ /* jeli mamy proxy, czymy si z nim. */
+ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
+ /* nie wyszo? 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 waciwym 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 wyszo? prbujemy portu 443. */
+ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
+ /* ostatnia deska ratunku zawioda?
+ * 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");
+
+ /* jeli wystpi bd podczas czenia si... */
+ if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ /* jeli nie udao si poczenie z proxy,
+ * nie mamy czego prbowa wicej. */
+ 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
+ /* jeli logujemy si po TLS, nie prbujemy
+ * si czy ju z niczym innym w przypadku
+ * bdu. 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;
+
+ /* prbujemy 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;
+
+ /* jeli mamy proxy, wylijmy 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);
+
+ /* wysyamy zapytanie. jest ono na tyle krtkie,
+ * e musi si zmieci w buforze gniazda. jeli
+ * write() zawiedzie, stao si co zego. */
+ 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 gupi 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. gupio, ale dziaa. */
+ 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 poczenie po http.
+ *
+ * - hostname - adres serwera
+ * - port - port serwera
+ * - async - asynchroniczne poczenie
+ * - method - metoda http (GET, POST, cokolwiek)
+ * - path - cieka do zasobu (musi by poprzedzona ,,/'')
+ * - header - nagwek zapytania plus ewentualne dane dla POST
+ *
+ * zaalokowana struct gg_http, ktr poniej naley
+ * zwolni funkcj gg_http_free(), albo NULL jeli wystpi bd.
+ */
+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 obsudze HTTP funkcj t naley wywoa, jeli
+ * zmienio si co na obserwowanym deskryptorze.
+ *
+ * - h - struktura opisujca poczenie
+ *
+ * jeli wszystko poszo dobrze to 0, inaczej -1. poczenie bdzie
+ * zakoczone, jeli h->state == GG_STATE_PARSING. jeli wystpi jaki
+ * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu 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()
+ *
+ * jeli poczenie jest w trakcie, przerywa je. nie zwalnia h->data.
+ *
+ * - h - struktura opisujca poczenie
+ */
+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 wewntrzna
+ *
+ * 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()
+ *
+ * prbuje zamkn poczenie i zwalnia pami po nim.
+ *
+ * - h - struktura, ktr naley 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. Wony <speedy@ziew.org>
+ * Arkadiusz Mikiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliski <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 opisujce 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 bajtw w liczbie 32-bitowej tak, by odpowiadaa
+ * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera,
+ * zamienia tylko na maszynach BE-wych.
+ *
+ * - x - liczba do zamiany
+ *
+ * liczba z odpowiedni kolejnoci bajtw.
+ */
+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 bajtw w liczbie 16-bitowej tak, by odpowiadaa
+ * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera,
+ * zamienia tylko na maszynach BE-wych.
+ *
+ * - x - liczba do zamiany
+ *
+ * liczba z odpowiedni kolejnoci bajtw.
+ */
+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 wewntrzna
+ *
+ * liczy hash z hasa i danego seeda.
+ *
+ * - password - haso 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 wewntrzna
+ *
+ * tworzy potok, forkuje si i w drugim procesie zaczyna resolvowa
+ * podanego hosta. zapisuje w sesji deskryptor potoku. jeli co tam
+ * bdzie gotowego, znaczy, e mona wczyta struct in_addr. jeli
+ * nie znajdzie, zwraca INADDR_NONE.
+ *
+ * - fd - wskanik 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 wewntrzna
+ *
+ * tworzy potok, nowy wtek i w nim zaczyna resolvowa podanego hosta.
+ * zapisuje w sesji deskryptor potoku. jeli co tam bdzie gotowego,
+ * znaczy, e mona wczyta struct in_addr. jeli nie znajdzie, zwraca
+ * INADDR_NONE.
+ *
+ * - fd - wskanik do zmiennej przechowujcej desktyptor resolvera
+ * - resolver - wskanik do wskanika 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 okrelon ilo bajtw. bierze pod uwag, czy mamy
+ * poczenie zwyke czy TLS.
+ *
+ * - sess - sesja,
+ * - buf - bufor,
+ * - length - ilo bajtw,
+ *
+ * takie same wartoci 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 okrelon ilo bajtw. bierze pod uwag, czy mamy
+ * poczenie zwyke czy TLS.
+ *
+ * - sess - sesja,
+ * - buf - bufor,
+ * - length - ilo bajtw,
+ *
+ * takie same wartoci 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 wewntrzna
+ *
+ * odbiera jeden pakiet i zwraca wskanik do niego. pami po nim
+ * naley zwolni za pomoc free().
+ *
+ * - sess - opis sesji
+ *
+ * w przypadku bdu NULL, kod bdu w errno. naley zwrci uwag, e gdy
+ * poczenie jest nieblokujce, a kod bdu wynosi EAGAIN, nie udao si
+ * odczyta caego pakietu i nie naley tego traktowa jako bd.
+ */
+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 wewntrzna
+ *
+ * konstruuje pakiet i wysya go do serwera.
+ *
+ * - sock - deskryptor gniazda
+ * - type - typ pakietu
+ * - payload_1 - pierwsza cz pakietu
+ * - payload_length_1 - dugo pierwszej czci
+ * - payload_2 - druga cz pakietu
+ * - payload_length_2 - dugo drugiej czci
+ * - ... - kolejne czci pakietu i ich dugoci
+ * - NULL - kocowym parametr (konieczny!)
+ *
+ * jeli si powiodo, zwraca 0, w przypadku bdu -1. jeli errno == ENOMEM,
+ * zabrako pamici. inaczej by bd przy wysyaniu pakietu. dla errno == 0
+ * nie wysano caego 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 wewntrzna
+ *
+ * wywoywany 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 obsuguje si przez
+ * gg_watch_fd().
+ *
+ * UWAGA! program musi obsuy SIGCHLD, jeli czy si asynchronicznie,
+ * eby poprawnie zamkn proces resolvera.
+ *
+ * - p - struktura opisujca pocztkowy stan. wymagane pola: uin,
+ * password
+ *
+ * w przypadku bdu NULL, jeli idzie dobrze (async) albo poszo
+ * dobrze (sync), zwrci wskanik 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()
+ *
+ * prbuje zamkn poczenia 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 mogo 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 uytkownika. przydatne do /away i /busy oraz /quit.
+ *
+ * - sess - opis sesji
+ * - status - nowy status uytkownika
+ *
+ * 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 uytkownika na opisowy.
+ *
+ * - sess - opis sesji
+ * - status - nowy status uytkownika
+ * - 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 uytkownika na opisowy z godzin powrotu.
+ *
+ * - sess - opis sesji
+ * - status - nowy status uytkownika
+ * - 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 uytkownika i zamyka poczenie, ale nie zwalnia pamici.
+ *
+ * - 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()
+ *
+ * wysya danie wysania 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()
+ *
+ * wysya 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 cieki, 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 kawaku 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()
+ *
+ * wysya wiadomo do innego uytkownika. zwraca losowy numer
+ * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomoci
+ * - recipient - numer adresata
+ * - message - tre wiadomoci
+ * - message_len - dugo
+ *
+ * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
+ */
+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()
+ *
+ * wysya wiadomo do innego uytkownika. zwraca losowy numer
+ * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomoci
+ * - recipient - numer adresata
+ * - message - tre wiadomoci
+ *
+ * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
+ */
+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()
+ *
+ * wysya kolorow wiadomo do innego uytkownika. zwraca losowy numer
+ * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomoci
+ * - recipient - numer adresata
+ * - message - tre wiadomoci
+ * - format - informacje o formatowaniu
+ * - formatlen - dugo informacji o formatowaniu
+ *
+ * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
+ */
+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()
+ *
+ * wysya wiadomo do kilku uytkownikow (konferencja). zwraca losowy numer
+ * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomoci
+ * - recipients_count - ilo adresatw
+ * - recipients - numerki adresatw
+ * - message - tre wiadomoci
+ *
+ * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
+ */
+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()
+ *
+ * wysya kolorow wiadomo do kilku uytkownikow (konferencja). zwraca
+ * losowy numer sekwencyjny, ktry mona zignorowa albo wykorzysta do
+ * potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomoci
+ * - recipients_count - ilo adresatw
+ * - recipients - numerki adresatw
+ * - message - tre wiadomoci
+ * - format - informacje o formatowaniu
+ * - formatlen - dugo informacji o formatowaniu
+ *
+ * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
+ */
+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()
+ *
+ * wysya 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()
+ *
+ * wysya serwerowi list kontaktw (wraz z odpowiadajcymi im typami userw),
+ * dziki czemu wie, czyj stan nas interesuje.
+ *
+ * - sess - opis sesji
+ * - userlist - wskanik do tablicy numerw
+ * - types - wskanik do tablicy typw uytkownikw
+ * - count - ilo numerkw
+ *
+ * 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()
+ *
+ * wysya serwerowi list kontaktw, dziki czemu wie, czyj stan nas
+ * interesuje.
+ *
+ * - sess - opis sesji
+ * - userlist - wskanik do tablicy numerw
+ * - count - ilo numerkw
+ *
+ * 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 kontaktw dany numer w trakcie poczenia.
+ * dodawanemu uytkownikowi okrelamy 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 kontaktw dany numer w trakcie poczenia.
+ *
+ * - 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 kontaktw w trakcie poczenia.
+ * usuwanemu uytkownikowi okrelamy 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 kontaktw w trakcie poczenia.
+ *
+ * - 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()
+ *
+ * wysya danie/zapytanie listy kontaktw na serwerze.
+ *
+ * - sess - opis sesji
+ * - type - rodzaj zapytania/dania
+ * - request - tre zapytania/dania (moe 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. Wony <speedy@ziew.org>
+ * Arkadiusz Mikiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliski <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 reprezentujcy numer osoby.
+ */
+typedef uint32_t uin_t;
+
+/*
+ * oglna struktura opisujca rne sesje. przydatna w klientach.
+ */
+#define gg_common_head(x) \
+ int fd; /* podgldany deskryptor */ \
+ int check; /* sprawdzamy zapis czy odczyt */ \
+ int state; /* aktualny stan maszynki */ \
+ int error; /* kod bdu 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 opisujca dan sesj. tworzona przez gg_login(), zwalniana
+ * przez gg_free_session().
+ */
+struct gg_session {
+ gg_common_head(struct gg_session)
+
+ int async; /* czy poczenie jest asynchroniczne */
+ int pid; /* pid procesu resolvera */
+ int port; /* port, z ktrym si czymy */
+ int seq; /* numer sekwencyjny ostatniej wiadomoci */
+ 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 resolvniciu */
+ uint32_t server_addr; /* adres serwera, od huba */
+
+ uint32_t client_addr; /* adres klienta */
+ uint16_t client_port; /* port, na ktrym klient sucha */
+
+ uint32_t external_addr; /* adres zewnetrzny klienta */
+ uint16_t external_port; /* port zewnetrzny klienta */
+
+ uin_t uin; /* numerek klienta */
+ char *password; /* i jego haso. zwalniane automagicznie */
+
+ int initial_status; /* pocztkowy 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 uywanego protokou */
+ char *client_version; /* wersja uywanego klienta */
+ int last_sysmsg; /* ostatnia wiadomo systemowa */
+
+ char *initial_descr; /* pocztkowy opis stanu klienta */
+
+ void *resolver; /* wskanik na informacje resolvera */
+
+ char *header_buf; /* bufor na pocztek nagwka */
+ 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 obrazkw w KiB */
+
+ char *userlist_reply; /* fragment odpowiedzi listy kontaktw */
+
+ int userlist_blocks; /* na ile kawakw podzielono list kontaktw */
+
+ struct gg_image_queue *images; /* aktualnie wczytywane obrazki */
+};
+
+/*
+ * struct gg_http
+ *
+ * oglna struktura opisujca 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 poczenie asynchroniczne */
+ int pid; /* pid procesu resolvera */
+ int port; /* port, z ktrym si czymy */
+
+ char *query; /* bufor zapytania http */
+ char *header; /* bufor nagwka */
+ int header_size; /* rozmiar wczytanego nagwka */
+ char *body; /* bufor otrzymanych informacji */
+ unsigned int body_size; /* oczekiwana ilo informacji */
+
+ void *data; /* dane danej operacji http */
+
+ char *user_data; /* dane uytkownika, nie s zwalniane przez gg_http_free() */
+
+ void *resolver; /* wskanik na informacje resolvera */
+
+ unsigned int body_done; /* ile ju treci 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 niezbdnej przy
+ * wysyaniu plikw.
+ */
+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 opisujca nasuchujce gniazdo pocze midzy 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 ktrym 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 kawaka */
+ unsigned int chunk_offset;/* offset w aktualnym kawaku */
+ struct gg_file_info file_info;
+ /* informacje o pliku */
+ int established; /* poczenie ustanowione */
+ char *voice_buf; /* bufor na pakiet poczenia gosowego */
+ int incoming; /* poczenie przychodzce */
+ char *chunk_buf; /* bufor na kawaek 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, /* poczenie z serwerem gg */
+ GG_SESSION_HTTP, /* oglna sesja http */
+ GG_SESSION_SEARCH, /* szukanie */
+ GG_SESSION_REGISTER, /* rejestrowanie */
+ GG_SESSION_REMIND, /* przypominanie hasa */
+ GG_SESSION_PASSWD, /* zmiana hasa */
+ GG_SESSION_CHANGE, /* zmiana informacji o sobie */
+ GG_SESSION_DCC, /* oglne poczenie DCC */
+ GG_SESSION_DCC_SOCKET, /* nasuchujcy socket */
+ GG_SESSION_DCC_SEND, /* wysyanie pliku */
+ GG_SESSION_DCC_GET, /* odbieranie pliku */
+ GG_SESSION_DCC_VOICE, /* rozmowa gosowa */
+ GG_SESSION_USERLIST_GET, /* pobieranie userlisty */
+ GG_SESSION_USERLIST_PUT, /* wysyanie userlisty */
+ GG_SESSION_UNREGISTER, /* usuwanie konta */
+ GG_SESSION_USERLIST_REMOVE, /* usuwanie userlisty */
+ GG_SESSION_TOKEN, /* pobieranie tokenu */
+
+ GG_SESSION_USER0 = 256, /* zdefiniowana dla uytkownika */
+ 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 {
+ /* wsplne */
+ GG_STATE_IDLE = 0, /* nie powinno wystpi. */
+ GG_STATE_RESOLVING, /* wywoa gethostbyname() */
+ GG_STATE_CONNECTING, /* wywoa connect() */
+ GG_STATE_READING_DATA, /* czeka na dane http */
+ GG_STATE_ERROR, /* wystpi bd. kod w x->error */
+
+ /* gg_session */
+ GG_STATE_CONNECTING_HUB, /* wywoa connect() na huba */
+ GG_STATE_CONNECTING_GG, /* wywoa connect() na serwer */
+ GG_STATE_READING_KEY, /* czeka na klucz */
+ GG_STATE_READING_REPLY, /* czeka na odpowied */
+ GG_STATE_CONNECTED, /* poczy si */
+
+ /* gg_http */
+ GG_STATE_SENDING_QUERY, /* wysya zapytanie http */
+ GG_STATE_READING_HEADER, /* czeka na nagwek http */
+ GG_STATE_PARSING, /* przetwarza dane */
+ GG_STATE_DONE, /* skoczy */
+
+ /* gg_dcc */
+ GG_STATE_LISTENING, /* czeka na poczenia */
+ GG_STATE_READING_UIN_1, /* czeka na uin peera */
+ GG_STATE_READING_UIN_2, /* czeka na swj uin */
+ GG_STATE_SENDING_ACK, /* wysya potwierdzenie dcc */
+ GG_STATE_READING_ACK, /* czeka na potwierdzenie dcc */
+ GG_STATE_READING_REQUEST, /* czeka na komend */
+ GG_STATE_SENDING_REQUEST, /* wysya komend */
+ GG_STATE_SENDING_FILE_INFO, /* wysya 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, /* wysya potwierdzenie pliku */
+ GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */
+ GG_STATE_SENDING_FILE_HEADER, /* wysya nagwek pliku */
+ GG_STATE_READING_FILE_HEADER, /* czeka na nagwek */
+ GG_STATE_GETTING_FILE, /* odbiera plik */
+ GG_STATE_SENDING_FILE, /* wysya 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, /* wysya potwierdzenie voip */
+ GG_STATE_SENDING_VOICE_REQUEST, /* wysya danie voip */
+ GG_STATE_READING_TYPE, /* czeka na typ poczenia */
+
+ /* nowe. bez sensu jest to API. */
+ GG_STATE_TLS_NEGOTIATION /* negocjuje poczenie TLS */
+};
+
+/*
+ * enum gg_check_t
+ *
+ * informuje, co proces klienta powinien sprawdzi na deskryptorze danego
+ * poczenia.
+ */
+enum gg_check_t {
+ GG_CHECK_NONE = 0, /* nic. nie powinno wystpi */
+ GG_CHECK_WRITE = 1, /* sprawdzamy moliwo zapisu */
+ GG_CHECK_READ = 2 /* sprawdzamy moliwo odczytu */
+};
+
+/*
+ * struct gg_login_params
+ *
+ * parametry gg_login(). przeniesiono do struktury, eby unikn problemw
+ * z cigymi zmianami API, gdy dodano co nowego do protokou.
+ */
+struct gg_login_params {
+ uin_t uin; /* numerek */
+ char *password; /* haso */
+ int async; /* asynchroniczne sockety? */
+ int status; /* pocztkowy 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 protokou */
+ char *client_version; /* wersja klienta */
+ int has_audio; /* czy ma dwik? */
+ 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; /* nastpny na licie */
+};
+
+/*
+ * enum gg_event_t
+ *
+ * rodzaje zdarze.
+ */
+enum gg_event_t {
+ GG_EVENT_NONE = 0, /* nic si nie wydarzyo */
+ 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 wysania wiadomoci */
+ GG_EVENT_PONG, /* pakiet pong */
+ GG_EVENT_CONN_FAILED, /* poczenie si nie udao */
+ GG_EVENT_CONN_SUCCESS, /* poczenie si powiodo */
+ GG_EVENT_DISCONNECT, /* serwer zrywa poczenie */
+
+ GG_EVENT_DCC_NEW, /* nowe poczenie midzy klientami */
+ GG_EVENT_DCC_ERROR, /* bd poczenia midzy klientami */
+ GG_EVENT_DCC_DONE, /* zakoczono poczenie */
+ GG_EVENT_DCC_CLIENT_ACCEPT, /* moment akceptacji klienta */
+ GG_EVENT_DCC_CALLBACK, /* klient si poczy na danie */
+ GG_EVENT_DCC_NEED_FILE_INFO, /* naley wypeni 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 gosowej */
+
+ GG_EVENT_PUBDIR50_SEARCH_REPLY, /* odpowiedz wyszukiwania */
+ GG_EVENT_PUBDIR50_READ, /* odczytano wasne dane z katalogu */
+ GG_EVENT_PUBDIR50_WRITE, /* wpisano wasne 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 kontaktw w GG 6.0 */
+ GG_EVENT_IMAGE_REQUEST, /* proba o wysanie obrazka GG 6.0 */
+ GG_EVENT_IMAGE_REPLY, /* podesany obrazek GG 6.0 */
+ GG_EVENT_DCC_ACK /* potwierdzenie transmisji */
+};
+
+#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
+
+/*
+ * enum gg_failure_t
+ *
+ * okrela powd nieudanego poczenia.
+ */
+enum gg_failure_t {
+ GG_FAILURE_RESOLVING = 1, /* nie znaleziono serwera */
+ GG_FAILURE_CONNECTING, /* nie mona si poczy */
+ GG_FAILURE_INVALID, /* serwer zwrci nieprawidowe dane */
+ GG_FAILURE_READING, /* zerwano poczenie podczas odczytu */
+ GG_FAILURE_WRITING, /* zerwano poczenie podczas zapisu */
+ GG_FAILURE_PASSWORD, /* nieprawidowe haso */
+ GG_FAILURE_404, /* XXX nieuywane */
+ GG_FAILURE_TLS, /* bd negocjacji TLS */
+ GG_FAILURE_NEED_EMAIL, /* serwer rozczy nas z prob o zmian emaila */
+ GG_FAILURE_INTRUDER, /* za duo prb poczenia si z nieprawidowym hasem */
+ GG_FAILURE_UNAVAILABLE /* serwery s wyczone */
+};
+
+/*
+ * enum gg_error_t
+ *
+ * okrela rodzaj bdu wywoanego przez dan operacj. nie zawiera
+ * przesadnie szczegowych informacji o powodzie bdu, by nie komplikowa
+ * obsugi bdw. jeli wymagana jest wiksza dokadno, naley sprawdzi
+ * zawarto zmiennej errno.
+ */
+enum gg_error_t {
+ GG_ERROR_RESOLVING = 1, /* bd znajdowania hosta */
+ GG_ERROR_CONNECTING, /* bd aczenia si */
+ GG_ERROR_READING, /* bd odczytu */
+ GG_ERROR_WRITING, /* bd wysyania */
+
+ GG_ERROR_DCC_HANDSHAKE, /* bd negocjacji */
+ GG_ERROR_DCC_FILE, /* bd odczytu/zapisu pliku */
+ GG_ERROR_DCC_EOF, /* plik si skoczy? */
+ GG_ERROR_DCC_NET, /* bd wysyania/odbierania */
+ GG_ERROR_DCC_REFUSED /* poczenie odrzucone przez usera */
+};
+
+/*
+ * struktury dotyczce wyszukiwania w GG 5.0. NIE NALEY SI DO NICH
+ * ODWOYWA BEZPOREDNIO! do dostpu do nich su 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 opisujcy zapytanie lub wynik zapytania katalogu publicznego
+ * z protokou GG 5.0. nie naley si odwoywa bezporednio do jego
+ * pl -- su do tego funkcje gg_pubdir50_*()
+ */
+typedef struct gg_pubdir50_s *gg_pubdir50_t;
+
+/*
+ * struct gg_event
+ *
+ * struktura opisujca 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 licie kontaktw -- GG_EVENT_NOTIFY */
+
+ enum gg_failure_t failure; /* bd poczenia -- GG_EVENT_FAILURE */
+
+ struct gg_dcc *dcc_new; /* nowe poczenie bezporednie -- GG_EVENT_DCC_NEW */
+
+ int dcc_error; /* bd poczenia bezporedniego -- GG_EVENT_DCC_ERROR */
+
+ gg_pubdir50_t pubdir50; /* wynik operacji zwizanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */
+
+ struct { /* @msg odebrano wiadomo -- GG_EVENT_MSG */
+ uin_t sender; /* numer nadawcy */
+ int msgclass; /* klasa wiadomoci */
+ time_t time; /* czas nadania */
+ unsigned char *message; /* tre wiadomoci */
+
+ int recipients_count; /* ilo odbiorcw konferencji */
+ uin_t *recipients; /* odbiorcy konferencji */
+
+ int formats_length; /* dugo informacji o formatowaniu tekstu */
+ void *formats; /* informacje o formatowaniu tekstu */
+ } msg;
+
+ struct { /* @notify_descr informacje o licie kontaktw z opisami stanu -- GG_EVENT_NOTIFY_DESCR */
+ struct gg_notify_reply *notify; /* informacje o licie kontaktw */
+ 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 licie kontaktw -- 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 wiadomoci -- GG_EVENT_ACK */
+ uin_t recipient; /* numer odbiorcy */
+ int status; /* stan dorczenia wiadomoci */
+ int seq; /* numer sekwencyjny wiadomoci */
+ } ack;
+
+ struct { /* @dcc_voice_data otrzymano dane dwikowe -- GG_EVENT_DCC_VOICE_DATA */
+ uint8_t *data; /* dane dwikowe */
+ int length; /* ilo danych dwikowych */
+ } dcc_voice_data;
+
+ struct { /* @userlist odpowied listy kontaktw serwera */
+ char type; /* rodzaj odpowiedzi */
+ char *reply; /* tre odpowiedzi */
+ } userlist;
+
+ struct { /* @image_request proba o obrazek */
+ uin_t sender; /* nadawca proby */
+ 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 obsugi listy kontaktw.
+ */
+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 obsugi 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 opisujca kryteria wyszukiwania dla gg_search(). nieaktualne,
+ * zastpione 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 obsugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje
+ * zachowuj pewien poziom abstrakcji, eby unikn zmian ABI przy zmianach
+ * w protokole.
+ *
+ * NIE NALEY SI ODWOYWA DO PL gg_pubdir50_t BEZPOREDNIO!
+ */
+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 udao */
+ uin_t uin; /* otrzymany numerek. 0 jeli bd */
+};
+
+/* oglne funkcje, nie powinny by uywane */
+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 znakw w tokenie */
+ char *tokenid; /* id tokenu */
+};
+
+/* funkcje dotyczce tokenw */
+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 hasa 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 hasa */
+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; /* pe */
+ 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 dotyczce listy kontaktw 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 dotyczce komunikacji midzy klientami.
+ */
+extern int gg_dcc_port; /* port, na ktrym nasuchuje klient */
+extern unsigned long gg_dcc_ip; /* adres, na ktrym nasuchuje 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
+
+/*
+ * jeli chcemy sobie podebugowa, wystarczy ustawi `gg_debug_level'.
+ * niestety w miar przybywania wpisw `gg_debug(...)' nie chciao mi
+ * si ustawia odpowiednich leveli, wic wikszo sza do _MISC.
+ */
+extern int gg_debug_level; /* poziom debugowania. mapa bitowa staych GG_DEBUG_* */
+
+/*
+ * mona poda wskanik do funkcji obsugujcej wywoania gg_debug().
+ * nieoficjalne, nieudokumentowane, moe si zmieni. jeli kto jest
+ * zainteresowany, niech da zna na ekg-devel.
+ */
+extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
+
+/*
+ * mona poda plik, do ktrego bd 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; /* wcza obsug proxy */
+extern char *gg_proxy_host; /* okrela adres serwera proxy */
+extern int gg_proxy_port; /* okrela port serwera proxy */
+extern char *gg_proxy_username; /* okrela nazw uytkownika przy autoryzacji serwera proxy */
+extern char *gg_proxy_password; /* okrela haso uytkownika przy autoryzacji serwera proxy */
+extern int gg_proxy_http_only; /* wcza obsug proxy wycznie dla usug HTTP */
+
+
+/*
+ * adres, z ktrego lemy pakiety (np czymy si z serwerem)
+ * uywany przy gg_connect()
+ */
+extern unsigned long gg_local_ip;
+/*
+ * -------------------------------------------------------------------------
+ * poniej znajduj si wewntrzne sprawy biblioteki. zwyky klient nie
+ * powinien ich w ogle rusza, bo i nie ma po co. wszystko mona zaatwi
+ * procedurami wyszego poziomu, ktrych definicje znajduj si na pocztku
+ * 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; /* dugo reszty pakietu */
+} GG_PACKED;
+
+#define GG_WELCOME 0x0001
+#define GG_NEED_EMAIL 0x0014
+
+struct gg_welcome {
+ uint32_t key; /* klucz szyfrowania hasa */
+} GG_PACKED;
+
+#define GG_LOGIN 0x000c
+
+struct gg_login {
+ uint32_t uin; /* mj numerek */
+ uint32_t hash; /* hash hasa */
+ uint32_t status; /* status na dzie dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint32_t local_ip; /* mj adres ip */
+ uint16_t local_port; /* port, na ktrym sucham */
+} GG_PACKED;
+
+#define GG_LOGIN_EXT 0x0013
+
+struct gg_login_ext {
+ uint32_t uin; /* mj numerek */
+ uint32_t hash; /* hash hasa */
+ uint32_t status; /* status na dzie dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint32_t local_ip; /* mj adres ip */
+ uint16_t local_port; /* port, na ktrym sucham */
+ uint32_t external_ip; /* zewntrzny adres ip */
+ uint16_t external_port; /* zewntrzny port */
+} GG_PACKED;
+
+#define GG_LOGIN60 0x0015
+
+struct gg_login60 {
+ uint32_t uin; /* mj numerek */
+ uint32_t hash; /* hash hasa */
+ uint32_t status; /* status na dzie dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint8_t dunno1; /* 0x00 */
+ uint32_t local_ip; /* mj adres ip */
+ uint16_t local_port; /* port, na ktrym sucham */
+ uint32_t external_ip; /* zewntrzny adres ip */
+ uint16_t external_port; /* zewntrzny 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 wysania zapytania */
+} GG_PACKED;
+
+#define GG_PUBDIR50_REPLY 0x000e
+
+struct gg_pubdir50_reply {
+ uint8_t type; /* GG_PUBDIR50_* */
+ uint32_t seq; /* czas wysania zapytania */
+} GG_PACKED;
+
+#define GG_NEW_STATUS 0x0002
+
+#define GG_STATUS_NOT_AVAIL 0x0001 /* niedostpny */
+#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedostpny z opisem (4.8) */
+#define GG_STATUS_AVAIL 0x0002 /* dostpny */
+#define GG_STATUS_AVAIL_DESCR 0x0004 /* dostpny z opisem (4.9) */
+#define GG_STATUS_BUSY 0x0003 /* zajty */
+#define GG_STATUS_BUSY_DESCR 0x0005 /* zajty 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 uwzgldnienia trybu tylko dla znajomych */
+#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
+
+/* GG_S_A() dostpny */
+#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_NA() niedostpny */
+#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
+
+/* GG_S_B() zajty */
+#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 blokujcy */
+#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 licie */
+} GG_PACKED;
+
+#define GG_USER_OFFLINE 0x01 /* bdziemy niewidoczni dla uytkownika */
+#define GG_USER_NORMAL 0x03 /* zwyky uytkownik */
+#define GG_USER_BLOCKED 0x04 /* zablokowany uytkownik */
+
+#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 ktrym sucha 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 ktrym sucha 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 ktrym sucha 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, stae, 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;
+
+/*
+ * pki co, nie znamy dokadnie protokou. nie wiemy, co czemu odpowiada.
+ * nazwy s niepowane i tymczasowe.
+ */
+#define GG_DCC_WANT_FILE 0x0003 /* peer chce plik */
+#define GG_DCC_HAVE_FILE 0x0001 /* wic 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 /* wysyamy 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 uytkownika protokoem GG 6.0. wymaga wczeniejszego
+ * pobrania tokenu za pomoc funkcji gg_token().
+ *
+ * - email - adres e-mail klienta
+ * - password - haso klienta
+ * - tokenid - identyfikator tokenu
+ * - tokenval - warto tokenu
+ * - async - poczenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, ktr poniej naley zwolni
+ * funkcj gg_register_free(), albo NULL jeli wystpi bd.
+ */
+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 uytkownika z serwera protokoem GG 6.0
+ *
+ * - uin - numerek GG
+ * - password - haso klienta
+ * - tokenid - identyfikator tokenu
+ * - tokenval - warto tokenu
+ * - async - poczenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, ktr poniej naley zwolni
+ * funkcj gg_unregister_free(), albo NULL jeli wystpi bd.
+ */
+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()
+ *
+ * wysya danie zmiany hasa zgodnie z protokoem GG 6.0. wymaga
+ * wczeniejszego pobrania tokenu za pomoc funkcji gg_token().
+ *
+ * - uin - numer
+ * - email - adres e-mail
+ * - passwd - stare haso
+ * - newpasswd - nowe haso
+ * - tokenid - identyfikator tokenu
+ * - tokenval - warto tokenu
+ * - async - poczenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, ktr poniej naley zwolni
+ * funkcj gg_change_passwd_free(), albo NULL jeli wystpi bd.
+ */
+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()
+ *
+ * wysya danie przypomnienia hasa e-mailem.
+ *
+ * - uin - numer
+ * - email - adres e-mail taki, jak ten zapisany na serwerze
+ * - async - poczenie asynchroniczne
+ * - tokenid - identyfikator tokenu
+ * - tokenval - warto tokenu
+ *
+ * zaalokowana struct gg_http, ktr poniej naley zwolni
+ * funkcj gg_remind_passwd_free(), albo NULL jeli wystpi bd.
+ */
+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 naley wywoywa
+ * t funkcj przy zmianach na obserwowanym deskryptorze.
+ *
+ * - h - struktura opisujca poczenie
+ *
+ * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie
+ * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki
+ * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu 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 zakadania konta, usuwania
+ * konta i zmiany hasa.
+ *
+ * zaalokowana struct gg_http, ktr poniej naley zwolni
+ * funkcj gg_token_free(), albo NULL jeli wystpi bd.
+ */
+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 zwizanych z tokenem naley wywoywa
+ * t funkcj przy zmianach na obserwowanym deskryptorze.
+ *
+ * - h - struktura opisujca poczenie
+ *
+ * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie
+ * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki
+ * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu 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;
+
+ /* jeli h->data jest puste, to cigalimy tokenid i url do niego,
+ * ale jeli co tam jest, to znaczy, e mamy drugi etap polegajcy
+ * 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;
+ }
+
+ /* dostalimy tokenid i wszystkie niezbdne informacje,
+ * wic 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 pamici.
+ */
+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 wewntrzna
+ *
+ * funkcja dodaje lub zastpuje istniejce pole do zapytania lub odpowiedzi.
+ *
+ * - req - wskanik 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 - wskanik 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 uytkownika.
+ *
+ * - 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()
+ *
+ * wysya zapytanie katalogu publicznego do serwera.
+ *
+ * - sess - sesja,
+ * - req - zapytanie.
+ *
+ * numer sekwencyjny wyszukiwania lub 0 w przypadku bdu.
+ */
+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 wewntrzna
+ *
+ * analizuje przychodzcy pakiet odpowiedzi i zapisuje wynik w struct gg_event.
+ *
+ * - e - opis zdarzenia
+ * - packet - zawarto pakietu odpowiedzi
+ * - length - dugo 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 wynikw? */
+ if (length == 5)
+ return 0;
+
+ /* pomi pocztek odpowiedzi */
+ p = packet + 5;
+
+ while (p < end) {
+ const char *field, *value;
+
+ field = p;
+
+ /* sprawd, czy nie mamy podziau na kolejne pole */
+ if (!*field) {
+ num++;
+ field++;
+ }
+
+ value = NULL;
+
+ for (p = field; p < end; p++) {
+ /* jeli mamy koniec tekstu... */
+ if (!*p) {
+ /* ...i jeszcze nie mielimy wartoci pola to
+ * wiemy, e po tym zerze jest warto... */
+ if (!value)
+ value = p + 1;
+ else
+ /* ...w przeciwym wypadku koniec
+ * wartoci i moemy wychodzi
+ * grzecznie z ptli */
+ break;
+ }
+ }
+
+ /* sprawdmy, czy pole nie wychodzi poza pakiet, eby nie
+ * mie segfaultw, jeli serwer przestanie zakacza pakietw
+ * przez \0 */
+
+ if (p == end) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n");
+ goto failure;
+ }
+
+ p++;
+
+ /* jeli dostalimy namier na nastpne wyniki, to znaczy e
+ * mamy koniec wynikw 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, jeli 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 wynikw danego zapytania.
+ *
+ * - res - odpowied
+ *
+ * ilo lub -1 w przypadku bdu.
+ */
+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 bdu.
+ */
+int gg_pubdir50_type(gg_pubdir50_t res)
+{
+ return (!res) ? -1 : res->type;
+}
+
+/*
+ * gg_pubdir50_next()
+ *
+ * zwraca numer, od ktrego naley rozpocz kolejne wyszukiwanie, jeli
+ * zaley nam na kolejnych wynikach.
+ *
+ * - res - odpowied
+ *
+ * numer lub -1 w przypadku bdu.
+ */
+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 bdu.
+ */
+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 Ravns <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 Ravns <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 Ravns <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 Ravns <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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 Lrkng <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