summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/jabber')
-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
529 files changed, 101021 insertions, 0 deletions
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