summaryrefslogtreecommitdiffstats
path: root/third_party
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-09-13 14:14:40 +0100
committerBen Murdoch <benm@google.com>2010-09-13 14:15:50 +0100
commitf74420b3285b9fe04a7e00aa3b8c0ab07ea344bc (patch)
tree38a5a185aef5b39d4880bf65ecbd132e7a3514e9 /third_party
parentd90229595b741bfdb2f8cd50a7eeba55330abf78 (diff)
downloadexternal_chromium-f74420b3285b9fe04a7e00aa3b8c0ab07ea344bc.zip
external_chromium-f74420b3285b9fe04a7e00aa3b8c0ab07ea344bc.tar.gz
external_chromium-f74420b3285b9fe04a7e00aa3b8c0ab07ea344bc.tar.bz2
Add libjingle to third_party
AutoFill requires this for it's XML parsing. Added from chromium src@52593 Change-Id: Ie0b1f600ed3a470f5d7eb4f939eba2f1d71e6fbe
Diffstat (limited to 'third_party')
-rw-r--r--third_party/libjingle/README.chromium12
-rw-r--r--third_party/libjingle/libjingle.Makefile6
-rw-r--r--third_party/libjingle/libjingle.gyp429
-rw-r--r--third_party/libjingle/libjingle.target.mk236
-rw-r--r--third_party/libjingle/libjingle_p2p.target.mk186
-rw-r--r--third_party/libjingle/mods-since-v0_4_0.diff1684
-rw-r--r--third_party/libjingle/overrides/config.h121
-rw-r--r--third_party/libjingle/overrides/talk/base/basictypes.h53
-rw-r--r--third_party/libjingle/overrides/talk/base/constructormagic.h14
-rw-r--r--third_party/libjingle/overrides/talk/base/logging.h386
-rw-r--r--third_party/libjingle/overrides/talk/base/scoped_ptr.h20
-rw-r--r--third_party/libjingle/overrides/talk/base/win32socketinit.cc22
-rw-r--r--third_party/libjingle/overrides/talk/xmllite/qname.cc65
-rw-r--r--third_party/libjingle/overrides/talk/xmllite/qname.h37
-rw-r--r--third_party/libjingle/source/AUTHORS1
-rw-r--r--third_party/libjingle/source/COPYING25
-rw-r--r--third_party/libjingle/source/README57
-rw-r--r--third_party/libjingle/source/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h55
-rw-r--r--third_party/libjingle/source/talk/base/asyncfile.h56
-rw-r--r--third_party/libjingle/source/talk/base/asynchttprequest.cc108
-rw-r--r--third_party/libjingle/source/talk/base/asynchttprequest.h111
-rw-r--r--third_party/libjingle/source/talk/base/asyncpacketsocket.cc92
-rw-r--r--third_party/libjingle/source/talk/base/asyncpacketsocket.h68
-rw-r--r--third_party/libjingle/source/talk/base/asyncsocket.h147
-rw-r--r--third_party/libjingle/source/talk/base/asynctcpsocket.cc201
-rw-r--r--third_party/libjingle/source/talk/base/asynctcpsocket.h73
-rw-r--r--third_party/libjingle/source/talk/base/asyncudpsocket.cc74
-rw-r--r--third_party/libjingle/source/talk/base/asyncudpsocket.h64
-rw-r--r--third_party/libjingle/source/talk/base/autodetectproxy.cc187
-rw-r--r--third_party/libjingle/source/talk/base/autodetectproxy.h90
-rw-r--r--third_party/libjingle/source/talk/base/base64.cc243
-rw-r--r--third_party/libjingle/source/talk/base/base64.h96
-rw-r--r--third_party/libjingle/source/talk/base/basicdefs.h37
-rw-r--r--third_party/libjingle/source/talk/base/basictypes.h101
-rw-r--r--third_party/libjingle/source/talk/base/bytebuffer.cc190
-rw-r--r--third_party/libjingle/source/talk/base/bytebuffer.h80
-rw-r--r--third_party/libjingle/source/talk/base/byteorder.h156
-rw-r--r--third_party/libjingle/source/talk/base/checks.cc47
-rw-r--r--third_party/libjingle/source/talk/base/checks.h44
-rw-r--r--third_party/libjingle/source/talk/base/common.cc71
-rw-r--r--third_party/libjingle/source/talk/base/common.h131
-rw-r--r--third_party/libjingle/source/talk/base/constructormagic.h55
-rw-r--r--third_party/libjingle/source/talk/base/criticalsection.h131
-rw-r--r--third_party/libjingle/source/talk/base/cryptstring.h198
-rw-r--r--third_party/libjingle/source/talk/base/diskcache.cc364
-rw-r--r--third_party/libjingle/source/talk/base/diskcache.h142
-rw-r--r--third_party/libjingle/source/talk/base/event.cc193
-rw-r--r--third_party/libjingle/source/talk/base/event.h71
-rw-r--r--third_party/libjingle/source/talk/base/fileutils.cc284
-rw-r--r--third_party/libjingle/source/talk/base/fileutils.h439
-rw-r--r--third_party/libjingle/source/talk/base/firewallsocketserver.cc246
-rw-r--r--third_party/libjingle/source/talk/base/firewallsocketserver.h133
-rw-r--r--third_party/libjingle/source/talk/base/flags.cc324
-rw-r--r--third_party/libjingle/source/talk/base/flags.h281
-rw-r--r--third_party/libjingle/source/talk/base/helpers.cc249
-rw-r--r--third_party/libjingle/source/talk/base/helpers.h55
-rw-r--r--third_party/libjingle/source/talk/base/host.cc49
-rw-r--r--third_party/libjingle/source/talk/base/host.h40
-rw-r--r--third_party/libjingle/source/talk/base/httpbase.cc895
-rw-r--r--third_party/libjingle/source/talk/base/httpbase.h201
-rw-r--r--third_party/libjingle/source/talk/base/httpclient.cc813
-rw-r--r--third_party/libjingle/source/talk/base/httpclient.h213
-rw-r--r--third_party/libjingle/source/talk/base/httpcommon-inl.h143
-rw-r--r--third_party/libjingle/source/talk/base/httpcommon.cc1054
-rw-r--r--third_party/libjingle/source/talk/base/httpcommon.h463
-rw-r--r--third_party/libjingle/source/talk/base/httprequest.cc127
-rw-r--r--third_party/libjingle/source/talk/base/httprequest.h132
-rw-r--r--third_party/libjingle/source/talk/base/linked_ptr.h142
-rw-r--r--third_party/libjingle/source/talk/base/linux.cc219
-rw-r--r--third_party/libjingle/source/talk/base/linux.h97
-rw-r--r--third_party/libjingle/source/talk/base/logging.cc586
-rw-r--r--third_party/libjingle/source/talk/base/logging.h381
-rw-r--r--third_party/libjingle/source/talk/base/macconversion.cc176
-rw-r--r--third_party/libjingle/source/talk/base/macconversion.h56
-rw-r--r--third_party/libjingle/source/talk/base/macutils.cc156
-rw-r--r--third_party/libjingle/source/talk/base/macutils.h62
-rw-r--r--third_party/libjingle/source/talk/base/md5.h45
-rw-r--r--third_party/libjingle/source/talk/base/md5c.c256
-rw-r--r--third_party/libjingle/source/talk/base/messagehandler.cc37
-rw-r--r--third_party/libjingle/source/talk/base/messagehandler.h46
-rw-r--r--third_party/libjingle/source/talk/base/messagequeue.cc370
-rw-r--r--third_party/libjingle/source/talk/base/messagequeue.h245
-rw-r--r--third_party/libjingle/source/talk/base/nethelpers.cc138
-rw-r--r--third_party/libjingle/source/talk/base/nethelpers.h76
-rw-r--r--third_party/libjingle/source/talk/base/network.cc456
-rw-r--r--third_party/libjingle/source/talk/base/network.h170
-rw-r--r--third_party/libjingle/source/talk/base/openssladapter.cc861
-rw-r--r--third_party/libjingle/source/talk/base/openssladapter.h105
-rw-r--r--third_party/libjingle/source/talk/base/opensslidentity.cc274
-rw-r--r--third_party/libjingle/source/talk/base/opensslidentity.h137
-rw-r--r--third_party/libjingle/source/talk/base/opensslstreamadapter.cc650
-rw-r--r--third_party/libjingle/source/talk/base/opensslstreamadapter.h171
-rw-r--r--third_party/libjingle/source/talk/base/pathutils.cc268
-rw-r--r--third_party/libjingle/source/talk/base/pathutils.h180
-rw-r--r--third_party/libjingle/source/talk/base/physicalsocketserver.cc1606
-rw-r--r--third_party/libjingle/source/talk/base/physicalsocketserver.h108
-rw-r--r--third_party/libjingle/source/talk/base/proxydetect.cc1253
-rw-r--r--third_party/libjingle/source/talk/base/proxydetect.h48
-rw-r--r--third_party/libjingle/source/talk/base/proxyinfo.cc37
-rw-r--r--third_party/libjingle/source/talk/base/proxyinfo.h59
-rw-r--r--third_party/libjingle/source/talk/base/schanneladapter.cc722
-rw-r--r--third_party/libjingle/source/talk/base/schanneladapter.h94
-rw-r--r--third_party/libjingle/source/talk/base/scoped_ptr.h257
-rw-r--r--third_party/libjingle/source/talk/base/sec_buffer.h173
-rw-r--r--third_party/libjingle/source/talk/base/signalthread.cc156
-rw-r--r--third_party/libjingle/source/talk/base/signalthread.h153
-rw-r--r--third_party/libjingle/source/talk/base/sigslot.h2816
-rw-r--r--third_party/libjingle/source/talk/base/sigslotrepeater.h107
-rw-r--r--third_party/libjingle/source/talk/base/socket.h165
-rw-r--r--third_party/libjingle/source/talk/base/socketadapters.cc910
-rw-r--r--third_party/libjingle/source/talk/base/socketadapters.h261
-rw-r--r--third_party/libjingle/source/talk/base/socketaddress.cc358
-rw-r--r--third_party/libjingle/source/talk/base/socketaddress.h191
-rw-r--r--third_party/libjingle/source/talk/base/socketfactory.h51
-rw-r--r--third_party/libjingle/source/talk/base/socketpool.cc278
-rw-r--r--third_party/libjingle/source/talk/base/socketpool.h160
-rw-r--r--third_party/libjingle/source/talk/base/socketserver.h61
-rw-r--r--third_party/libjingle/source/talk/base/socketstream.h149
-rw-r--r--third_party/libjingle/source/talk/base/ssladapter.cc102
-rw-r--r--third_party/libjingle/source/talk/base/ssladapter.h76
-rw-r--r--third_party/libjingle/source/talk/base/sslidentity.cc72
-rw-r--r--third_party/libjingle/source/talk/base/sslidentity.h91
-rw-r--r--third_party/libjingle/source/talk/base/sslsocketfactory.cc181
-rw-r--r--third_party/libjingle/source/talk/base/sslsocketfactory.h94
-rw-r--r--third_party/libjingle/source/talk/base/sslstreamadapter.cc70
-rw-r--r--third_party/libjingle/source/talk/base/sslstreamadapter.h123
-rw-r--r--third_party/libjingle/source/talk/base/stream.cc1017
-rw-r--r--third_party/libjingle/source/talk/base/stream.h748
-rw-r--r--third_party/libjingle/source/talk/base/stringdigest.cc49
-rw-r--r--third_party/libjingle/source/talk/base/stringdigest.h47
-rw-r--r--third_party/libjingle/source/talk/base/stringencode.cc579
-rw-r--r--third_party/libjingle/source/talk/base/stringencode.h183
-rw-r--r--third_party/libjingle/source/talk/base/stringutils.cc132
-rw-r--r--third_party/libjingle/source/talk/base/stringutils.h331
-rw-r--r--third_party/libjingle/source/talk/base/task.cc296
-rw-r--r--third_party/libjingle/source/talk/base/task.h194
-rw-r--r--third_party/libjingle/source/talk/base/taskparent.cc112
-rw-r--r--third_party/libjingle/source/talk/base/taskparent.h89
-rw-r--r--third_party/libjingle/source/talk/base/taskrunner.cc241
-rw-r--r--third_party/libjingle/source/talk/base/taskrunner.h117
-rw-r--r--third_party/libjingle/source/talk/base/thread.cc495
-rw-r--r--third_party/libjingle/source/talk/base/thread.h229
-rw-r--r--third_party/libjingle/source/talk/base/time.cc125
-rw-r--r--third_party/libjingle/source/talk/base/time.h81
-rw-r--r--third_party/libjingle/source/talk/base/unixfilesystem.cc517
-rw-r--r--third_party/libjingle/source/talk/base/unixfilesystem.h109
-rw-r--r--third_party/libjingle/source/talk/base/urlencode.cc196
-rw-r--r--third_party/libjingle/source/talk/base/urlencode.h60
-rw-r--r--third_party/libjingle/source/talk/base/win32.cc183
-rw-r--r--third_party/libjingle/source/talk/base/win32.h130
-rw-r--r--third_party/libjingle/source/talk/base/win32filesystem.cc369
-rw-r--r--third_party/libjingle/source/talk/base/win32filesystem.h114
-rw-r--r--third_party/libjingle/source/talk/base/win32securityerrors.cc66
-rw-r--r--third_party/libjingle/source/talk/base/win32socketinit.cc63
-rw-r--r--third_party/libjingle/source/talk/base/win32socketinit.h37
-rw-r--r--third_party/libjingle/source/talk/base/win32socketserver.cc811
-rw-r--r--third_party/libjingle/source/talk/base/win32socketserver.h174
-rw-r--r--third_party/libjingle/source/talk/base/win32window.cc134
-rw-r--r--third_party/libjingle/source/talk/base/win32window.h79
-rw-r--r--third_party/libjingle/source/talk/base/winfirewall.cc172
-rw-r--r--third_party/libjingle/source/talk/base/winfirewall.h73
-rw-r--r--third_party/libjingle/source/talk/base/winping.cc318
-rw-r--r--third_party/libjingle/source/talk/base/winping.h97
-rw-r--r--third_party/libjingle/source/talk/examples/call/call_main.cc290
-rw-r--r--third_party/libjingle/source/talk/examples/call/callclient.cc857
-rw-r--r--third_party/libjingle/source/talk/examples/call/callclient.h186
-rw-r--r--third_party/libjingle/source/talk/examples/call/console.cc167
-rw-r--r--third_party/libjingle/source/talk/examples/call/console.h85
-rw-r--r--third_party/libjingle/source/talk/examples/call/discoitemsquerytask.cc97
-rw-r--r--third_party/libjingle/source/talk/examples/call/discoitemsquerytask.h88
-rw-r--r--third_party/libjingle/source/talk/examples/call/friendinvitesendtask.cc76
-rw-r--r--third_party/libjingle/source/talk/examples/call/friendinvitesendtask.h48
-rw-r--r--third_party/libjingle/source/talk/examples/call/muc.h66
-rw-r--r--third_party/libjingle/source/talk/examples/call/mucinviterecvtask.cc124
-rw-r--r--third_party/libjingle/source/talk/examples/call/mucinviterecvtask.h81
-rw-r--r--third_party/libjingle/source/talk/examples/call/mucinvitesendtask.cc63
-rw-r--r--third_party/libjingle/source/talk/examples/call/mucinvitesendtask.h49
-rw-r--r--third_party/libjingle/source/talk/examples/call/presenceouttask.cc147
-rw-r--r--third_party/libjingle/source/talk/examples/call/presenceouttask.h53
-rw-r--r--third_party/libjingle/source/talk/examples/call/presencepushtask.cc254
-rw-r--r--third_party/libjingle/source/talk/examples/call/presencepushtask.h70
-rw-r--r--third_party/libjingle/source/talk/examples/call/status.h245
-rw-r--r--third_party/libjingle/source/talk/examples/call/voicemailjidrequester.cc135
-rw-r--r--third_party/libjingle/source/talk/examples/call/voicemailjidrequester.h126
-rw-r--r--third_party/libjingle/source/talk/examples/login/login_main.cc64
-rw-r--r--third_party/libjingle/source/talk/examples/login/xmppauth.cc87
-rw-r--r--third_party/libjingle/source/talk/examples/login/xmppauth.h72
-rw-r--r--third_party/libjingle/source/talk/examples/login/xmpppump.cc78
-rw-r--r--third_party/libjingle/source/talk/examples/login/xmpppump.h74
-rw-r--r--third_party/libjingle/source/talk/examples/login/xmppsocket.cc249
-rw-r--r--third_party/libjingle/source/talk/examples/login/xmppsocket.h81
-rw-r--r--third_party/libjingle/source/talk/examples/login/xmppthread.cc80
-rw-r--r--third_party/libjingle/source/talk/examples/login/xmppthread.h57
-rw-r--r--third_party/libjingle/source/talk/libjingle.scons204
-rw-r--r--third_party/libjingle/source/talk/main.scons434
-rw-r--r--third_party/libjingle/source/talk/p2p/base/candidate.h137
-rw-r--r--third_party/libjingle/source/talk/p2p/base/common.h36
-rw-r--r--third_party/libjingle/source/talk/p2p/base/constants.cc160
-rw-r--r--third_party/libjingle/source/talk/p2p/base/constants.h177
-rw-r--r--third_party/libjingle/source/talk/p2p/base/p2ptransport.cc194
-rw-r--r--third_party/libjingle/source/talk/p2p/base/p2ptransport.h73
-rw-r--r--third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc917
-rw-r--r--third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h164
-rw-r--r--third_party/libjingle/source/talk/p2p/base/parsing.cc134
-rw-r--r--third_party/libjingle/source/talk/p2p/base/parsing.h92
-rw-r--r--third_party/libjingle/source/talk/p2p/base/port.cc976
-rw-r--r--third_party/libjingle/source/talk/p2p/base/port.h429
-rw-r--r--third_party/libjingle/source/talk/p2p/base/portallocator.h105
-rw-r--r--third_party/libjingle/source/talk/p2p/base/pseudotcp.cc1069
-rw-r--r--third_party/libjingle/source/talk/p2p/base/pseudotcp.h183
-rw-r--r--third_party/libjingle/source/talk/p2p/base/rawtransport.cc124
-rw-r--r--third_party/libjingle/source/talk/p2p/base/rawtransport.h75
-rw-r--r--third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc278
-rw-r--r--third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h131
-rw-r--r--third_party/libjingle/source/talk/p2p/base/relayport.cc799
-rw-r--r--third_party/libjingle/source/talk/p2p/base/relayport.h116
-rw-r--r--third_party/libjingle/source/talk/p2p/base/session.cc672
-rw-r--r--third_party/libjingle/source/talk/p2p/base/session.h402
-rw-r--r--third_party/libjingle/source/talk/p2p/base/sessionclient.h103
-rw-r--r--third_party/libjingle/source/talk/p2p/base/sessiondescription.h55
-rw-r--r--third_party/libjingle/source/talk/p2p/base/sessionid.h97
-rw-r--r--third_party/libjingle/source/talk/p2p/base/sessionmanager.cc299
-rw-r--r--third_party/libjingle/source/talk/p2p/base/sessionmanager.h187
-rw-r--r--third_party/libjingle/source/talk/p2p/base/sessionmessages.cc375
-rw-r--r--third_party/libjingle/source/talk/p2p/base/sessionmessages.h204
-rw-r--r--third_party/libjingle/source/talk/p2p/base/stun.cc580
-rw-r--r--third_party/libjingle/source/talk/p2p/base/stun.h365
-rw-r--r--third_party/libjingle/source/talk/p2p/base/stunport.cc248
-rw-r--r--third_party/libjingle/source/talk/p2p/base/stunport.h119
-rw-r--r--third_party/libjingle/source/talk/p2p/base/stunrequest.cc200
-rw-r--r--third_party/libjingle/source/talk/p2p/base/stunrequest.h129
-rw-r--r--third_party/libjingle/source/talk/p2p/base/tcpport.cc256
-rw-r--r--third_party/libjingle/source/talk/p2p/base/tcpport.h138
-rw-r--r--third_party/libjingle/source/talk/p2p/base/transport.cc443
-rw-r--r--third_party/libjingle/source/talk/p2p/base/transport.h265
-rw-r--r--third_party/libjingle/source/talk/p2p/base/transportchannel.cc56
-rw-r--r--third_party/libjingle/source/talk/p2p/base/transportchannel.h110
-rw-r--r--third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h81
-rw-r--r--third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc100
-rw-r--r--third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h77
-rw-r--r--third_party/libjingle/source/talk/p2p/base/udpport.cc106
-rw-r--r--third_party/libjingle/source/talk/p2p/base/udpport.h89
-rw-r--r--third_party/libjingle/source/talk/p2p/client/basicportallocator.cc784
-rw-r--r--third_party/libjingle/source/talk/p2p/client/basicportallocator.h199
-rw-r--r--third_party/libjingle/source/talk/p2p/client/httpportallocator.cc243
-rw-r--r--third_party/libjingle/source/talk/p2p/client/httpportallocator.h134
-rw-r--r--third_party/libjingle/source/talk/p2p/client/sessionmanagertask.h92
-rw-r--r--third_party/libjingle/source/talk/p2p/client/sessionsendtask.h137
-rw-r--r--third_party/libjingle/source/talk/p2p/client/socketmonitor.cc154
-rw-r--r--third_party/libjingle/source/talk/p2p/client/socketmonitor.h91
-rw-r--r--third_party/libjingle/source/talk/session/phone/audiomonitor.cc121
-rw-r--r--third_party/libjingle/source/talk/session/phone/audiomonitor.h75
-rw-r--r--third_party/libjingle/source/talk/session/phone/call.cc443
-rw-r--r--third_party/libjingle/source/talk/session/phone/call.h134
-rw-r--r--third_party/libjingle/source/talk/session/phone/channel.cc969
-rw-r--r--third_party/libjingle/source/talk/session/phone/channel.h400
-rw-r--r--third_party/libjingle/source/talk/session/phone/channelmanager.cc752
-rw-r--r--third_party/libjingle/source/talk/session/phone/channelmanager.h200
-rw-r--r--third_party/libjingle/source/talk/session/phone/codec.cc59
-rw-r--r--third_party/libjingle/source/talk/session/phone/codec.h90
-rw-r--r--third_party/libjingle/source/talk/session/phone/cryptoparams.h54
-rw-r--r--third_party/libjingle/source/talk/session/phone/devicemanager.cc885
-rw-r--r--third_party/libjingle/source/talk/session/phone/devicemanager.h107
-rw-r--r--third_party/libjingle/source/talk/session/phone/filevideoengine.h118
-rw-r--r--third_party/libjingle/source/talk/session/phone/filevideomediachannel.h149
-rw-r--r--third_party/libjingle/source/talk/session/phone/mediachannel.h312
-rw-r--r--third_party/libjingle/source/talk/session/phone/mediaengine.cc44
-rw-r--r--third_party/libjingle/source/talk/session/phone/mediaengine.h319
-rw-r--r--third_party/libjingle/source/talk/session/phone/mediamonitor.cc109
-rw-r--r--third_party/libjingle/source/talk/session/phone/mediamonitor.h100
-rw-r--r--third_party/libjingle/source/talk/session/phone/mediasessionclient.cc371
-rw-r--r--third_party/libjingle/source/talk/session/phone/mediasessionclient.h212
-rw-r--r--third_party/libjingle/source/talk/session/phone/soundclip.cc82
-rw-r--r--third_party/libjingle/source/talk/session/phone/soundclip.h70
-rw-r--r--third_party/libjingle/source/talk/session/phone/srtpfilter.cc428
-rw-r--r--third_party/libjingle/source/talk/session/phone/srtpfilter.h145
-rw-r--r--third_party/libjingle/source/talk/session/phone/v4llookup.cc66
-rw-r--r--third_party/libjingle/source/talk/session/phone/v4llookup.h40
-rw-r--r--third_party/libjingle/source/talk/session/phone/voicechannel.h33
-rw-r--r--third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc567
-rw-r--r--third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h127
-rw-r--r--third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.cc350
-rw-r--r--third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.h161
-rw-r--r--third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.cc358
-rw-r--r--third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.h176
-rw-r--r--third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.py20
-rw-r--r--third_party/libjingle/source/talk/site_scons/talk.py441
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/BUILD9
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/COPYING.txt22
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/OWNERS3
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/README.google14
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/BUILD9
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/COPYING.txt22
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Changes.txt169
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/expat.pngbin0 -> 1027 bytes
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/reference.html2341
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/style.css101
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/valid-xhtml10.pngbin0 -> 2368 bytes
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/MANIFEST.txt27
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/README.google39
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/README.txt137
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/README.txt80
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/amigaconfig.h32
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/ascii.h92
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/asciitab.h36
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat.dsp185
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat.h1014
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_config.h104
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_external.h115
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_static.dsp162
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expatw.dsp185
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expatw_static.dsp162
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/iasciitab.h37
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/internal.h73
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/latin1tab.h36
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/libexpat.def73
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/libexpatw.def73
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/macconfig.h53
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/nametab.h150
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/utf8tab.h37
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/watcomconfig.h47
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/winconfig.h32
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlparse.c6287
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlrole.c1336
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlrole.h114
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok.c1651
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok.h316
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_impl.c1786
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_impl.h46
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_ns.c115
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/expat.scons24
-rw-r--r--third_party/libjingle/source/talk/third_party/expat/v2_0_1/mk_file16
-rwxr-xr-xthird_party/libjingle/source/talk/third_party/expat/v2_0_1/setup_env.bat8
-rw-r--r--third_party/libjingle/source/talk/third_party/gipslite/Interface/GipsVoiceEngineLite.h132
-rw-r--r--third_party/libjingle/source/talk/third_party/gipslite/Interface/expiration.h6
-rw-r--r--third_party/libjingle/source/talk/third_party/gipslite/Libraries/GipsVoiceEngineLite.dllbin0 -> 357376 bytes
-rw-r--r--third_party/libjingle/source/talk/third_party/gipslite/Libraries/GipsVoiceEngineLite.libbin0 -> 8148 bytes
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/Makefile.am91
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/affine.c144
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/affine.h43
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/alsacard.c640
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/alsacard.h50
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/audiostream.c343
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/g711common.h171
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/hpuxsndcard.c301
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/jackcard.c574
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/jackcard.h81
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mediastream.c117
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mediastream.h130
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/ms.c342
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/ms.h81
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msAlawdec.c132
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msAlawdec.h65
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msAlawenc.c124
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msAlawenc.h64
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msGSMdecoder.c121
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msGSMdecoder.h64
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msGSMencoder.c101
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msGSMencoder.h61
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10decoder.c129
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10decoder.h64
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10encoder.c251
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10encoder.h74
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawdec.c130
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawdec.h66
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawenc.c99
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawenc.h63
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msavdecoder.c278
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msavdecoder.h87
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msavencoder.c235
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msavencoder.h90
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msbuffer.c94
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msbuffer.h75
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mscodec.c250
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mscodec.h67
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mscopy.c96
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mscopy.h61
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msfdispatcher.c94
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msfdispatcher.h61
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msfifo.c168
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msfifo.h73
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msfilter.c537
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msfilter.h201
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msilbcdec.c194
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msilbcdec.h72
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msilbcenc.c244
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msilbcenc.h84
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msnosync.c82
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msnosync.h60
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msossread.c148
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msossread.h77
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msosswrite.c247
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msosswrite.h78
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msqdispatcher.c91
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msqdispatcher.h60
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msqueue.c56
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msqueue.h49
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msread.c182
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msread.h80
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msringplayer.c246
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msringplayer.h81
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msrtprecv.c163
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msrtprecv.h80
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msrtpsend.c211
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msrtpsend.h85
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mssdlout.c303
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mssdlout.h64
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mssoundread.c39
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mssoundread.h80
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mssoundwrite.c39
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mssoundwrite.h80
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msspeexdec.c218
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msspeexdec.h69
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msspeexenc.c192
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msspeexenc.h66
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mssync.c193
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mssync.h136
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mstimer.c114
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mstimer.h68
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechdecoder.c152
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechdecoder.h55
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechencoder.c161
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechencoder.h62
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msutils.h61
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msv4l.c530
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msv4l.h96
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msvideosource.c94
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/msvideosource.h74
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mswrite.c121
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/mswrite.h63
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/osscard.c495
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/osscard.h47
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/recvrtp.c109
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/ring_test.c63
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/rtpspeex.c38
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/sendrtp.c110
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/sndcard.c204
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/sndcard.h143
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test.c91
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_alaw.c90
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_gsm.c110
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_lpc10.c109
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_mulaw.c87
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_rtprecv.c100
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_speex.c38
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_truespeech.c91
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_v4l.c32
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/test_videostream.c44
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/videostream.c258
-rw-r--r--third_party/libjingle/source/talk/third_party/mediastreamer/waveheader.h111
-rw-r--r--third_party/libjingle/source/talk/xmllite/qname.cc162
-rw-r--r--third_party/libjingle/source/talk/xmllite/qname.h87
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlbuilder.cc152
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlbuilder.h78
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlconstants.cc65
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlconstants.h61
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlelement.cc520
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlelement.h239
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlnsstack.cc204
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlnsstack.h62
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlparser.cc292
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlparser.h121
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlprinter.cc198
-rw-r--r--third_party/libjingle/source/talk/xmllite/xmlprinter.h49
-rw-r--r--third_party/libjingle/source/talk/xmpp/asyncsocket.h86
-rw-r--r--third_party/libjingle/source/talk/xmpp/constants.cc481
-rw-r--r--third_party/libjingle/source/talk/xmpp/constants.h449
-rw-r--r--third_party/libjingle/source/talk/xmpp/jid.cc503
-rw-r--r--third_party/libjingle/source/talk/xmpp/jid.h148
-rw-r--r--third_party/libjingle/source/talk/xmpp/plainsaslhandler.h80
-rw-r--r--third_party/libjingle/source/talk/xmpp/prexmppauth.h86
-rw-r--r--third_party/libjingle/source/talk/xmpp/ratelimitmanager.cc80
-rw-r--r--third_party/libjingle/source/talk/xmpp/ratelimitmanager.h141
-rw-r--r--third_party/libjingle/source/talk/xmpp/saslcookiemechanism.h88
-rw-r--r--third_party/libjingle/source/talk/xmpp/saslhandler.h59
-rw-r--r--third_party/libjingle/source/talk/xmpp/saslmechanism.cc72
-rw-r--r--third_party/libjingle/source/talk/xmpp/saslmechanism.h74
-rw-r--r--third_party/libjingle/source/talk/xmpp/saslplainmechanism.h65
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppclient.cc407
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppclient.h163
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppclientsettings.h116
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppengine.h341
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc498
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppengineimpl.h278
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppengineimpl_iq.cc277
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmpplogintask.cc380
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmpplogintask.h98
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppstanzaparser.cc105
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmppstanzaparser.h97
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmpptask.cc175
-rw-r--r--third_party/libjingle/source/talk/xmpp/xmpptask.h130
491 files changed, 107741 insertions, 0 deletions
diff --git a/third_party/libjingle/README.chromium b/third_party/libjingle/README.chromium
new file mode 100644
index 0000000..297aa71
--- /dev/null
+++ b/third_party/libjingle/README.chromium
@@ -0,0 +1,12 @@
+Name: libjingle
+URL: http://code.google.com/p/libjingle/
+Version: 2.7.0
+License: BSD
+License File: source/COPYING
+
+Description:
+ Libjingle provides xmpp support to the sync code, which
+ lives at chrome/browser/sync/notifier.
+
+ Libjingle is distributed under a Berkeley-style license detailed in
+ source/COPYING.
diff --git a/third_party/libjingle/libjingle.Makefile b/third_party/libjingle/libjingle.Makefile
new file mode 100644
index 0000000..a934ecb
--- /dev/null
+++ b/third_party/libjingle/libjingle.Makefile
@@ -0,0 +1,6 @@
+# This file is generated by gyp; do not edit.
+
+export builddir_name ?= /usr/local/google/src/chromium/src/third_party/libjingle/out
+.PHONY: all
+all:
+ $(MAKE) -C ../.. libjingle libjingle_p2p
diff --git a/third_party/libjingle/libjingle.gyp b/third_party/libjingle/libjingle.gyp
new file mode 100644
index 0000000..ff191b4
--- /dev/null
+++ b/third_party/libjingle/libjingle.gyp
@@ -0,0 +1,429 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'no_libjingle_logging%': 0,
+ },
+ 'target_defaults': {
+ 'defines': [
+ 'FEATURE_ENABLE_SSL',
+ 'FEATURE_ENABLE_VOICEMAIL', # TODO(ncarter): Do we really need this?
+ '_USE_32BIT_TIME_T',
+ 'SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS',
+ 'EXPAT_RELATIVE_PATH',
+ ],
+ 'configurations': {
+ 'Debug': {
+ 'defines': [
+ # TODO(sergeyu): Fix libjingle to use NDEBUG instead of
+ # _DEBUG and remove this define. See below as well.
+ '_DEBUG',
+ ],
+ }
+ },
+ 'include_dirs': [
+ './overrides',
+ './source',
+ '../../third_party/expat/files'
+ ],
+ 'dependencies': [
+ '../expat/expat.gyp:expat',
+ '../../base/base.gyp:base',
+ '../../net/net.gyp:net_base',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ './overrides',
+ './source',
+ '../../third_party/expat/files'
+ ],
+ 'defines': [
+ 'FEATURE_ENABLE_SSL',
+ 'FEATURE_ENABLE_VOICEMAIL',
+ 'EXPAT_RELATIVE_PATH',
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-lsecur32.lib',
+ '-lcrypt32.lib',
+ '-liphlpapi.lib',
+ ],
+ },
+ }],
+ ['OS=="win"', {
+ 'include_dirs': [
+ '../third_party/platformsdk_win7/files/Include',
+ ],
+ 'defines': [
+ '_CRT_SECURE_NO_WARNINGS', # Suppres warnings about _vsnprinf
+ ],
+ }],
+ ['OS=="linux"', {
+ 'defines': [
+ 'LINUX',
+ ],
+ }],
+ ['OS=="mac"', {
+ 'defines': [
+ 'OSX',
+ ],
+ }],
+ ['OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="openbsd"', {
+ 'defines': [
+ 'POSIX',
+ ],
+ }],
+ ['OS=="openbsd" or OS=="freebsd"', {
+ 'defines': [
+ 'BSD',
+ ],
+ }],
+ ['no_libjingle_logging==1', {
+ 'defines': [
+ 'NO_LIBJINGLE_LOGGING',
+ ],
+ }],
+ ],
+ },
+ 'all_dependent_settings': {
+ 'configurations': {
+ 'Debug': {
+ 'defines': [
+ # TODO(sergeyu): Fix libjingle to use NDEBUG instead of
+ # _DEBUG and remove this define. See above as well.
+ '_DEBUG',
+ ],
+ }
+ },
+ },
+ 'conditions': [
+ ['OS=="win"', {
+ 'include_dirs': [
+ '../third_party/platformsdk_win7/files/Include',
+ ],
+ }],
+ ['OS=="linux"', {
+ 'defines': [
+ 'LINUX',
+ ],
+ }],
+ ['OS=="mac"', {
+ 'defines': [
+ 'OSX',
+ ],
+ }],
+ ['OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="openbsd"', {
+ 'defines': [
+ 'POSIX',
+ ],
+ }],
+ ['OS=="openbsd" or OS=="freebsd"', {
+ 'defines': [
+ 'BSD',
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'libjingle',
+ 'type': '<(library)',
+ 'sources': [
+ 'overrides/talk/base/basictypes.h',
+ 'overrides/talk/base/constructormagic.h',
+
+ # Need to override logging.h because we need
+ # SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS to work.
+ # TODO(sergeyu): push SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS to
+ # libjingle and remove this override.
+ 'overrides/talk/base/logging.h',
+
+ 'overrides/talk/base/scoped_ptr.h',
+
+ # Libjingle's QName is not threadsafe, so we need to use our own version
+ # here.
+ # TODO(sergeyu): Fix QName in Libjingle.
+ 'overrides/talk/xmllite/qname.cc',
+ 'overrides/talk/xmllite/qname.h',
+
+ 'source/talk/base/DiskCacheStd.h',
+ 'source/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h',
+ 'source/talk/base/asyncfile.h',
+ 'source/talk/base/asynchttprequest.cc',
+ 'source/talk/base/asynchttprequest.h',
+ 'source/talk/base/asyncpacketsocket.cc',
+ 'source/talk/base/asyncpacketsocket.h',
+ 'source/talk/base/asyncsocket.h',
+ 'source/talk/base/asynctcpsocket.cc',
+ 'source/talk/base/asynctcpsocket.h',
+ 'source/talk/base/asyncudpsocket.cc',
+ 'source/talk/base/asyncudpsocket.h',
+ 'source/talk/base/autodetectproxy.cc',
+ 'source/talk/base/autodetectproxy.h',
+ 'source/talk/base/base64.cc',
+ 'source/talk/base/base64.h',
+ 'source/talk/base/basicdefs.h',
+ 'source/talk/base/bytebuffer.cc',
+ 'source/talk/base/bytebuffer.h',
+ 'source/talk/base/byteorder.h',
+ 'source/talk/base/checks.cc',
+ 'source/talk/base/checks.h',
+ 'source/talk/base/common.cc',
+ 'source/talk/base/common.h',
+ 'source/talk/base/criticalsection.h',
+ 'source/talk/base/cryptstring.h',
+ 'source/talk/base/diskcache.cc',
+ 'source/talk/base/diskcache.h',
+ 'source/talk/base/event.cc',
+ 'source/talk/base/event.h',
+ 'source/talk/base/fakenetwork.h',
+ 'source/talk/base/fileutils.cc',
+ 'source/talk/base/fileutils.h',
+ 'source/talk/base/fileutils_mock.h',
+ 'source/talk/base/firewallsocketserver.cc',
+ 'source/talk/base/firewallsocketserver.h',
+ 'source/talk/base/flags.cc',
+ 'source/talk/base/flags.h',
+ 'source/talk/base/hash.h',
+ 'source/talk/base/helpers.cc',
+ 'source/talk/base/helpers.h',
+ 'source/talk/base/host.cc',
+ 'source/talk/base/host.h',
+ 'source/talk/base/httpbase.cc',
+ 'source/talk/base/httpbase.h',
+ 'source/talk/base/httpclient.h',
+ 'source/talk/base/httpclient.cc',
+ 'source/talk/base/httpcommon-inl.h',
+ 'source/talk/base/httpcommon.cc',
+ 'source/talk/base/httpcommon.h',
+ 'source/talk/base/httprequest.cc',
+ 'source/talk/base/httprequest.h',
+ 'source/talk/base/icftypes.h',
+ 'source/talk/base/linked_ptr.h',
+ 'source/talk/base/logging.cc',
+ 'source/talk/base/md5.h',
+ 'source/talk/base/md5c.c',
+ 'source/talk/base/messagehandler.cc',
+ 'source/talk/base/messagehandler.h',
+ 'source/talk/base/messagequeue.cc',
+ 'source/talk/base/messagequeue.h',
+ 'source/talk/base/netfw.h',
+ 'source/talk/base/nethelpers.cc',
+ 'source/talk/base/nethelpers.h',
+ 'source/talk/base/network.cc',
+ 'source/talk/base/network.h',
+ 'source/talk/base/pathutils.cc',
+ 'source/talk/base/pathutils.h',
+ 'source/talk/base/physicalsocketserver.cc',
+ 'source/talk/base/physicalsocketserver.h',
+ 'source/talk/base/proxydetect.cc',
+ 'source/talk/base/proxydetect.h',
+ 'source/talk/base/proxyinfo.cc',
+ 'source/talk/base/proxyinfo.h',
+ 'source/talk/base/sec_buffer.h',
+ 'source/talk/base/signalthread.cc',
+ 'source/talk/base/signalthread.h',
+ 'source/talk/base/sigslot.h',
+ 'source/talk/base/sigslotrepeater.h',
+ 'source/talk/base/socket.h',
+ 'source/talk/base/socketadapters.cc',
+ 'source/talk/base/socketadapters.h',
+ 'source/talk/base/socketaddress.cc',
+ 'source/talk/base/socketaddress.h',
+ 'source/talk/base/socketfactory.h',
+ 'source/talk/base/socketpool.cc',
+ 'source/talk/base/socketpool.h',
+ 'source/talk/base/socketserver.h',
+ 'source/talk/base/socketstream.h',
+ 'source/talk/base/ssladapter.cc',
+ 'source/talk/base/ssladapter.h',
+ 'source/talk/base/sslsocketfactory.cc',
+ 'source/talk/base/sslsocketfactory.h',
+ 'source/talk/base/stream.cc',
+ 'source/talk/base/stream.h',
+ 'source/talk/base/stringdigest.cc',
+ 'source/talk/base/stringdigest.h',
+ 'source/talk/base/stringencode.cc',
+ 'source/talk/base/stringencode.h',
+ 'source/talk/base/stringutils.cc',
+ 'source/talk/base/stringutils.h',
+ 'source/talk/base/task.cc',
+ 'source/talk/base/task.h',
+ 'source/talk/base/taskparent.cc',
+ 'source/talk/base/taskparent.h',
+ 'source/talk/base/taskrunner.cc',
+ 'source/talk/base/taskrunner.h',
+ 'source/talk/base/thread.cc',
+ 'source/talk/base/thread.h',
+ 'source/talk/base/time.cc',
+ 'source/talk/base/time.h',
+ 'source/talk/base/urlencode.cc',
+ 'source/talk/base/urlencode.h',
+ 'source/talk/xmllite/xmlbuilder.cc',
+ 'source/talk/xmllite/xmlbuilder.h',
+ 'source/talk/xmllite/xmlconstants.cc',
+ 'source/talk/xmllite/xmlconstants.h',
+ 'source/talk/xmllite/xmlelement.cc',
+ 'source/talk/xmllite/xmlelement.h',
+ 'source/talk/xmllite/xmlnsstack.cc',
+ 'source/talk/xmllite/xmlnsstack.h',
+ 'source/talk/xmllite/xmlparser.cc',
+ 'source/talk/xmllite/xmlparser.h',
+ 'source/talk/xmllite/xmlprinter.cc',
+ 'source/talk/xmllite/xmlprinter.h',
+ 'source/talk/xmpp/asyncsocket.h',
+ 'source/talk/xmpp/constants.cc',
+ 'source/talk/xmpp/constants.h',
+ 'source/talk/xmpp/jid.cc',
+ 'source/talk/xmpp/jid.h',
+ 'source/talk/xmpp/plainsaslhandler.h',
+ 'source/talk/xmpp/prexmppauth.h',
+ 'source/talk/xmpp/ratelimitmanager.cc',
+ 'source/talk/xmpp/ratelimitmanager.h',
+ 'source/talk/xmpp/saslcookiemechanism.h',
+ 'source/talk/xmpp/saslhandler.h',
+ 'source/talk/xmpp/saslmechanism.cc',
+ 'source/talk/xmpp/saslmechanism.h',
+ 'source/talk/xmpp/saslplainmechanism.h',
+ 'source/talk/xmpp/xmppclient.cc',
+ 'source/talk/xmpp/xmppclient.h',
+ 'source/talk/xmpp/xmppclientsettings.h',
+ 'source/talk/xmpp/xmppengine.h',
+ 'source/talk/xmpp/xmppengineimpl.cc',
+ 'source/talk/xmpp/xmppengineimpl.h',
+ 'source/talk/xmpp/xmppengineimpl_iq.cc',
+ 'source/talk/xmpp/xmpplogintask.cc',
+ 'source/talk/xmpp/xmpplogintask.h',
+ 'source/talk/xmpp/xmppstanzaparser.cc',
+ 'source/talk/xmpp/xmppstanzaparser.h',
+ 'source/talk/xmpp/xmpptask.cc',
+ 'source/talk/xmpp/xmpptask.h',
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'sources': [
+ 'overrides/talk/base/win32socketinit.cc',
+ 'source/talk/base/convert.h', # win32 only
+ 'source/talk/base/schanneladapter.cc',
+ 'source/talk/base/schanneladapter.h',
+ 'source/talk/base/win32.h',
+ 'source/talk/base/win32.cc',
+ 'source/talk/base/win32filesystem.cc',
+ 'source/talk/base/win32filesystem.h',
+ 'source/talk/base/win32window.h',
+ 'source/talk/base/win32window.cc',
+ 'source/talk/base/win32securityerrors.cc',
+ 'source/talk/base/winfirewall.cc',
+ 'source/talk/base/winfirewall.h',
+ 'source/talk/base/winping.cc',
+ 'source/talk/base/winping.h',
+ ],
+ }],
+ ['OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="openbsd"', {
+ 'sources': [
+ 'source/talk/base/sslstreamadapter.cc',
+ 'source/talk/base/sslstreamadapter.h',
+ 'source/talk/base/unixfilesystem.cc',
+ 'source/talk/base/unixfilesystem.h',
+ ],
+ }],
+ ['OS=="linux"', {
+ 'sources': [
+ 'source/talk/base/linux.cc',
+ 'source/talk/base/linux.h',
+ ],
+ }],
+ ['OS=="mac"', {
+ 'sources': [
+ 'source/talk/base/macconversion.cc',
+ 'source/talk/base/macconversion.h',
+ 'source/talk/base/macutils.cc',
+ 'source/talk/base/macutils.h',
+ ],
+ }],
+ ],
+ },
+ # This has to be is a separate project due to a bug in MSVS:
+ # https://connect.microsoft.com/VisualStudio/feedback/details/368272/duplicate-cpp-filename-in-c-project-visual-studio-2008
+ # We have two files named "constants.cc" and MSVS doesn't handle this
+ # properly.
+ {
+ 'target_name': 'libjingle_p2p',
+ 'type': '<(library)',
+ 'sources': [
+ 'source/talk/p2p/base/candidate.h',
+ 'source/talk/p2p/base/common.h',
+ 'source/talk/p2p/base/constants.cc',
+ 'source/talk/p2p/base/constants.h',
+ 'source/talk/p2p/base/p2ptransport.cc',
+ 'source/talk/p2p/base/p2ptransport.h',
+ 'source/talk/p2p/base/p2ptransportchannel.cc',
+ 'source/talk/p2p/base/p2ptransportchannel.h',
+ 'source/talk/p2p/base/port.cc',
+ 'source/talk/p2p/base/port.h',
+ 'source/talk/p2p/base/portallocator.h',
+ 'source/talk/p2p/base/pseudotcp.cc',
+ 'source/talk/p2p/base/pseudotcp.h',
+ 'source/talk/p2p/base/rawtransport.cc',
+ 'source/talk/p2p/base/rawtransport.h',
+ 'source/talk/p2p/base/rawtransportchannel.cc',
+ 'source/talk/p2p/base/rawtransportchannel.h',
+ 'source/talk/p2p/base/relayport.cc',
+ 'source/talk/p2p/base/relayport.h',
+ 'source/talk/p2p/base/session.cc',
+ 'source/talk/p2p/base/session.h',
+ 'source/talk/p2p/base/sessionclient.h',
+ 'source/talk/p2p/base/sessiondescription.h',
+ 'source/talk/p2p/base/sessionid.h',
+ 'source/talk/p2p/base/sessionmanager.cc',
+ 'source/talk/p2p/base/sessionmanager.h',
+ 'source/talk/p2p/base/sessionmessages.cc',
+ 'source/talk/p2p/base/sessionmessages.h',
+ 'source/talk/p2p/base/parsing.cc',
+ 'source/talk/p2p/base/parsing.h',
+ 'source/talk/p2p/base/stun.cc',
+ 'source/talk/p2p/base/stun.h',
+ 'source/talk/p2p/base/stunport.cc',
+ 'source/talk/p2p/base/stunport.h',
+ 'source/talk/p2p/base/stunrequest.cc',
+ 'source/talk/p2p/base/stunrequest.h',
+ 'source/talk/p2p/base/tcpport.cc',
+ 'source/talk/p2p/base/tcpport.h',
+ 'source/talk/p2p/base/transport.cc',
+ 'source/talk/p2p/base/transport.h',
+ 'source/talk/p2p/base/transportchannel.cc',
+ 'source/talk/p2p/base/transportchannel.h',
+ 'source/talk/p2p/base/transportchannelimpl.h',
+ 'source/talk/p2p/base/transportchannelproxy.cc',
+ 'source/talk/p2p/base/transportchannelproxy.h',
+ 'source/talk/p2p/base/udpport.cc',
+ 'source/talk/p2p/base/udpport.h',
+ 'source/talk/p2p/client/basicportallocator.cc',
+ 'source/talk/p2p/client/basicportallocator.h',
+ 'source/talk/p2p/client/httpportallocator.cc',
+ 'source/talk/p2p/client/httpportallocator.h',
+ 'source/talk/p2p/client/sessionmanagertask.h',
+ 'source/talk/p2p/client/sessionsendtask.h',
+ 'source/talk/p2p/client/socketmonitor.cc',
+ 'source/talk/p2p/client/socketmonitor.h',
+ 'source/talk/session/tunnel/pseudotcpchannel.cc',
+ 'source/talk/session/tunnel/pseudotcpchannel.h',
+ 'source/talk/session/tunnel/tunnelsessionclient.cc',
+ 'source/talk/session/tunnel/tunnelsessionclient.h',
+ ],
+ 'dependencies': [
+ 'libjingle',
+ ],
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/third_party/libjingle/libjingle.target.mk b/third_party/libjingle/libjingle.target.mk
new file mode 100644
index 0000000..2b399d3
--- /dev/null
+++ b/third_party/libjingle/libjingle.target.mk
@@ -0,0 +1,236 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := libjingle
+DEFS_Debug := '-DFEATURE_ENABLE_SSL' \
+ '-DFEATURE_ENABLE_VOICEMAIL' \
+ '-D_USE_32BIT_TIME_T' \
+ '-DSAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS' \
+ '-DEXPAT_RELATIVE_PATH' \
+ '-DNO_HEAPCHECKER' \
+ '-DLINUX' \
+ '-DPOSIX' \
+ '-DCHROMIUM_BUILD' \
+ '-DENABLE_REMOTING=1' \
+ '-DENABLE_GPU=1' \
+ '-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+ '-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -pthread \
+ -fno-exceptions \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -D_FILE_OFFSET_BITS=64 \
+ -fvisibility=hidden \
+ -fno-strict-aliasing \
+ -pthread \
+ -D_REENTRANT \
+ -I/usr/include/gtk-2.0 \
+ -I/usr/lib/gtk-2.0/include \
+ -I/usr/include/atk-1.0 \
+ -I/usr/include/cairo \
+ -I/usr/include/pango-1.0 \
+ -I/usr/include/gio-unix-2.0/ \
+ -I/usr/include/glib-2.0 \
+ -I/usr/lib/glib-2.0/include \
+ -I/usr/include/pixman-1 \
+ -I/usr/include/freetype2 \
+ -I/usr/include/directfb \
+ -I/usr/include/libpng12 \
+ -O0 \
+ -g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug :=
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+ -fno-threadsafe-statics \
+ -fvisibility-inlines-hidden
+
+INCS_Debug := -Ithird_party/libjingle/overrides \
+ -Ithird_party/libjingle/source \
+ -Ithird_party/expat/files \
+ -I.
+
+DEFS_Release := '-DFEATURE_ENABLE_SSL' \
+ '-DFEATURE_ENABLE_VOICEMAIL' \
+ '-D_USE_32BIT_TIME_T' \
+ '-DSAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS' \
+ '-DEXPAT_RELATIVE_PATH' \
+ '-DNO_HEAPCHECKER' \
+ '-DLINUX' \
+ '-DPOSIX' \
+ '-DCHROMIUM_BUILD' \
+ '-DENABLE_REMOTING=1' \
+ '-DENABLE_GPU=1' \
+ '-DNDEBUG' \
+ '-DNVALGRIND' \
+ '-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -pthread \
+ -fno-exceptions \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -D_FILE_OFFSET_BITS=64 \
+ -fvisibility=hidden \
+ -fno-strict-aliasing \
+ -pthread \
+ -D_REENTRANT \
+ -I/usr/include/gtk-2.0 \
+ -I/usr/lib/gtk-2.0/include \
+ -I/usr/include/atk-1.0 \
+ -I/usr/include/cairo \
+ -I/usr/include/pango-1.0 \
+ -I/usr/include/gio-unix-2.0/ \
+ -I/usr/include/glib-2.0 \
+ -I/usr/lib/glib-2.0/include \
+ -I/usr/include/pixman-1 \
+ -I/usr/include/freetype2 \
+ -I/usr/include/directfb \
+ -I/usr/include/libpng12 \
+ -O2 \
+ -fno-ident \
+ -fdata-sections \
+ -ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release :=
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+ -fno-threadsafe-statics \
+ -fvisibility-inlines-hidden
+
+INCS_Release := -Ithird_party/libjingle/overrides \
+ -Ithird_party/libjingle/source \
+ -Ithird_party/expat/files \
+ -I.
+
+OBJS := $(obj).target/$(TARGET)/third_party/libjingle/overrides/talk/xmllite/qname.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/asynchttprequest.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/asyncpacketsocket.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/asynctcpsocket.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/asyncudpsocket.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/autodetectproxy.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/base64.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/bytebuffer.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/checks.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/common.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/diskcache.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/event.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/fileutils.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/firewallsocketserver.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/flags.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/helpers.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/host.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/httpbase.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/httpclient.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/httpcommon.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/httprequest.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/logging.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/md5c.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/messagehandler.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/messagequeue.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/nethelpers.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/network.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/pathutils.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/physicalsocketserver.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/proxydetect.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/proxyinfo.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/signalthread.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/socketadapters.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/socketaddress.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/socketpool.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/ssladapter.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/sslsocketfactory.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/stream.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/stringdigest.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/stringencode.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/stringutils.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/task.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/taskparent.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/taskrunner.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/thread.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/time.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/urlencode.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmllite/xmlbuilder.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmllite/xmlconstants.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmllite/xmlelement.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmllite/xmlnsstack.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmllite/xmlparser.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmllite/xmlprinter.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/constants.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/jid.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/ratelimitmanager.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/saslmechanism.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/xmppclient.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/xmppengineimpl.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/xmppengineimpl_iq.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/xmpplogintask.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/xmppstanzaparser.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/xmpp/xmpptask.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/sslstreamadapter.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/unixfilesystem.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/base/linux.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+ @$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD
+ @$(call do_cmd,cc,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+ @$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD
+ @$(call do_cmd,cc,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+ @$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD
+ @$(call do_cmd,cc,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+ -Wl,-z,noexecstack \
+ -rdynamic
+
+LDFLAGS_Release := -pthread \
+ -Wl,-z,noexecstack \
+ -Wl,--gc-sections
+
+LIBS :=
+
+$(obj).target/third_party/libjingle/libjingle.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/third_party/libjingle/libjingle.a: LIBS := $(LIBS)
+$(obj).target/third_party/libjingle/libjingle.a: TOOLSET := $(TOOLSET)
+$(obj).target/third_party/libjingle/libjingle.a: $(OBJS) FORCE_DO_CMD
+ $(call do_cmd,alink)
+
+all_deps += $(obj).target/third_party/libjingle/libjingle.a
+# Add target alias
+.PHONY: libjingle
+libjingle: $(obj).target/third_party/libjingle/libjingle.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: libjingle
+
diff --git a/third_party/libjingle/libjingle_p2p.target.mk b/third_party/libjingle/libjingle_p2p.target.mk
new file mode 100644
index 0000000..a32e0f2
--- /dev/null
+++ b/third_party/libjingle/libjingle_p2p.target.mk
@@ -0,0 +1,186 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := libjingle_p2p
+DEFS_Debug := '-DFEATURE_ENABLE_SSL' \
+ '-DFEATURE_ENABLE_VOICEMAIL' \
+ '-D_USE_32BIT_TIME_T' \
+ '-DSAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS' \
+ '-DEXPAT_RELATIVE_PATH' \
+ '-DNO_HEAPCHECKER' \
+ '-DLINUX' \
+ '-DPOSIX' \
+ '-DCHROMIUM_BUILD' \
+ '-DENABLE_REMOTING=1' \
+ '-DENABLE_GPU=1' \
+ '-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+ '-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -pthread \
+ -fno-exceptions \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -D_FILE_OFFSET_BITS=64 \
+ -fvisibility=hidden \
+ -fno-strict-aliasing \
+ -pthread \
+ -D_REENTRANT \
+ -I/usr/include/gtk-2.0 \
+ -I/usr/lib/gtk-2.0/include \
+ -I/usr/include/atk-1.0 \
+ -I/usr/include/cairo \
+ -I/usr/include/pango-1.0 \
+ -I/usr/include/gio-unix-2.0/ \
+ -I/usr/include/glib-2.0 \
+ -I/usr/lib/glib-2.0/include \
+ -I/usr/include/pixman-1 \
+ -I/usr/include/freetype2 \
+ -I/usr/include/directfb \
+ -I/usr/include/libpng12 \
+ -O0 \
+ -g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug :=
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+ -fno-threadsafe-statics \
+ -fvisibility-inlines-hidden
+
+INCS_Debug := -Ithird_party/libjingle/overrides \
+ -Ithird_party/libjingle/source \
+ -Ithird_party/expat/files \
+ -I.
+
+DEFS_Release := '-DFEATURE_ENABLE_SSL' \
+ '-DFEATURE_ENABLE_VOICEMAIL' \
+ '-D_USE_32BIT_TIME_T' \
+ '-DSAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS' \
+ '-DEXPAT_RELATIVE_PATH' \
+ '-DNO_HEAPCHECKER' \
+ '-DLINUX' \
+ '-DPOSIX' \
+ '-DCHROMIUM_BUILD' \
+ '-DENABLE_REMOTING=1' \
+ '-DENABLE_GPU=1' \
+ '-DNDEBUG' \
+ '-DNVALGRIND' \
+ '-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -pthread \
+ -fno-exceptions \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -D_FILE_OFFSET_BITS=64 \
+ -fvisibility=hidden \
+ -fno-strict-aliasing \
+ -pthread \
+ -D_REENTRANT \
+ -I/usr/include/gtk-2.0 \
+ -I/usr/lib/gtk-2.0/include \
+ -I/usr/include/atk-1.0 \
+ -I/usr/include/cairo \
+ -I/usr/include/pango-1.0 \
+ -I/usr/include/gio-unix-2.0/ \
+ -I/usr/include/glib-2.0 \
+ -I/usr/lib/glib-2.0/include \
+ -I/usr/include/pixman-1 \
+ -I/usr/include/freetype2 \
+ -I/usr/include/directfb \
+ -I/usr/include/libpng12 \
+ -O2 \
+ -fno-ident \
+ -fdata-sections \
+ -ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release :=
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+ -fno-threadsafe-statics \
+ -fvisibility-inlines-hidden
+
+INCS_Release := -Ithird_party/libjingle/overrides \
+ -Ithird_party/libjingle/source \
+ -Ithird_party/expat/files \
+ -I.
+
+OBJS := $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/constants.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/p2ptransport.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/port.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/pseudotcp.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/rawtransport.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/relayport.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/session.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/sessionmanager.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/sessionmessages.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/parsing.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/stun.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/stunport.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/stunrequest.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/tcpport.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/transport.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/transportchannel.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/base/udpport.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/client/basicportallocator.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/client/httpportallocator.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/p2p/client/socketmonitor.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.o \
+ $(obj).target/$(TARGET)/third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+ @$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+ @$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+ @$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+ -Wl,-z,noexecstack \
+ -rdynamic
+
+LDFLAGS_Release := -pthread \
+ -Wl,-z,noexecstack \
+ -Wl,--gc-sections
+
+LIBS :=
+
+$(obj).target/third_party/libjingle/libjingle_p2p.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/third_party/libjingle/libjingle_p2p.a: LIBS := $(LIBS)
+$(obj).target/third_party/libjingle/libjingle_p2p.a: TOOLSET := $(TOOLSET)
+$(obj).target/third_party/libjingle/libjingle_p2p.a: $(OBJS) FORCE_DO_CMD
+ $(call do_cmd,alink)
+
+all_deps += $(obj).target/third_party/libjingle/libjingle_p2p.a
+# Add target alias
+.PHONY: libjingle_p2p
+libjingle_p2p: $(obj).target/third_party/libjingle/libjingle_p2p.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: libjingle_p2p
+
diff --git a/third_party/libjingle/mods-since-v0_4_0.diff b/third_party/libjingle/mods-since-v0_4_0.diff
new file mode 100644
index 0000000..5812304
--- /dev/null
+++ b/third_party/libjingle/mods-since-v0_4_0.diff
@@ -0,0 +1,1684 @@
+Only in libjingle-0.4.0: Makefile.in
+diff -r libjingle-0.4.0/README libjingle/files/README
+1,39c1,39
+< 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
+---
+> 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
+41,57c41,57
+<
+< 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.
+---
+>
+> 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 -r libjingle-0.4.0/README.win libjingle/files/README.win
+1,24c1,24
+< 1. Install Visual C++ Express 2005. It is free from this link:
+< http://msdn.microsoft.com/vstudio/express/visualc/
+<
+< 2. Install the platform SDK and integrate it into VC++ express
+< http://msdn.microsoft.com/vstudio/express/visualc/usingpsdk/
+<
+< 3. Download and install binary package for expat:
+< http://sourceforge.net/project/showfiles.php?group_id=10127&package_id=11277
+<
+< 4. Update the Visual C++ directories in the Projects and Solutions section in the Options dialog box
+< Library files: C:\expat-VERSION\StaticLibs
+< Include files: C:\expat-VERSION\Source\Lib
+< where VERSION is the version of expat you've downoaded
+<
+< 5. Unzip the libjingle files and open the solution.
+<
+< 6. If you wish to build the call example with GIPS Voice Engine Lite, download Voice Engine Lite from http://developer.globalipsound.com
+<
+< 7. Extract the Interface and Library directories from the Voice Engine Lite zip file into talk\third_party\gips
+<
+< 8. Open talk\third_party\gips\expiration.h and set the GIPS_EXPIRATION #defines to the expiration date provided by GIPS and remove the #error directive
+<
+< 9. Build the solution
+<
+---
+> 1. Install Visual C++ Express 2005. It is free from this link:
+> http://msdn.microsoft.com/vstudio/express/visualc/
+>
+> 2. Install the platform SDK and integrate it into VC++ express
+> http://msdn.microsoft.com/vstudio/express/visualc/usingpsdk/
+>
+> 3. Download and install binary package for expat:
+> http://sourceforge.net/project/showfiles.php?group_id=10127&package_id=11277
+>
+> 4. Update the Visual C++ directories in the Projects and Solutions section in the Options dialog box
+> Library files: C:\expat-VERSION\StaticLibs
+> Include files: C:\expat-VERSION\Source\Lib
+> where VERSION is the version of expat you've downoaded
+>
+> 5. Unzip the libjingle files and open the solution.
+>
+> 6. If you wish to build the call example with GIPS Voice Engine Lite, download Voice Engine Lite from http://developer.globalipsound.com
+>
+> 7. Extract the Interface and Library directories from the Voice Engine Lite zip file into talk\third_party\gips
+>
+> 8. Open talk\third_party\gips\expiration.h and set the GIPS_EXPIRATION #defines to the expiration date provided by GIPS and remove the #error directive
+>
+> 9. Build the solution
+>
+Only in libjingle-0.4.0: aclocal.m4
+Only in libjingle-0.4.0: config.guess
+diff -r libjingle-0.4.0/config.h libjingle/files/config.h
+14c14
+< #define HAVE_ALSA_ASOUNDLIB_H 1
+---
+> /* #undef HAVE_ALSA_ASOUNDLIB_H */
+23c23
+< #define HAVE_GLIB 1
+---
+> /* #undef HAVE_GLIB */
+38c38
+< #define HAVE_ORTP 1
+---
+> /* #undef HAVE_ORTP */
+41c41
+< #define HAVE_SPEEX 1
+---
+> /* #undef HAVE_SPEEX */
+47c47
+< #define HAVE_SPEEX_SPEEX_H 1
+---
+> /* #undef HAVE_SPEEX_SPEEX_H */
+71c71
+< #define LINUX 1
+---
+> /* #undef LINUX */
+113c113
+< #define __ALSA_ENABLED__ 1
+---
+> /* #undef __ALSA_ENABLED__ */
+Only in libjingle-0.4.0: config.h.in
+Only in libjingle-0.4.0: config.sub
+Only in libjingle-0.4.0: configure
+Only in libjingle-0.4.0: depcomp
+Only in libjingle-0.4.0: install-sh
+Only in libjingle-0.4.0: ltmain.sh
+Only in libjingle-0.4.0: missing
+Only in libjingle-0.4.0/talk: Makefile.in
+Only in libjingle-0.4.0/talk/base: Makefile.in
+diff -r libjingle-0.4.0/talk/base/asynchttprequest.cc libjingle/files/talk/base/asynchttprequest.cc
+73c73
+< talk_base::SSLAdapter * ssl_adapter = talk_base::SSLAdapter::Create(socket);
+---
+> talk_base::SSLAdapter * ssl_adapter = factory_->CreateSSLAdapter(socket);
+75c75,81
+< ssl_adapter->StartSSL(hostname_.c_str(), true);
+---
+> int error = ssl_adapter->StartSSL(hostname_.c_str(),
+> use_restartable_ssl_sockets_);
+> if (error != 0) {
+> LOG(LS_WARNING) << "Could not start SSL; error = " << error;
+> delete ssl_adapter;
+> return 0;
+> }
+diff -r libjingle-0.4.0/talk/base/asynchttprequest.h libjingle/files/talk/base/asynchttprequest.h
+23,24c23
+< public SignalThread,
+< public sigslot::has_slots<> {
+---
+> public SignalThread {
+106c105,106
+< binary_mode_(false), agent_(user_agent) { }
+---
+> binary_mode_(false), agent_(user_agent),
+> ignore_bad_cert_(false), use_restartable_ssl_sockets_(false) { }
+114a115,120
+> bool use_restartable_ssl_sockets() const {
+> return use_restartable_ssl_sockets_;
+> }
+> void SetUseRestartableSSLSockets(bool use_restartable_ssl_sockets) {
+> use_restartable_ssl_sockets_ = use_restartable_ssl_sockets;
+> }
+133a140
+> bool use_restartable_ssl_sockets_;
+diff -r libjingle-0.4.0/talk/base/asynctcpsocket.cc libjingle/files/talk/base/asynctcpsocket.cc
+31a32,33
+> #include <cstring>
+>
+diff -r libjingle-0.4.0/talk/base/autodetectproxy.cc libjingle/files/talk/base/autodetectproxy.cc
+29c29
+< #include "talk/base/httpcommon.h"
+---
+> #include "talk/base/httpcommon-inl.h"
+114c114
+< Thread::Current()->MessageQueue::Stop();
+---
+> Thread::Current()->Quit();
+diff -r libjingle-0.4.0/talk/base/autodetectproxy.h libjingle/files/talk/base/autodetectproxy.h
+22c22
+< class AutoDetectProxy : public SignalThread, public sigslot::has_slots<> {
+---
+> class AutoDetectProxy : public SignalThread {
+diff -r libjingle-0.4.0/talk/base/base64.h libjingle/files/talk/base/base64.h
+26,27c26,27
+< static const std::string Base64::Base64Table;
+< static const std::string::size_type Base64::DecodeTable[];
+---
+> static const std::string Base64Table;
+> static const std::string::size_type DecodeTable[];
+diff -r libjingle-0.4.0/talk/base/common.h libjingle/files/talk/base/common.h
+54c54
+< #define stdmax(x,y) max(x,y)
+---
+> #define stdmax(x,y) _max(x,y)
+114,119d113
+< // 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&)
+<
+diff -r libjingle-0.4.0/talk/base/criticalsection.h libjingle/files/talk/base/criticalsection.h
+39c39
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+41c41
+< #endif // _DEBUG
+---
+> #endif // !defined(NDEBUG)
+83c83
+< public:
+---
+> public:
+85a86
+> pthread_mutexattr_init(&mutex_attribute);
+87a89,90
+> pthread_mutexattr_destroy(&mutex_attribute);
+> TRACK_OWNER(thread_ = 0);
+93a97
+> TRACK_OWNER(thread_ = pthread_self());
+95a100
+> TRACK_OWNER(thread_ = 0);
+98c103,110
+< private:
+---
+>
+> #if CS_TRACK_OWNER
+> bool CurrentThreadIsOwner() const {
+> return pthread_equal(thread_, pthread_self());
+> }
+> #endif // CS_TRACK_OWNER
+>
+> private:
+99a112
+> TRACK_OWNER(pthread_t thread_);
+diff -r libjingle-0.4.0/talk/base/cryptstring.h libjingle/files/talk/base/cryptstring.h
+30a31
+> #include <string.h>
+diff -r libjingle-0.4.0/talk/base/diskcache.cc libjingle/files/talk/base/diskcache.cc
+43c43
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+45c45
+< #else // !_DEBUG
+---
+> #else // defined(NDEBUG)
+47c47
+< #endif // !_DEBUG
+---
+> #endif // !defined(NDEBUG)
+231c231
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+238c238
+< #endif // _DEBUG
+---
+> #endif // !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/base/diskcache_win32.cc libjingle/files/talk/base/diskcache_win32.cc
+38c38
+< entry->streams = max(entry->streams, index + 1);
+---
+> entry->streams = _max(entry->streams, index + 1);
+diff -r libjingle-0.4.0/talk/base/helpers.cc libjingle/files/talk/base/helpers.cc
+38a39
+> #include <wincrypt.h>
+diff -r libjingle-0.4.0/talk/base/host.cc libjingle/files/talk/base/host.cc
+30a31
+> #include <cstdlib>
+diff -r libjingle-0.4.0/talk/base/httpclient.cc libjingle/files/talk/base/httpclient.cc
+670a671
+> HttpAuthContext *context = context_.get();
+676c677,678
+< *context_.use(), response, auth_method);
+---
+> context, response, auth_method);
+> context_.reset(context);
+diff -r libjingle-0.4.0/talk/base/httpcommon.h libjingle/files/talk/base/httpcommon.h
+172c172
+< inline const uint16 UrlDefaultPort(bool secure) {
+---
+> inline uint16 UrlDefaultPort(bool secure) {
+diff -r libjingle-0.4.0/talk/base/logging.cc libjingle/files/talk/base/logging.cc
+27a28
+> #include <stdio.h>
+76c77
+< #if _DEBUG
+---
+> #if LOGGING
+78c79
+< #else // !_DEBUG
+---
+> #else
+80c81
+< #endif // !_DEBUG
+---
+> #endif
+diff -r libjingle-0.4.0/talk/base/logging.h libjingle/files/talk/base/logging.h
+67a68,69
+>
+> #if defined(SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS)
+70a73
+> #endif // defined(SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS)
+195a199
+> #if defined(SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS)
+197c201
+< #if defined(_DEBUG) && !defined(NDEBUG)
+---
+> #if !defined(NDEBUG)
+290a295
+> #endif // defined(SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS)
+diff -r libjingle-0.4.0/talk/base/messagequeue.cc libjingle/files/talk/base/messagequeue.cc
+98,99c98,99
+< new_ss = true;
+< ss_ = new PhysicalSocketServer();
+---
+> default_ss_.reset(new PhysicalSocketServer());
+> ss_ = default_ss_.get();
+103a104,107
+> // The signal is done from here to ensure
+> // that it always gets called when the queue
+> // is going away.
+> SignalQueueDestroyed();
+108,109d111
+< if (new_ss)
+< delete ss_;
+113,115d114
+< if (new_ss)
+< delete ss_;
+< new_ss = false;
+119c118
+< void MessageQueue::Stop() {
+---
+> void MessageQueue::Quit() {
+124c123
+< bool MessageQueue::IsStopping() {
+---
+> bool MessageQueue::IsQuitting() {
+diff -r libjingle-0.4.0/talk/base/messagequeue.h libjingle/files/talk/base/messagequeue.h
+35a36
+> #include "talk/base/scoped_ptr.h"
+162,164c163,164
+<
+< virtual void Stop();
+< virtual bool IsStopping();
+---
+> virtual void Quit();
+> virtual bool IsQuitting();
+188a189,192
+> // When this signal is sent out, any references to this queue should
+> // no longer be used.
+> sigslot::signal0<> SignalQueueDestroyed;
+>
+192a197,198
+> // If a server isn't supplied in the constructor, use this one.
+> scoped_ptr<SocketServer> default_ss_;
+diff -r libjingle-0.4.0/talk/base/natserver.cc libjingle/files/talk/base/natserver.cc
+28a29
+> #include <cstring>
+diff -r libjingle-0.4.0/talk/base/natsocketfactory.cc libjingle/files/talk/base/natsocketfactory.cc
+29a30
+> #include <cstring>
+diff -r libjingle-0.4.0/talk/base/openssladapter.cc libjingle/files/talk/base/openssladapter.cc
+619c619
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+707c707
+< #if _DEBUG
+---
+> #if !defined(NDEBUG)
+736c736
+< #endif // _DEBUG
+---
+> #endif // !defined(NDEBUG)
+740c740
+< #if _DEBUG
+---
+> #if !defined(NDEBUG)
+798c798
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/base/openssladapter.h libjingle/files/talk/base/openssladapter.h
+72c72
+< #if _DEBUG
+---
+> #if !defined(NDEBUG)
+74c74
+< #endif // !_DEBUG
+---
+> #endif // !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/base/physicalsocketserver.cc libjingle/files/talk/base/physicalsocketserver.cc
+61a62
+> #include "talk/base/winsock_initializer.h"
+67,86d67
+< #ifdef WIN32
+< 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
+<
+124a106,108
+> #ifdef WIN32
+> EnsureWinsockInit();
+> #endif
+187c171,177
+< addr2.Resolve(); // TODO: Do this async later?
+---
+> // TODO: Do this async later?
+> if (!addr2.Resolve()) {
+> LOG(LS_ERROR) << "Resolving addr failed";
+> UpdateLastError();
+> Close();
+> return SOCKET_ERROR;
+> }
+265a256,259
+> LOG(LS_WARNING) << "EOF from socket; deferring close event";
+> // Must turn this back on so that the select() loop will notice the close
+> // event.
+> enabled_events_ |= kfRead;
+402a397
+> virtual bool IsDescriptorClosed() = 0;
+452a448,451
+> virtual bool IsDescriptorClosed() {
+> return false;
+> }
+>
+490a490,497
+> virtual bool IsDescriptorClosed() {
+> // We don't have a reliable way of distinguishing end-of-stream
+> // from readability. So test on each readable call. Is this
+> // inefficient? Probably.
+> char ch;
+> return (0 == ::recv(s_, &ch, 1, MSG_PEEK));
+> }
+>
+546a554,557
+> virtual bool IsDescriptorClosed() {
+> return false;
+> }
+>
+916c927,931
+< ff |= kfRead;
+---
+> if (pdispatcher->IsDescriptorClosed()) {
+> ff |= kfClose;
+> } else {
+> ff |= kfRead;
+> }
+diff -r libjingle-0.4.0/talk/base/proxydetect.cc libjingle/files/talk/base/proxydetect.cc
+205,206c205,206
+< const char* list = slist.c_str();
+< while (*list) {
+---
+> const char* clist = slist.c_str();
+> while (*clist) {
+208,209c208,209
+< if (isspace(*list)) {
+< ++list;
+---
+> if (isspace(*clist)) {
+> ++clist;
+214,217c214,217
+< const char * start = list;
+< if (const char * end = strchr(list, sep)) {
+< len = (end - list);
+< list += len + 1;
+---
+> const char * start = clist;
+> if (const char * end = strchr(clist, sep)) {
+> len = (end - clist);
+> clist += len + 1;
+219,220c219,220
+< len = strlen(list);
+< list += len;
+---
+> len = strlen(clist);
+> clist += len;
+diff -r libjingle-0.4.0/talk/base/schanneladapter.cc libjingle/files/talk/base/schanneladapter.cc
+607c607
+< size_t read = min(cb, readable.size());
+---
+> size_t read = _min(cb, readable.size());
+diff -r libjingle-0.4.0/talk/base/scoped_ptr.h libjingle/files/talk/base/scoped_ptr.h
+36,257c36
+< namespace talk_base {
+<
+< 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);
+< }
+<
+< } // namespace talk_base
+<
+< // TODO: get rid of this global using
+< using talk_base::scoped_ptr;
+---
+> #include "base/scoped_ptr.h"
+diff -r libjingle-0.4.0/talk/base/signalthread.cc libjingle/files/talk/base/signalthread.cc
+12a13,15
+> main_->SignalQueueDestroyed.connect(this,
+> &SignalThread::OnMainThreadDestroyed);
+> refcount_ = 1;
+15a19,23
+> void SignalThread::OnMainThreadDestroyed() {
+> EnterExit ee(this);
+> main_ = NULL;
+> }
+>
+19a28
+> EnterExit ee(this);
+25a35
+> EnterExit ee(this);
+27c37
+< if (kInit == state_) {
+---
+> if (kInit == state_ || kComplete == state_) {
+36c46,47
+< void SignalThread::Destroy() {
+---
+> void SignalThread::Destroy(bool wait) {
+> EnterExit ee(this);
+39,40c50,51
+< delete this;
+< } else if (kRunning == state_) {
+---
+> refcount_--;
+> } else if (kRunning == state_ || kReleasing == state_) {
+42,47c53,63
+< // A couple tricky issues here:
+< // 1) Thread::Stop() calls Join(), which we don't want... we just want
+< // to stop the MessageQueue, which causes ContinueWork() to return false.
+< // 2) OnWorkStop() must follow Stop(), so that when the thread wakes up
+< // due to OWS(), ContinueWork() will return false.
+< worker_.MessageQueue::Stop();
+---
+> // OnWorkStop() must follow Quit(), so that when the thread wakes up due to
+> // OWS(), ContinueWork() will return false.
+> if (wait) {
+> // Release the thread's lock so that it can return from ::Run.
+> cs_.Leave();
+> worker_.Stop();
+> cs_.Enter();
+> refcount_--;
+> } else {
+> worker_.Quit();
+> }
+54a71
+> EnterExit ee(this);
+57c74
+< delete this;
+---
+> refcount_--;
+66a84
+> EnterExit ee(this);
+71a90
+> EnterExit ee(this);
+81a101,111
+> // Before signaling that the work is done, make sure that the worker
+> // thread actually is done. We got here because DoWork() finished and
+> // Run() posted the ST_MSG_WORKER_DONE message. This means the worker
+> // thread is about to go away anyway, but sometimes it doesn't actually
+> // finish before SignalWorkDone is processed, and for a reusable
+> // SignalThread this makes an assert in thread.cc fire.
+> //
+> // Calling Stop() on the worker ensures that the OS thread that underlies
+> // the worker will finish, and will be set to NULL, enabling us to call
+> // Start() again.
+> worker_.Stop();
+85c115
+< delete this;
+---
+> refcount_--;
+92c122,127
+< main_->Post(this, ST_MSG_WORKER_DONE);
+---
+> {
+> EnterExit ee(this);
+> if (main_) {
+> main_->Post(this, ST_MSG_WORKER_DONE);
+> }
+> }
+diff -r libjingle-0.4.0/talk/base/signalthread.h libjingle/files/talk/base/signalthread.h
+15a16,19
+> // Periodic tasks: Wait for SignalWorkDone, then eventually call Start()
+> // again to repeat the task. When the instance isn't needed anymore,
+> // call Release. DoWork, OnWorkStart and OnWorkStop are called again,
+> // on a new thread.
+22c26
+< class SignalThread : protected MessageHandler {
+---
+> class SignalThread : public sigslot::has_slots<>, protected MessageHandler {
+35,36c39,41
+< // SignalWorkDone will not be signalled.
+< void Destroy();
+---
+> // SignalWorkDone will not be signalled. If wait is true, does not return
+> // until the thread is deleted.
+> void Destroy(bool wait);
+53c58
+<
+---
+>
+57c62
+< // Context: Worker Thread. Subclass should call periodically to
+---
+> // Context: Worker Thread. Subclass should call periodically to
+67c72
+<
+---
+>
+79a85,106
+> class EnterExit {
+> friend class SignalThread;
+>
+> SignalThread * t_;
+>
+> EnterExit(SignalThread * t) : t_(t) {
+> t_->cs_.Enter();
+> t_->refcount_ += 1;
+> }
+> ~EnterExit() {
+> bool d = (0 == (--(t_->refcount_)));
+> t_->cs_.Leave();
+> if (d)
+> delete t_;
+> }
+> };
+>
+> friend class EnterExit;
+>
+> CriticalSection cs_;
+> int refcount_;
+>
+80a108
+> void OnMainThreadDestroyed();
+84c112,118
+< enum State { kInit, kRunning, kComplete, kStopping, kReleasing } state_;
+---
+> enum State {
+> kInit, // Initialized, but not started
+> kRunning, // Started and doing work
+> kReleasing, // Same as running, but to be deleted when work is done
+> kComplete, // Work is done
+> kStopping, // Work is being interrupted
+> } state_;
+diff -r libjingle-0.4.0/talk/base/sigslot.h libjingle/files/talk/base/sigslot.h
+530c530
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+676c676
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+807c807
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+937c937
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+1067c1067
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+1199c1199
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+1331c1331
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+1463c1463
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+1596c1596
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/base/socket.h libjingle/files/talk/base/socket.h
+77a78
+> #undef ETIMEDOUT // remove pthread.h's definition
+diff -r libjingle-0.4.0/talk/base/socketadapters.cc libjingle/files/talk/base/socketadapters.cc
+43a44,45
+> #include <cstring>
+>
+diff -r libjingle-0.4.0/talk/base/socketaddress.cc libjingle/files/talk/base/socketaddress.cc
+52c52
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+54c54
+< #else // !_DEBUG
+---
+> #else // defined(NDEBUG)
+56c56
+< #endif // !_DEBUG
+---
+> #endif // !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/base/socketfactory.h libjingle/files/talk/base/socketfactory.h
+32a33
+> #include "talk/base/ssladapter.h"
+46a48,52
+>
+> // Wraps the given socket in an SSL adapter.
+> virtual SSLAdapter* CreateSSLAdapter(AsyncSocket* socket) {
+> return SSLAdapter::Create(socket);
+> }
+diff -r libjingle-0.4.0/talk/base/socketpool.cc libjingle/files/talk/base/socketpool.cc
+142d141
+< ASSERT(false);
+189d187
+< ASSERT(false);
+diff -r libjingle-0.4.0/talk/base/ssladapter.cc libjingle/files/talk/base/ssladapter.cc
+34c34,35
+< #define SSL_USE_OPENSSL 1
+---
+> // Turn off OpenSSL
+> //#define SSL_USE_OPENSSL 1
+84a86
+> #if SSL_USE_OPENSSL || SSL_USE_SCHANNEL
+85a88,90
+> #else
+> return NULL;
+> #endif
+diff -r libjingle-0.4.0/talk/base/stream.cc libjingle/files/talk/base/stream.cc
+27a28
+> #include <stdio.h>
+diff -r libjingle-0.4.0/talk/base/stringencode.cc libjingle/files/talk/base/stringencode.cc
+34a35
+> #include <stdlib.h>
+525c526
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+575c576
+< #endif // _DEBUG
+---
+> #endif // !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/base/stringutils.cc libjingle/files/talk/base/stringutils.cc
+72c72
+< #if _DEBUG
+---
+> #if !defined(NDEBUG)
+76c76
+< #endif // _DEBUG
+---
+> #endif // !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/base/stringutils.h libjingle/files/talk/base/stringutils.h
+33a34
+> #include <string.h>
+87a89
+> #if 0
+93a96
+> #endif
+200,208d202
+< 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>
+218a213,221
+> 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;
+> }
+>
+272c275
+< inline static const char* Traits<char>::empty_str() { return ""; }
+---
+> inline static const char* empty_str() { return ""; }
+diff -r libjingle-0.4.0/talk/base/task.cc libjingle/files/talk/base/task.cc
+5c5
+< * Redistribution and use in source and binary forms, with or without
+---
+> * Redistribution and use in source and binary forms, with or without
+8c8
+< * 1. Redistributions of source code must retain the above copyright notice,
+---
+> * 1. Redistributions of source code must retain the above copyright notice,
+13c13
+< * 3. The name of the author may not be used to endorse or promote products
+---
+> * 3. The name of the author may not be used to endorse or promote products
+17c17
+< * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+---
+> * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+19c19
+< * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+---
+> * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+23,24c23,24
+< * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+< * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+---
+> * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+> * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+141c141
+< if (aborted_ || done_)
+---
+> if (done_)
+150c150
+< Wake(); // to self-delete
+---
+> GetRunner()->WakeTasks();
+diff -r libjingle-0.4.0/talk/base/taskrunner.h libjingle/files/talk/base/taskrunner.h
+63a64,68
+> bool HasPendingTimeoutTask() {
+> return next_timeout_task_ != NULL &&
+> next_timeout_task_->TimedOut();
+> }
+>
+diff -r libjingle-0.4.0/talk/base/testclient.cc libjingle/files/talk/base/testclient.cc
+29a30
+> #include <cstring>
+diff -r libjingle-0.4.0/talk/base/thread.cc libjingle/files/talk/base/thread.cc
+100a101,105
+> bool ThreadManager::ThreadActive(Thread *thread) {
+> CritScope cs(&crit_);
+> return(std::find(threads_.begin(), threads_.end(), thread) != threads_.end());
+> }
+>
+103a109
+> stopped_ = false;
+124,125c130,135
+< pthread_create(&thread_, &attr, PreRun, this);
+< started_ = true;
+---
+> CritScope cs(&started_crit_);
+> // Make sure Join() hasn't been called yet.
+> if (stopped_)
+> return;
+> if (pthread_create(&thread_, &attr, PreRun, this) == 0)
+> started_ = true;
+128a139,140
+> CritScope cs(&started_crit_);
+> stopped_ = true;
+131a144
+> started_ = false;
+168a182,185
+> CritScope cs(&started_crit_);
+> // Make sure Join() hasn't been called yet.
+> if (stopped_)
+> return;
+181a199,200
+> CritScope cs(&started_crit_);
+> stopped_ = true;
+191a211,213
+> // Make sure the thread hasn't been deleted.
+> if (!g_thmgr.ThreadActive(thread))
+> return NULL;
+207c229
+< MessageQueue::Stop();
+---
+> MessageQueue::Quit();
+329c351
+< return false;
+---
+> return !IsQuitting();
+diff -r libjingle-0.4.0/talk/base/thread.h libjingle/files/talk/base/thread.h
+57a58
+> bool ThreadActive(Thread *thread);
+134a136
+> CriticalSection started_crit_;
+135a138
+> bool stopped_;
+diff -r libjingle-0.4.0/talk/base/urlencode.cc libjingle/files/talk/base/urlencode.cc
+0a1,2
+> #include <stdlib.h>
+> #include <string.h>
+Only in libjingle-0.4.0/talk/base: win32socketserver.cc
+Only in libjingle-0.4.0/talk/base: win32socketserver.h
+Only in libjingle/files/talk/base: win32window.cc
+diff -r libjingle-0.4.0/talk/base/winping.cc libjingle/files/talk/base/winping.cc
+133c133
+< return sizeof(ICMP_ECHO_REPLY) + max(8UL, data_size);
+---
+> return sizeof(ICMP_ECHO_REPLY) + _max((uint32)(8UL), data_size);
+317c317
+< } // namespace talk_base
+\ No newline at end of file
+---
+> } // namespace talk_base
+Only in libjingle/files/talk/base: winsock_initializer.cc
+Only in libjingle/files/talk/base: winsock_initializer.h
+Only in libjingle-0.4.0/talk: examples
+Only in libjingle-0.4.0/talk: libjingle.sln
+Only in libjingle-0.4.0/talk: libjingle.vcproj
+Only in libjingle-0.4.0/talk: p2p
+Only in libjingle-0.4.0/talk: session
+Only in libjingle-0.4.0/talk: third_party
+Only in libjingle-0.4.0/talk/xmllite: Makefile.in
+diff -r libjingle-0.4.0/talk/xmllite/qname.cc libjingle/files/talk/xmllite/qname.cc
+39c39
+< int result = ns.size() * 101;
+---
+> int result = static_cast<int>(ns.size()) * 101;
+diff -r libjingle-0.4.0/talk/xmllite/qname.h libjingle/files/talk/xmllite/qname.h
+64d63
+< refcount_(1),
+66c65,66
+< localPart_(local) {}
+---
+> localPart_(local),
+> refcount_(1) {}
+diff -r libjingle-0.4.0/talk/xmllite/xmlelement.cc libjingle/files/talk/xmllite/xmlelement.cc
+88c88,89
+< pLastChild_(NULL) {
+---
+> pLastChild_(NULL),
+> cdata_(false) {
+97c98,99
+< pLastChild_(NULL) {
+---
+> pLastChild_(NULL),
+> cdata_(false) {
+125a128
+> cdata_ = elt.cdata_;
+133c136,137
+< pLastChild_(NULL) {
+---
+> pLastChild_(NULL),
+> cdata_(false) {
+393a398,403
+> XmlElement::AddCDATAText(const char * buf, int len) {
+> cdata_ = true;
+> AddParsedText(buf, len);
+> }
+>
+> void
+diff -r libjingle-0.4.0/talk/xmllite/xmlelement.h libjingle/files/talk/xmllite/xmlelement.h
+203a204,206
+> // Note: CDATA is not supported by XMPP, therefore using this function will
+> // generate non-XMPP compatible XML.
+> void AddCDATAText(const char * buf, int len);
+217a221,222
+> bool IsCDATA() const { return cdata_; }
+>
+228a234
+> bool cdata_;
+diff -r libjingle-0.4.0/talk/xmllite/xmlparser.cc libjingle/files/talk/xmllite/xmlparser.cc
+28,29d27
+< #include "talk/xmllite/xmlparser.h"
+<
+35a34
+> #include "talk/xmllite/xmlconstants.h"
+38c37
+< #include "talk/xmllite/xmlconstants.h"
+---
+> #include "talk/xmllite/xmlparser.h"
+119a119,121
+> context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+> XML_GetCurrentColumnNumber(expat_),
+> XML_GetCurrentByteIndex(expat_));
+127a130,132
+> context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+> XML_GetCurrentColumnNumber(expat_),
+> XML_GetCurrentByteIndex(expat_));
+134a140,142
+> context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+> XML_GetCurrentColumnNumber(expat_),
+> XML_GetCurrentByteIndex(expat_));
+168c176,180
+< if (XML_Parse(expat_, data, static_cast<int>(len), isFinal) != XML_STATUS_OK)
+---
+> if (XML_Parse(expat_, data, static_cast<int>(len), isFinal) !=
+> XML_STATUS_OK) {
+> context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+> XML_GetCurrentColumnNumber(expat_),
+> XML_GetCurrentByteIndex(expat_));
+169a182
+> }
+193c206,209
+< raised_(XML_ERROR_NONE) {
+---
+> raised_(XML_ERROR_NONE),
+> line_number_(0),
+> column_number_(0),
+> byte_index_(0) {
+247c263,285
+< XmlParser::ParseContext::~ParseContext() {
+---
+> void
+> XmlParser::ParseContext::SetPosition(XML_Size line, XML_Size column,
+> XML_Index byte_index) {
+> line_number_ = line;
+> column_number_ = column;
+> byte_index_ = byte_index;
+> }
+>
+> void
+> XmlParser::ParseContext::GetPosition(unsigned long * line,
+> unsigned long * column,
+> unsigned long * byte_index) {
+> if (line != NULL) {
+> *line = static_cast<unsigned long>(line_number_);
+> }
+>
+> if (column != NULL) {
+> *column = static_cast<unsigned long>(column_number_);
+> }
+>
+> if (byte_index != NULL) {
+> *byte_index = static_cast<unsigned long>(byte_index_);
+> }
+249a288
+> XmlParser::ParseContext::~ParseContext() {
+251a291
+> }
+diff -r libjingle-0.4.0/talk/xmllite/xmlparser.h libjingle/files/talk/xmllite/xmlparser.h
+48a49,50
+> virtual void GetPosition(unsigned long * line, unsigned long * column,
+> unsigned long * byte_index) = 0;
+85a88,89
+> virtual void GetPosition(unsigned long * line, unsigned long * column,
+> unsigned long * byte_index);
+91a96
+> void SetPosition(XML_Size line, XML_Size column, XML_Index byte_index);
+96a102,104
+> XML_Size line_number_;
+> XML_Size column_number_;
+> XML_Index byte_index_;
+diff -r libjingle-0.4.0/talk/xmllite/xmlprinter.cc libjingle/files/talk/xmllite/xmlprinter.cc
+46a47
+> void PrintCDATAText(const std::string & text);
+134,136c135,141
+< if (pchild->IsText())
+< PrintBodyText(pchild->AsText()->Text());
+< else
+---
+> if (pchild->IsText()) {
+> if (element->IsCDATA()) {
+> PrintCDATAText(pchild->AsText()->Text());
+> } else {
+> PrintBodyText(pchild->AsText()->Text());
+> }
+> } else
+188a194,197
+> void
+> XmlPrinterImpl::PrintCDATAText(const std::string & text) {
+> *pout_ << "<![CDATA[" << text << "]]>";
+> }
+Only in libjingle-0.4.0/talk/xmpp: Makefile.in
+Only in libjingle-0.4.0/talk/xmpp: constants.cc
+Only in libjingle-0.4.0/talk/xmpp: constants.h
+diff -r libjingle-0.4.0/talk/xmpp/jid.cc libjingle/files/talk/xmpp/jid.cc
+33c33
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+diff -r libjingle-0.4.0/talk/xmpp/plainsaslhandler.h libjingle/files/talk/xmpp/plainsaslhandler.h
+31d30
+< #include "talk/xmpp/saslhandler.h"
+32a32,34
+> #include <string>
+>
+> #include "talk/xmpp/saslhandler.h"
+68a71,76
+>
+> virtual bool GetTlsServerInfo(const talk_base::SocketAddress& server,
+> std::string* tls_server_hostname,
+> std::string* tls_server_domain) {
+> return false;
+> }
+diff -r libjingle-0.4.0/talk/xmpp/prexmppauth.h libjingle/files/talk/xmpp/prexmppauth.h
+33d32
+< #include "talk/xmpp/saslhandler.h"
+64c63
+< class PreXmppAuth : public SaslHandler {
+---
+> class PreXmppAuth {
+diff -r libjingle-0.4.0/talk/xmpp/saslcookiemechanism.h libjingle/files/talk/xmpp/saslcookiemechanism.h
+33c33
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+40,41c40,55
+< SaslCookieMechanism(const std::string & mechanism, const std::string & username, const std::string & cookie) :
+< mechanism_(mechanism), username_(username), cookie_(cookie) {}
+---
+> SaslCookieMechanism(const std::string & mechanism,
+> const std::string & username,
+> const std::string & cookie,
+> const std::string & token_service)
+> : mechanism_(mechanism),
+> username_(username),
+> cookie_(cookie),
+> token_service_(token_service) {}
+>
+> SaslCookieMechanism(const std::string & mechanism,
+> const std::string & username,
+> const std::string & cookie)
+> : mechanism_(mechanism),
+> username_(username),
+> cookie_(cookie),
+> token_service_("") {}
+48a63,67
+> if (!token_service_.empty()) {
+> el->AddAttr(
+> QName(true, "http://www.google.com/talk/protocol/auth", "service"),
+> token_service_);
+> }
+62a82
+> std::string token_service_;
+diff -r libjingle-0.4.0/talk/xmpp/saslhandler.h libjingle/files/talk/xmpp/saslhandler.h
+31a32,34
+> #include <vector>
+>
+> #include "talk/base/socketaddress.h"
+53a57,63
+>
+> // Fills in the tls server hostname/domain to use for the given
+> // server (and returns true). Return false if you want the defaults
+> // to be used.
+> virtual bool GetTlsServerInfo(const talk_base::SocketAddress& server,
+> std::string* tls_server_hostname,
+> std::string* tls_server_domain) = 0;
+diff -r libjingle-0.4.0/talk/xmpp/saslmechanism.cc libjingle/files/talk/xmpp/saslmechanism.cc
+30c30
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+diff -r libjingle-0.4.0/talk/xmpp/xmppclient.cc libjingle/files/talk/xmpp/xmppclient.cc
+30c30
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+32a33
+> #include "talk/xmpp/saslhandler.h"
+68a70
+> scoped_ptr<SaslHandler> sasl_handler_;
+93c95,99
+< XmppClient::Connect(const XmppClientSettings & settings, const std::string & lang, AsyncSocket * socket, PreXmppAuth * pre_auth) {
+---
+> XmppClient::Connect(const XmppClientSettings & settings,
+> const std::string & lang,
+> AsyncSocket * socket,
+> PreXmppAuth * pre_auth,
+> SaslHandler * sasl_handler) {
+113,125c119,125
+< //
+< // The talk.google.com server expects you to use "gmail.com" in the
+< // stream, and expects the domain certificate to be "gmail.com" as well.
+< // For all other servers, we leave the strings empty, which causes
+< // the jid's domain to be used. "foo@example.com" -> stream to="example.com"
+< // tls certificate for "example.com"
+< //
+< // This is only true when using Gaia auth, so let's say if there's no preauth,
+< // we should use the actual server name
+< if ((settings.server().IPAsString() == buzz::STR_TALK_GOOGLE_COM ||
+< settings.server().IPAsString() == buzz::STR_TALKX_L_GOOGLE_COM) &&
+< pre_auth != NULL) {
+< d_->engine_->SetTlsServer(buzz::STR_GMAIL_COM, buzz::STR_GMAIL_COM);
+---
+> if (sasl_handler) {
+> std::string tls_server_hostname, tls_server_domain;
+> if (sasl_handler->GetTlsServerInfo(settings.server(),
+> &tls_server_hostname,
+> &tls_server_domain)) {
+> d_->engine_->SetTlsServer(tls_server_hostname, tls_server_domain);
+> }
+139a140
+> d_->sasl_handler_.reset(sasl_handler);
+200a202,209
+> if (d_->sasl_handler_.get()) {
+> d_->engine_->SetSaslHandler(d_->sasl_handler_.release());
+> }
+> else {
+> d_->engine_->SetSaslHandler(new PlainSaslHandler(
+> d_->engine_->GetUser(), d_->pass_, d_->allow_plain_));
+> }
+>
+209,210d217
+< d_->engine_->SetSaslHandler(new PlainSaslHandler(
+< d_->engine_->GetUser(), d_->pass_, d_->allow_plain_));
+253,254d259
+< // transfer ownership of pre_auth_ to engine
+< d_->engine_->SetSaslHandler(d_->pre_auth_.release());
+261a267,268
+> d_->pre_engine_error_ = XmppEngine::ERROR_SOCKET;
+> d_->pre_engine_subcode_ = d_->socket_->GetError();
+347c354
+< //#ifdef _DEBUG
+---
+> //#if !defined(NDEBUG)
+375c382
+< //#ifdef _DEBUG
+---
+> //#if !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/xmpp/xmppclient.h libjingle/files/talk/xmpp/xmppclient.h
+42a43
+> class SaslHandler;
+80c81,82
+< PreXmppAuth * preauth);
+---
+> PreXmppAuth * preauth,
+> SaslHandler * sasl_handler);
+141c143
+< std::string XmppClient::GetStateName(int state) const {
+---
+> std::string GetStateName(int state) const {
+diff -r libjingle-0.4.0/talk/xmpp/xmppclientsettings.h libjingle/files/talk/xmpp/xmppclientsettings.h
+31d30
+< #include "talk/p2p/base/port.h"
+32a32,45
+> #include "talk/base/proxyinfo.h"
+>
+> namespace cricket {
+>
+> // This enum was taken from talk/p2p/base/port.h, which is the only
+> // thing we actually need from the p2p directory.
+> enum ProtocolType {
+> PROTO_UDP,
+> PROTO_TCP,
+> PROTO_SSLTCP,
+> PROTO_LAST = PROTO_SSLTCP
+> };
+>
+> } // namespace cricket
+59a73,75
+> void set_token_service(const std::string & token_service) {
+> token_service_ = token_service;
+> }
+75a92
+> const std::string & token_service() const { return token_service_; }
+93a111
+> std::string token_service_;
+Only in libjingle/files/talk/xmpp: xmppconstants.cc
+Only in libjingle/files/talk/xmpp: xmppconstants.h
+diff -r libjingle-0.4.0/talk/xmpp/xmppengineimpl.cc libjingle/files/talk/xmpp/xmppengineimpl.cc
+37c37
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+diff -r libjingle-0.4.0/talk/xmpp/xmppengineimpl_iq.cc libjingle/files/talk/xmpp/xmppengineimpl_iq.cc
+32c32
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+diff -r libjingle-0.4.0/talk/xmpp/xmpplogintask.cc libjingle/files/talk/xmpp/xmpplogintask.cc
+34c34
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+44c44
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+59c59
+< #endif // _DEBUG
+---
+> #endif // !defined(NDEBUG)
+103c103
+< #if _DEBUG
+---
+> #if !defined(NDEBUG)
+106c106
+< #endif // _DEBUG
+---
+> #endif // !defined(NDEBUG)
+218a219,221
+> auth->SetAttr(QN_GOOGLE_ALLOW_GENERATED_JID_XMPP_LOGIN, "true");
+> auth->SetAttr(QN_GOOGLE_AUTH_CLIENT_USES_FULL_BIND_RESULT, "true");
+>
+diff -r libjingle-0.4.0/talk/xmpp/xmpplogintask.h libjingle/files/talk/xmpp/xmpplogintask.h
+93c93
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+95c95
+< #endif // _DEBUG
+---
+> #endif // !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/xmpp/xmppstanzaparser.cc libjingle/files/talk/xmpp/xmppstanzaparser.cc
+32c32
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+diff -r libjingle-0.4.0/talk/xmpp/xmpptask.cc libjingle/files/talk/xmpp/xmpptask.cc
+31c31
+< #include "talk/xmpp/constants.h"
+---
+> #include "talk/xmpp/xmppconstants.h"
+40c40
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+88c88
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+174c174
+< }
+\ No newline at end of file
+---
+> }
+diff -r libjingle-0.4.0/talk/xmpp/xmpptask.h libjingle/files/talk/xmpp/xmpptask.h
+80c80
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+106,107c106,107
+< XmlElement *MakeIqResult(const XmlElement* query);
+< XmlElement *MakeIq(const std::string& type,
+---
+> static XmlElement *MakeIqResult(const XmlElement* query);
+> static XmlElement *MakeIq(const std::string& type,
+123c123
+< #ifdef _DEBUG
+---
+> #if !defined(NDEBUG)
+diff -r libjingle-0.4.0/talk/base/common.cc libjingle/files/talk/base/common.cc
+62c62
+< } // namespace talk_base
+\ No newline at end of file
+---
+> } // namespace talk_base
+diff -r libjingle-0.4.0/talk/base/httpbase.cc libjingle/files/talk/base/httpbase.cc
+154c154
+< if (sscanf(value, "%d", &data_size_) != 1) {
+---
+> if (sscanf(value, "%zu", &data_size_) != 1) {
+diff -r libjingle-0.4.0/talk/base/httpcommon.cc libjingle/files/talk/base/httpcommon.cc
+339c339
+< &tval.tm_hour, &tval.tm_min, &tval.tm_sec, &zone)) {
+---
+> &tval.tm_hour, &tval.tm_min, &tval.tm_sec, zone)) {
+472c472
+< uint32 vmajor, vminor;
+---
+> unsigned long vmajor, vminor;
+549,550c549,550
+< uint32 vmajor, vminor;
+< if ((sscanf(line, "HTTP/%lu.%lu %lu%n", &vmajor, &vminor, &scode, &pos) != 3)
+---
+> unsigned long vmajor, vminor;
+> if ((sscanf(line, "HTTP/%lu.%lu %lu%zu", &vmajor, &vminor, &scode, &pos) != 3)
+693c693
+< sprintf(buffer, "%d", time(0));
+---
+> sprintf(buffer, "%ld", time(0));
+diff -r libjingle-0.4.0/talk/base/httpcommon.h libjingle/files/talk/base/httpcommon.h
+329c329
+< uint32 scode;
+---
+> unsigned long scode;
+diff -r libjingle-0.4.0/talk/base/diskcache.cc libjingle/files/talk/base/diskcache.cc
+300c300
+< if (1 != sscanf(pathname.extension().c_str(), ".%u", index))
+---
+> if (1 != sscanf(pathname.extension().c_str(), ".%zu", index))
+diff -r libjingle-0.4.0/talk/base/logging.cc libjingle/files/talk/base/logging.cc
+69c69
+< snprintf(buffer, sizeof(buffer), "0x%08lx", err);
+---
+> snprintf(buffer, sizeof(buffer), "0x%08x", err);
+diff -r libjingle-0.4.0/talk/base/socketadapters.cc libjingle/files/talk/base/socketadapters.cc
+360,361c360,361
+< uint32 code;
+< if (sscanf(data, "HTTP/%*lu.%*lu %lu", &code) != 1) {
+---
+> unsigned long code;
+> if (sscanf(data, "HTTP/%*u.%*u %lu", &code) != 1) {
diff --git a/third_party/libjingle/overrides/config.h b/third_party/libjingle/overrides/config.h
new file mode 100644
index 0000000..872d609
--- /dev/null
+++ b/third_party/libjingle/overrides/config.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a <config.h> include for compiling libjingle.
+// This file was generated using libjingle's autoconf utility.
+// It fixes compilation in linux.
+
+/* Chat archiving */
+//#define FEATURE_ENABLE_CHAT_ARCHIVING 1
+
+/* Enable SSL */
+//#define FEATURE_ENABLE_SSL 1
+
+/* voice mail */
+//#define FEATURE_ENABLE_VOICEMAIL 1
+
+/* Define to 1 if you have the <alsa/asoundlib.h> header file. */
+/* #undef HAVE_ALSA_ASOUNDLIB_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Have GIPS Voice Engine */
+#define HAVE_GIPS 1
+
+/* Glib is required for oRTP code */
+/* #undef HAVE_GLIB */
+
+/* Defined when we have ilbc codec lib */
+/* #undef HAVE_ILBC */
+
+/* Define to 1 if you have the <iLBC_decode.h> header file. */
+/* #undef HAVE_ILBC_DECODE_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the <openssl/ssl.h> header file. */
+#define HAVE_OPENSSL_SSL_H 1
+
+/* Define if you have semtimedop() for SysV semaphares. */
+#define HAVE_SEMTIMEDOP 1
+
+/* has speex */
+/* #undef HAVE_SPEEX */
+
+/* Define to 1 if you have the <speex.h> header file. */
+/* #undef HAVE_SPEEX_H */
+
+/* Define to 1 if you have the <speex/speex.h> header file. */
+/* #undef HAVE_SPEEX_SPEEX_H */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Building on Linux */
+#define LINUX 1
+
+/* Logging */
+#define LOGGING 1
+
+/* Building on OSX */
+/* #undef OSX */
+
+/* Name of package */
+// #define PACKAGE "dist-zip"
+
+/* Define to the address where bug reports for this package should be sent. */
+// #define PACKAGE_BUGREPORT "google-talk-open@googlegroups.com"
+
+/* Define to the full name of this package. */
+// #define PACKAGE_NAME "libjingle"
+
+/* Define to the full name and version of this package. */
+// #define PACKAGE_STRING "libjingle 0.3.0"
+
+/* Define to the one symbol short name of this package. */
+// #define PACKAGE_TARNAME "libjingle"
+
+/* Define to the version of this package. */
+// #define PACKAGE_VERSION "0.3.0"
+
+/* If we're using configure, we're on POSIX */
+// #define POSIX 1
+
+/* Build as a production build */
+//#define PRODUCTION 1
+
+/* Build as a production build */
+//#define PRODUCTION_BUILD 1
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+// #define VERSION ""
+
+/* Defined when alsa support is enabled */
+/* #undef __ALSA_ENABLED__ */
diff --git a/third_party/libjingle/overrides/talk/base/basictypes.h b/third_party/libjingle/overrides/talk/base/basictypes.h
new file mode 100644
index 0000000..d6f90cf
--- /dev/null
+++ b/third_party/libjingle/overrides/talk/base/basictypes.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file overrides the inclusion of talk/base/basictypes.h to remove
+// collisions with Chromium's base/basictypes.h. We then add back a few
+// items that Chromium's version doesn't provide, but libjingle expects.
+
+#ifndef OVERRIDES_TALK_BASE_BASICTYPES_H__
+#define OVERRIDES_TALK_BASE_BASICTYPES_H__
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+
+#ifndef INT_TYPES_DEFINED
+#define INT_TYPES_DEFINED
+#ifdef COMPILER_MSVC
+typedef __int64 int64;
+#endif /* COMPILER_MSVC */
+
+#ifdef COMPILER_MSVC
+typedef unsigned __int64 uint64;
+typedef __int64 int64;
+#define INT64_C(x) x ## I64
+#define UINT64_C(x) x ## UI64
+#define INT64_F "I64"
+#else
+#ifndef INT64_C
+#define INT64_C(x) x ## LL
+#endif
+#ifndef UINT64_C
+#define UINT64_C(x) x ## ULL
+#endif
+#ifndef INT64_F
+#define INT64_F "ll"
+#endif
+#endif /* COMPILER_MSVC */
+#endif // INT_TYPES_DEFINED
+
+#ifdef WIN32
+typedef int socklen_t;
+#endif
+
+namespace talk_base {
+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; }
+
+// For wait functions that take a number of milliseconds, kForever indicates
+// unlimited time.
+const int kForever = -1;
+}
+
+#endif // OVERRIDES_TALK_BASE_BASICTYPES_H__
diff --git a/third_party/libjingle/overrides/talk/base/constructormagic.h b/third_party/libjingle/overrides/talk/base/constructormagic.h
new file mode 100644
index 0000000..012357e
--- /dev/null
+++ b/third_party/libjingle/overrides/talk/base/constructormagic.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file overrides the inclusion of talk/base/constructormagic.h
+// We do this because constructor magic defines DISALLOW_EVIL_CONSTRUCTORS,
+// but we want to use the version from Chromium.
+
+#ifndef OVERRIDES_TALK_BASE_CONSTRUCTORMAGIC_H__
+#define OVERRIDES_TALK_BASE_CONSTRUCTORMAGIC_H__
+
+#include "base/basictypes.h"
+
+#endif // OVERRIDES_TALK_BASE_CONSTRUCTORMAGIC_H__
diff --git a/third_party/libjingle/overrides/talk/base/logging.h b/third_party/libjingle/overrides/talk/base/logging.h
new file mode 100644
index 0000000..1a865af
--- /dev/null
+++ b/third_party/libjingle/overrides/talk/base/logging.h
@@ -0,0 +1,386 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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.
+
+// LOG(sev) logs the given stream at severity "sev", which must be a
+// compile-time constant of the LoggingSeverity type, without the namespace
+// prefix.
+// LOG_V(sev) Like LOG(), but sev is a run-time variable of the LoggingSeverity
+// type (basically, it just doesn't prepend the namespace).
+// LOG_F(sev) Like LOG(), but includes the name of the current function.
+// LOG_GLE(M)(sev [, mod]) attempt to add a string description of the
+// HRESULT returned by GetLastError. The "M" variant allows searching of a
+// DLL's string table for the error description.
+// LOG_ERRNO(sev) attempts to add a string description of an errno-derived
+// error. errno and associated facilities exist on both Windows and POSIX,
+// but on Windows they only apply to the C/C++ runtime.
+// LOG_ERR(sev) is an alias for the platform's normal error system, i.e. _GLE on
+// Windows and _ERRNO on POSIX.
+// (The above three also all have _EX versions that let you specify the error
+// code, rather than using the last one.)
+// LOG_E(sev, ctx, err, ...) logs a detailed error interpreted using the
+// specified context.
+// LOG_CHECK_LEVEL(sev) (and LOG_CHECK_LEVEL_V(sev)) can be used as a test
+// before performing expensive or sensitive operations whose sole purpose is
+// to output logging data at the desired level.
+// Lastly, PLOG(sev, err) is an alias for LOG_ERR_EX.
+
+#ifndef TALK_BASE_LOGGING_H_
+#define TALK_BASE_LOGGING_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h" // NOLINT
+#endif
+
+#include <list>
+#include <sstream>
+#include <string>
+#include <utility>
+#include "talk/base/basictypes.h"
+#include "talk/base/criticalsection.h"
+
+namespace talk_base {
+
+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; };
+
+#if defined(SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS)
+#define KLABEL(x) { x, #x }
+#define TLABEL(x, y) { x, y }
+#define LASTLABEL { 0, 0 }
+#endif // defined(SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS)
+
+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.
+enum LogErrorContext {
+ ERRCTX_NONE,
+ ERRCTX_ERRNO, // System-local errno
+ ERRCTX_HRESULT, // Windows HRESULT
+ ERRCTX_OSSTATUS, // MacOS OSStatus
+
+ // Abbreviations for LOG_E macro
+ ERRCTX_EN = ERRCTX_ERRNO, // LOG_E(sev, EN, x)
+ ERRCTX_HR = ERRCTX_HRESULT, // LOG_E(sev, HR, x)
+ ERRCTX_OS = ERRCTX_OSSTATUS, // LOG_E(sev, OS, x)
+};
+
+class LogMessage {
+ public:
+ static const int NO_LOGGING;
+
+ 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_; }
+
+ // 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. Multiple streams may be specified by using AddLogToStream.
+ // LogToStream is retained for backwards compatibility; when invoked, it
+ // will discard any previously set streams and install the specified stream.
+ // GetLogToStream gets the severity for the specified stream, of if none
+ // is specified, the minimum stream severity.
+ // RemoveLogToStream removes the specified stream, without destroying it.
+ static void LogToStream(StreamInterface* stream, int min_sev);
+ static int GetLogToStream(StreamInterface* stream = NULL);
+ static void AddLogToStream(StreamInterface* stream, int min_sev);
+ static void RemoveLogToStream(StreamInterface* stream);
+
+ // Testing against MinLogSeverity allows code to avoid potentially expensive
+ // logging operations by pre-checking the logging level.
+ static int GetMinLogSeverity() { return min_sev_; }
+
+ static void SetDiagnosticMode(bool f) { is_diagnostic_mode_ = f; }
+ static bool IsDiagnosticMode() { return is_diagnostic_mode_; }
+
+ // Parses the provided parameter stream to configure the options above.
+ // Useful for configuring logging from the command line. If file logging
+ // is enabled, it is output to the specified filename.
+ static void ConfigureLogging(const char* params, const char* filename);
+
+ // Convert the string to a LS_ value; also accept numeric values.
+ static int ParseLogSeverity(const std::string& value);
+
+ private:
+ typedef std::list<std::pair<StreamInterface*, int> > StreamList;
+
+ // Updates min_sev_ appropriately when debug sinks change.
+ static void UpdateMinLogSeverity();
+
+ // These assist in formatting some parts of the debug output.
+ static const char* Describe(LoggingSeverity sev);
+ static const char* DescribeFile(const char* file);
+
+ // These write out the actual log messages.
+ static void OutputToDebug(const std::string& msg, LoggingSeverity severity_);
+ static void OutputToStream(StreamInterface* stream, const std::string& msg);
+
+ // 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_;
+
+ // Global lock for the logging subsystem
+ static CriticalSection crit_;
+
+ // dbg_sev_ is 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_, ctx_sev_;
+
+ // The output streams and their associated severities
+ static StreamList streams_;
+
+ // Flags for formatting options
+ static bool thread_, timestamp_;
+
+ // The timestamp at which logging started.
+ static uint32 start_;
+
+ // are we in diagnostic mode (as defined by the app)?
+ static bool is_diagnostic_mode_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+//////////////////////////////////////////////////////////////////////
+// Logging Helpers
+//////////////////////////////////////////////////////////////////////
+
+class LogMultilineState {
+ public:
+ size_t unprintable_count_[2];
+ LogMultilineState() {
+ unprintable_count_[0] = unprintable_count_[1] = 0;
+ }
+};
+
+// When possible, pass optional state variable to track various data across
+// multiple calls to LogMultiline. Otherwise, pass NULL.
+void LogMultiline(LoggingSeverity level, const char* label, bool input,
+ const void* data, size_t len, bool hex_mode,
+ LogMultilineState* state);
+
+//////////////////////////////////////////////////////////////////////
+// Macros which automatically disable logging when LOGGING == 0
+//////////////////////////////////////////////////////////////////////
+
+// If LOGGING is not explicitly defined, default to enabled in debug mode
+#if defined(SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS)
+#if !defined(LOGGING)
+#if defined(_DEBUG) && !defined(NDEBUG) && !defined(NO_LIBJINGLE_LOGGING)
+#define LOGGING 1
+#else
+#define LOGGING 0
+#endif
+#endif // !defined(LOGGING)
+
+#ifndef LOG
+#if LOGGING
+
+// The following non-obvious technique for implementation of a
+// conditional log stream was stolen from google3/base/logging.h.
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros. This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+
+class LogMessageVoidify {
+ public:
+ LogMessageVoidify() { }
+ // This has to be an operator with a precedence lower than << but
+ // higher than ?:
+ void operator&(std::ostream&) { }
+};
+
+#define LOG_SEVERITY_PRECONDITION(sev) \
+ !(talk_base::LogMessage::Loggable(sev)) \
+ ? (void) 0 \
+ : talk_base::LogMessageVoidify() &
+
+#define LOG(sev) \
+ LOG_SEVERITY_PRECONDITION(talk_base::sev) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev).stream()
+
+// The _V version is for when a variable is passed in. It doesn't do the
+// namespace concatination.
+#define LOG_V(sev) \
+ LOG_SEVERITY_PRECONDITION(sev) \
+ talk_base::LogMessage(__FILE__, __LINE__, sev).stream()
+
+// The _F version prefixes the message with the current function name.
+#if defined(__GNUC__) && defined(_DEBUG)
+#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": "
+#else
+#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
+#endif
+
+#define LOG_CHECK_LEVEL(sev) \
+ talk_base::LogCheckLevel(talk_base::sev)
+#define LOG_CHECK_LEVEL_V(sev) \
+ talk_base::LogCheckLevel(sev)
+inline bool LogCheckLevel(LoggingSeverity sev) {
+ return (LogMessage::GetMinLogSeverity() <= sev);
+}
+
+#define LOG_E(sev, ctx, err, ...) \
+ LOG_SEVERITY_PRECONDITION(talk_base::sev) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \
+ talk_base::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \
+ .stream()
+
+#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)talk_base:: LogMessage(NULL, 0, talk_base::sev).stream()
+#define LOG_V(sev) \
+ while (false) talk_base::LogMessage(NULL, 0, sev).stream()
+#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
+#define LOG_CHECK_LEVEL(sev) \
+ false
+#define LOG_CHECK_LEVEL_V(sev) \
+ false
+
+#define LOG_E(sev, ctx, err, ...) \
+ while (false) talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \
+ talk_base::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \
+ .stream()
+
+#endif // !LOGGING
+
+#define LOG_ERRNO_EX(sev, err) \
+ LOG_E(sev, ERRNO, err)
+#define LOG_ERRNO(sev) \
+ LOG_ERRNO_EX(sev, errno)
+
+#ifdef WIN32
+#define LOG_GLE_EX(sev, err) \
+ LOG_E(sev, HRESULT, err)
+#define LOG_GLE(sev) \
+ LOG_GLE_EX(sev, GetLastError())
+#define LOG_GLEM(sev, mod) \
+ LOG_E(sev, HRESULT, GetLastError(), mod)
+#define LOG_ERR_EX(sev, err) \
+ LOG_GLE_EX(sev, err)
+#define LOG_ERR(sev) \
+ LOG_GLE(sev)
+#define LAST_SYSTEM_ERROR \
+ (::GetLastError())
+#elif POSIX
+#define LOG_ERR_EX(sev, err) \
+ LOG_ERRNO_EX(sev, err)
+#define LOG_ERR(sev) \
+ LOG_ERRNO(sev)
+#define LAST_SYSTEM_ERROR \
+ (errno)
+#endif // WIN32
+
+#define PLOG(sev, err) \
+ LOG_ERR_EX(sev, err)
+
+// TODO(?): Add an "assert" wrapper that logs in the same manner.
+
+#endif // LOG
+#endif // defined(SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS)
+
+} // namespace talk_base
+
+#endif // TALK_BASE_LOGGING_H_
diff --git a/third_party/libjingle/overrides/talk/base/scoped_ptr.h b/third_party/libjingle/overrides/talk/base/scoped_ptr.h
new file mode 100644
index 0000000..bfe5f14
--- /dev/null
+++ b/third_party/libjingle/overrides/talk/base/scoped_ptr.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file overrides the inclusion of talk/base/scoped_ptr.h. We use
+// a version of scoped_ptr from Chromium base, to avoid multiple definitions.
+
+#ifndef OVERRIDES_TALK_BASE_SCOPED_PTR_H__
+#define OVERRIDES_TALK_BASE_SCOPED_PTR_H__
+
+#include "base/scoped_ptr.h"
+
+namespace talk_base {
+
+using ::scoped_ptr;
+using ::scoped_array;
+
+} // namespace talk_base
+
+#endif // OVERRIDES_TALK_BASE_SCOPED_PTR_H__
diff --git a/third_party/libjingle/overrides/talk/base/win32socketinit.cc b/third_party/libjingle/overrides/talk/base/win32socketinit.cc
new file mode 100644
index 0000000..2a05f02
--- /dev/null
+++ b/third_party/libjingle/overrides/talk/base/win32socketinit.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Redirect Libjingle's winsock initialization activity into Chromium's
+// singleton object that managest precisely that for the browser.
+
+#include "talk/base/win32socketinit.h"
+
+#include "net/base/winsock_init.h"
+
+#ifndef WIN32
+#error "Only compile this on Windows"
+#endif
+
+namespace talk_base {
+
+void EnsureWinsockInit() {
+ net::EnsureWinsockInit();
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/overrides/talk/xmllite/qname.cc b/third_party/libjingle/overrides/talk/xmllite/qname.cc
new file mode 100644
index 0000000..5c9e62d
--- /dev/null
+++ b/third_party/libjingle/overrides/talk/xmllite/qname.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "talk/xmllite/qname.h"
+
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+QName::QName() : namespace_(QN_EMPTY.namespace_),
+ local_part_(QN_EMPTY.local_part_) {}
+
+QName::QName(const std::string & ns, const std::string & local) :
+ namespace_(ns), local_part_(local) {}
+
+QName::QName(bool add, const std::string & ns, const std::string & local) :
+ namespace_(ns), local_part_(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) :
+ namespace_(QName_Namespace(mergedOrLocal)),
+ local_part_(QName_LocalPart(mergedOrLocal)) {}
+
+std::string
+QName::Merged() const {
+ if (namespace_ == STR_EMPTY)
+ return local_part_;
+ return namespace_ + ':' + local_part_;
+}
+
+bool
+QName::operator==(const QName & other) const {
+ return
+ local_part_ == other.local_part_ &&
+ namespace_ == other.namespace_;
+}
+
+int
+QName::Compare(const QName & other) const {
+ int result = local_part_.compare(other.local_part_);
+ if (result)
+ return result;
+
+ return namespace_.compare(other.namespace_);
+}
+
+} // namespace buzz
diff --git a/third_party/libjingle/overrides/talk/xmllite/qname.h b/third_party/libjingle/overrides/talk/xmllite/qname.h
new file mode 100644
index 0000000..db80efb
--- /dev/null
+++ b/third_party/libjingle/overrides/talk/xmllite/qname.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TALK_XMLLITE_QNAME_H_
+#define TALK_XMLLITE_QNAME_H_
+
+#include <string>
+
+namespace buzz {
+
+// Default libjingle's implementation of QName class is not threadsafe. This
+// one is.
+class QName
+{
+public:
+ QName();
+ QName(const std::string & ns, const std::string & local);
+ QName(bool add, const std::string & ns, const std::string & local);
+ explicit QName(const std::string & mergedOrLocal);
+
+ const std::string & Namespace() const { return namespace_; }
+ const std::string & LocalPart() const { return local_part_; }
+ 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; }
+
+private:
+ std::string namespace_;
+ std::string local_part_;
+};
+
+} // namespace buzz
+
+#endif // TALK_XMLLITE_QNAME_H_
diff --git a/third_party/libjingle/source/AUTHORS b/third_party/libjingle/source/AUTHORS
new file mode 100644
index 0000000..e491a9e
--- /dev/null
+++ b/third_party/libjingle/source/AUTHORS
@@ -0,0 +1 @@
+Google Inc.
diff --git a/third_party/libjingle/source/COPYING b/third_party/libjingle/source/COPYING
new file mode 100644
index 0000000..d11f105
--- /dev/null
+++ b/third_party/libjingle/source/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. \ No newline at end of file
diff --git a/third_party/libjingle/source/README b/third_party/libjingle/source/README
new file mode 100644
index 0000000..2c4d73d
--- /dev/null
+++ b/third_party/libjingle/source/README
@@ -0,0 +1,57 @@
+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 and the pcp example from talk/examples/pcp.
+
+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/third_party/libjingle/source/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h b/third_party/libjingle/source/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h
new file mode 100644
index 0000000..6ff97a6
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h
@@ -0,0 +1,55 @@
+// This file is the Equifax Secure global eBusiness CA-1 certificate
+// in C form.
+
+// It was generated with the following command line:
+// > openssl x509 -in Equifax_Secure_Global_eBusiness_CA-1.cer -noout -C
+
+// The certificate was retrieved from:
+// http://www.geotrust.com/resources/root_certificates/certificates/Equifax_Secure_Global_eBusiness_CA-1.cer
+
+/* subject:/C=US/O=Equifax Secure Inc./CN=Equifax Secure Global eBusiness CA-1 */
+/* issuer :/C=US/O=Equifax Secure Inc./CN=Equifax Secure Global eBusiness CA-1 */
+unsigned char EquifaxSecureGlobalEBusinessCA1_certificate[660]={
+0x30,0x82,0x02,0x90,0x30,0x82,0x01,0xF9,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01,
+0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30,
+0x5A,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C,
+0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78,
+0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,
+0x06,0x03,0x55,0x04,0x03,0x13,0x24,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,
+0x65,0x63,0x75,0x72,0x65,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x65,0x42,0x75,
+0x73,0x69,0x6E,0x65,0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x1E,0x17,0x0D,0x39,
+0x39,0x30,0x36,0x32,0x31,0x30,0x34,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x30,
+0x30,0x36,0x32,0x31,0x30,0x34,0x30,0x30,0x30,0x30,0x5A,0x30,0x5A,0x31,0x0B,0x30,
+0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C,0x30,0x1A,0x06,0x03,
+0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,
+0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04,
+0x03,0x13,0x24,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72,
+0x65,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x65,0x42,0x75,0x73,0x69,0x6E,0x65,
+0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,
+0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,
+0x02,0x81,0x81,0x00,0xBA,0xE7,0x17,0x90,0x02,0x65,0xB1,0x34,0x55,0x3C,0x49,0xC2,
+0x51,0xD5,0xDF,0xA7,0xD1,0x37,0x8F,0xD1,0xE7,0x81,0x73,0x41,0x52,0x60,0x9B,0x9D,
+0xA1,0x17,0x26,0x78,0xAD,0xC7,0xB1,0xE8,0x26,0x94,0x32,0xB5,0xDE,0x33,0x8D,0x3A,
+0x2F,0xDB,0xF2,0x9A,0x7A,0x5A,0x73,0x98,0xA3,0x5C,0xE9,0xFB,0x8A,0x73,0x1B,0x5C,
+0xE7,0xC3,0xBF,0x80,0x6C,0xCD,0xA9,0xF4,0xD6,0x2B,0xC0,0xF7,0xF9,0x99,0xAA,0x63,
+0xA2,0xB1,0x47,0x02,0x0F,0xD4,0xE4,0x51,0x3A,0x12,0x3C,0x6C,0x8A,0x5A,0x54,0x84,
+0x70,0xDB,0xC1,0xC5,0x90,0xCF,0x72,0x45,0xCB,0xA8,0x59,0xC0,0xCD,0x33,0x9D,0x3F,
+0xA3,0x96,0xEB,0x85,0x33,0x21,0x1C,0x3E,0x1E,0x3E,0x60,0x6E,0x76,0x9C,0x67,0x85,
+0xC5,0xC8,0xC3,0x61,0x02,0x03,0x01,0x00,0x01,0xA3,0x66,0x30,0x64,0x30,0x11,0x06,
+0x09,0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x01,0x04,0x04,0x03,0x02,0x00,0x07,
+0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,
+0xFF,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xBE,0xA8,
+0xA0,0x74,0x72,0x50,0x6B,0x44,0xB7,0xC9,0x23,0xD8,0xFB,0xA8,0xFF,0xB3,0x57,0x6B,
+0x68,0x6C,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xBE,0xA8,0xA0,
+0x74,0x72,0x50,0x6B,0x44,0xB7,0xC9,0x23,0xD8,0xFB,0xA8,0xFF,0xB3,0x57,0x6B,0x68,
+0x6C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,
+0x03,0x81,0x81,0x00,0x30,0xE2,0x01,0x51,0xAA,0xC7,0xEA,0x5F,0xDA,0xB9,0xD0,0x65,
+0x0F,0x30,0xD6,0x3E,0xDA,0x0D,0x14,0x49,0x6E,0x91,0x93,0x27,0x14,0x31,0xEF,0xC4,
+0xF7,0x2D,0x45,0xF8,0xEC,0xC7,0xBF,0xA2,0x41,0x0D,0x23,0xB4,0x92,0xF9,0x19,0x00,
+0x67,0xBD,0x01,0xAF,0xCD,0xE0,0x71,0xFC,0x5A,0xCF,0x64,0xC4,0xE0,0x96,0x98,0xD0,
+0xA3,0x40,0xE2,0x01,0x8A,0xEF,0x27,0x07,0xF1,0x65,0x01,0x8A,0x44,0x2D,0x06,0x65,
+0x75,0x52,0xC0,0x86,0x10,0x20,0x21,0x5F,0x6C,0x6B,0x0F,0x6C,0xAE,0x09,0x1C,0xAF,
+0xF2,0xA2,0x18,0x34,0xC4,0x75,0xA4,0x73,0x1C,0xF1,0x8D,0xDC,0xEF,0xAD,0xF9,0xB3,
+0x76,0xB4,0x92,0xBF,0xDC,0x95,0x10,0x1E,0xBE,0xCB,0xC8,0x3B,0x5A,0x84,0x60,0x19,
+0x56,0x94,0xA9,0x55,
+};
diff --git a/third_party/libjingle/source/talk/base/asyncfile.h b/third_party/libjingle/source/talk/base/asyncfile.h
new file mode 100644
index 0000000..1437979
--- /dev/null
+++ b/third_party/libjingle/source/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 TALK_BASE_ASYNCFILE_H__
+#define TALK_BASE_ASYNCFILE_H__
+
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+
+// 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 talk_base
+
+#endif // TALK_BASE_ASYNCFILE_H__
diff --git a/third_party/libjingle/source/talk/base/asynchttprequest.cc b/third_party/libjingle/source/talk/base/asynchttprequest.cc
new file mode 100644
index 0000000..394cc6b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asynchttprequest.cc
@@ -0,0 +1,108 @@
+/*
+ * libjingle
+ * Copyright 2004--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/asynchttprequest.h"
+
+namespace talk_base {
+
+enum { MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE };
+static const int kDefaultHTTPTimeout = 30 * 1000; // 30 sec
+
+///////////////////////////////////////////////////////////////////////////////
+// AsyncHttpRequest
+///////////////////////////////////////////////////////////////////////////////
+
+AsyncHttpRequest::AsyncHttpRequest(const std::string &user_agent)
+ : firewall_(NULL), port_(80), secure_(false),
+ timeout_(kDefaultHTTPTimeout), fail_redirect_(false),
+ factory_(Thread::Current()->socketserver(), user_agent),
+ pool_(&factory_), client_(user_agent.c_str(), &pool_), error_(HE_NONE) {
+ client_.SignalHttpClientComplete.connect(this,
+ &AsyncHttpRequest::OnComplete);
+}
+
+void AsyncHttpRequest::OnWorkStart() {
+ factory_.SetProxy(proxy_);
+ if (secure_)
+ factory_.UseSSL(host_.c_str());
+
+ bool transparent_proxy = (port_ == 80) &&
+ ((proxy_.type == PROXY_HTTPS) || (proxy_.type == PROXY_UNKNOWN));
+ if (transparent_proxy) {
+ client_.set_proxy(proxy_);
+ }
+ client_.set_fail_redirect(fail_redirect_);
+ client_.set_server(SocketAddress(host_, port_));
+
+ LOG(LS_INFO) << "HttpRequest start: " << host_ + client_.request().path;
+
+ Thread::Current()->PostDelayed(timeout_, this, MSG_TIMEOUT);
+ client_.start();
+}
+
+void AsyncHttpRequest::OnWorkStop() {
+ // worker is already quitting, no need to explicitly quit
+ LOG(LS_INFO) << "HttpRequest cancelled";
+}
+
+void AsyncHttpRequest::OnComplete(HttpClient* client, HttpErrorType error) {
+ Thread::Current()->Clear(this, MSG_TIMEOUT);
+
+ set_error(error);
+ if (!error) {
+ LOG(LS_INFO) << "HttpRequest completed successfully";
+
+ std::string value;
+ if (client_.response().hasHeader(HH_LOCATION, &value)) {
+ response_redirect_ = value.c_str();
+ }
+ } else {
+ LOG(LS_INFO) << "HttpRequest completed with error: " << error;
+ }
+
+ worker()->Quit();
+}
+
+void AsyncHttpRequest::OnMessage(Message* message) {
+ if (message->message_id != MSG_TIMEOUT) {
+ SignalThread::OnMessage(message);
+ return;
+ }
+
+ LOG(LS_INFO) << "HttpRequest timed out";
+ client_.reset();
+ worker()->Quit();
+}
+
+void AsyncHttpRequest::DoWork() {
+ // Do nothing while we wait for the request to finish. We only do this so
+ // that we can be a SignalThread; in the future this class should not be
+ // a SignalThread, since it does not need to spawn a new thread.
+ Thread::Current()->ProcessMessages(kForever);
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/asynchttprequest.h b/third_party/libjingle/source/talk/base/asynchttprequest.h
new file mode 100644
index 0000000..3ab5d04
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asynchttprequest.h
@@ -0,0 +1,111 @@
+/*
+ * libjingle
+ * Copyright 2004--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_ASYNCHTTPREQUEST_H_
+#define TALK_BASE_ASYNCHTTPREQUEST_H_
+
+#include <string>
+#include "talk/base/event.h"
+#include "talk/base/httpclient.h"
+#include "talk/base/signalthread.h"
+#include "talk/base/socketpool.h"
+#include "talk/base/sslsocketfactory.h"
+
+namespace talk_base {
+
+class FirewallManager;
+
+///////////////////////////////////////////////////////////////////////////////
+// AsyncHttpRequest
+// Performs an HTTP request on a background thread. Notifies on the foreground
+// thread once the request is done (successfully or unsuccessfully).
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncHttpRequest : public SignalThread {
+ public:
+ explicit AsyncHttpRequest(const std::string &user_agent);
+
+ void set_proxy(const ProxyInfo& proxy) {
+ proxy_ = proxy;
+ }
+ void set_firewall(FirewallManager * firewall) {
+ firewall_ = firewall;
+ }
+
+ // The DNS name of the host to connect to.
+ const std::string& host() { return host_; }
+ void set_host(const std::string& host) { host_ = host; }
+
+ // The port to connect to on the target host.
+ int port() { return port_; }
+ void set_port(int port) { port_ = port; }
+
+ // Whether the request should use SSL.
+ bool secure() { return secure_; }
+ void set_secure(bool secure) { secure_ = secure; }
+
+ // Time to wait on the download, in ms.
+ int timeout() { return timeout_; }
+ void set_timeout(int timeout) { timeout_ = timeout; }
+
+ // Fail redirects to allow analysis of redirect urls, etc.
+ bool fail_redirect() const { return fail_redirect_; }
+ void set_fail_redirect(bool redirect) { fail_redirect_ = redirect; }
+
+ // Returns the redirect when redirection occurs
+ const std::string& response_redirect() { return response_redirect_; }
+
+ HttpRequestData& request() { return client_.request(); }
+ HttpResponseData& response() { return client_.response(); }
+ HttpErrorType error() { return error_; }
+
+ protected:
+ void set_error(HttpErrorType error) { error_ = error; }
+ virtual void OnWorkStart();
+ virtual void OnWorkStop();
+ void OnComplete(HttpClient* client, HttpErrorType error);
+ virtual void OnMessage(Message* message);
+ virtual void DoWork();
+
+ private:
+ ProxyInfo proxy_;
+ FirewallManager* firewall_;
+ std::string host_;
+ int port_;
+ bool secure_;
+ int timeout_;
+ bool fail_redirect_;
+ SslSocketFactory factory_;
+ ReuseSocketPool pool_;
+ HttpClient client_;
+ HttpErrorType error_;
+ std::string response_redirect_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCHTTPREQUEST_H_
diff --git a/third_party/libjingle/source/talk/base/asyncpacketsocket.cc b/third_party/libjingle/source/talk/base/asyncpacketsocket.cc
new file mode 100644
index 0000000..7628c12
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asyncpacketsocket.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.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include "talk/base/asyncpacketsocket.h"
+
+namespace talk_base {
+
+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();
+}
+
+Socket::ConnState AsyncPacketSocket::GetState() const {
+ return socket_->GetState();
+}
+
+int AsyncPacketSocket::GetOption(Socket::Option opt, int* value) {
+ return socket_->GetOption(opt, value);
+}
+
+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 talk_base
diff --git a/third_party/libjingle/source/talk/base/asyncpacketsocket.h b/third_party/libjingle/source/talk/base/asyncpacketsocket.h
new file mode 100644
index 0000000..9cb82ec
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asyncpacketsocket.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 TALK_BASE_ASYNCPACKETSOCKET_H_
+#define TALK_BASE_ASYNCPACKETSOCKET_H_
+
+#include "talk/base/asyncsocket.h"
+
+namespace talk_base {
+
+// 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:
+ explicit 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 Socket::ConnState GetState() const;
+ virtual int GetOption(Socket::Option opt, int* value);
+ 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_;
+ DISALLOW_EVIL_CONSTRUCTORS(AsyncPacketSocket);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCPACKETSOCKET_H_
diff --git a/third_party/libjingle/source/talk/base/asyncsocket.h b/third_party/libjingle/source/talk/base/asyncsocket.h
new file mode 100644
index 0000000..5cf1ada
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asyncsocket.h
@@ -0,0 +1,147 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_ASYNCSOCKET_H_
+#define TALK_BASE_ASYNCSOCKET_H_
+
+#include "talk/base/common.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socket.h"
+
+namespace talk_base {
+
+// TODO(juberti): Remove Socket and rename AsyncSocket to Socket.
+
+// Provides the ability to perform socket I/O asynchronously.
+class AsyncSocket : public Socket {
+ public:
+ virtual AsyncSocket* Accept(SocketAddress* paddr) = 0;
+
+ 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
+};
+
+class AsyncSocketAdapter : public AsyncSocket, public sigslot::has_slots<> {
+ public:
+ // The adapted socket may explicitly be NULL, and later assigned using Attach.
+ // However, subclasses which support detached mode must override any methods
+ // that will be called during the detached period (usually GetState()), to
+ // avoid dereferencing a null pointer.
+ explicit AsyncSocketAdapter(AsyncSocket* socket) : socket_(NULL) {
+ Attach(socket);
+ }
+ virtual ~AsyncSocketAdapter() {
+ delete socket_;
+ }
+ void Attach(AsyncSocket* socket) {
+ ASSERT(!socket_);
+ socket_ = socket;
+ if (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 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 AsyncSocket* 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 GetOption(Option opt, int* value) {
+ return socket_->GetOption(opt, value);
+ }
+ 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);
+ }
+
+ AsyncSocket* socket_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCSOCKET_H_
diff --git a/third_party/libjingle/source/talk/base/asynctcpsocket.cc b/third_party/libjingle/source/talk/base/asynctcpsocket.cc
new file mode 100644
index 0000000..43cef73
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asynctcpsocket.cc
@@ -0,0 +1,201 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <cstring>
+
+#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
+#include <errno.h>
+#endif // POSIX
+
+namespace talk_base {
+
+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.
+ if (!socket_->IsBlocking()) {
+ LOG_ERR(LS_ERROR) << "recvfrom";
+ }
+ 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 talk_base
diff --git a/third_party/libjingle/source/talk/base/asynctcpsocket.h b/third_party/libjingle/source/talk/base/asynctcpsocket.h
new file mode 100644
index 0000000..82e02f1
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asynctcpsocket.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 TALK_BASE_ASYNCTCPSOCKET_H_
+#define TALK_BASE_ASYNCTCPSOCKET_H_
+
+#include "talk/base/asyncpacketsocket.h"
+#include "talk/base/socketfactory.h"
+
+namespace talk_base {
+
+// 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:
+ static AsyncTCPSocket* Create(SocketFactory* factory) {
+ AsyncSocket* sock = factory->CreateAsyncSocket(SOCK_STREAM);
+ return (sock) ? new AsyncTCPSocket(sock) : NULL;
+ }
+ explicit 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:
+ 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);
+
+ char* inbuf_, * outbuf_;
+ size_t insize_, inpos_, outsize_, outpos_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCTCPSOCKET_H_
diff --git a/third_party/libjingle/source/talk/base/asyncudpsocket.cc b/third_party/libjingle/source/talk/base/asyncudpsocket.cc
new file mode 100644
index 0000000..3fae8e4
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asyncudpsocket.cc
@@ -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.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+const int BUF_SIZE = 64 * 1024;
+
+AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket)
+ : AsyncPacketSocket(socket) {
+ ASSERT(socket_ != NULL);
+ size_ = BUF_SIZE;
+ buf_ = new char[size_];
+
+ // 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) {
+ // An error here typically means we got an ICMP error in response to our
+ // send datagram, indicating the remote address was unreachable.
+ // When doing ICE, this kind of thing will often happen.
+ // TODO(juberti): Do something better like forwarding the error to the user.
+ SocketAddress local_addr = socket_->GetLocalAddress();
+ LOG(LS_INFO) << "AsyncUDPSocket[" << local_addr.ToString() << "] "
+ << "receive failed with error " << socket_->GetError();
+ return;
+ }
+
+ // TODO(juberti): 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 talk_base
diff --git a/third_party/libjingle/source/talk/base/asyncudpsocket.h b/third_party/libjingle/source/talk/base/asyncudpsocket.h
new file mode 100644
index 0000000..7af772e
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/asyncudpsocket.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 TALK_BASE_ASYNCUDPSOCKET_H_
+#define TALK_BASE_ASYNCUDPSOCKET_H_
+
+#include "talk/base/asyncpacketsocket.h"
+#include "talk/base/socketfactory.h"
+
+namespace talk_base {
+
+// 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:
+ // Creates a new socket for sending asynchronous UDP packets using an
+ // asynchronous socket from the given factory.
+ static AsyncUDPSocket* Create(SocketFactory* factory) {
+ AsyncSocket* sock = factory->CreateAsyncSocket(SOCK_DGRAM);
+ return (sock) ? new AsyncUDPSocket(sock) : NULL;
+ }
+ explicit AsyncUDPSocket(AsyncSocket* socket);
+ virtual ~AsyncUDPSocket();
+
+ private:
+ // Called when the underlying socket is ready to be read from.
+ void OnReadEvent(AsyncSocket* socket);
+
+ char* buf_;
+ size_t size_;
+};
+
+// TODO(juberti): This is now deprecated. Remove it.
+inline AsyncUDPSocket* CreateAsyncUDPSocket(SocketFactory* factory) {
+ return AsyncUDPSocket::Create(factory);
+}
+
+} // namespace talk_base
+
+#endif // TALK_BASE_ASYNCUDPSOCKET_H_
diff --git a/third_party/libjingle/source/talk/base/autodetectproxy.cc b/third_party/libjingle/source/talk/base/autodetectproxy.cc
new file mode 100644
index 0000000..2b06430
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/autodetectproxy.cc
@@ -0,0 +1,187 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/autodetectproxy.h"
+#include "talk/base/httpcommon.h"
+#include "talk/base/httpcommon-inl.h"
+#include "talk/base/proxydetect.h"
+
+namespace talk_base {
+
+enum { MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE };
+
+static const ProxyType TEST_ORDER[] = {
+ PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN
+};
+
+AutoDetectProxy::AutoDetectProxy(const std::string& user_agent)
+ : agent_(user_agent), socket_(NULL), next_(0) {
+}
+
+AutoDetectProxy::~AutoDetectProxy() {
+}
+
+void AutoDetectProxy::DoWork() {
+ // TODO(oja): Try connecting to server_url without proxy first here?
+ if (!server_url_.empty()) {
+ LOG(LS_INFO) << "GetProxySettingsForUrl(" << server_url_ << ") - start";
+ GetProxySettingsForUrl(agent_.c_str(), server_url_.c_str(), proxy_, true);
+ LOG(LS_INFO) << "GetProxySettingsForUrl - stop";
+ }
+ Url<char> url(proxy_.address.IPAsString());
+ if (url.valid()) {
+ LOG(LS_WARNING) << "AutoDetectProxy removing http prefix on proxy host";
+ proxy_.address.SetIP(url.host());
+ }
+ LOG(LS_INFO) << "AutoDetectProxy found proxy at " << proxy_.address;
+ if (proxy_.type == PROXY_UNKNOWN) {
+ LOG(LS_INFO) << "AutoDetectProxy initiating proxy classification";
+ Next();
+ // Process I/O until Stop()
+ Thread::Current()->ProcessMessages(kForever);
+ // Clean up the autodetect socket, from the thread that created it
+ delete socket_;
+ }
+ // TODO(oja): If we found a proxy, try to use it to verify that it
+ // works by sending a request to server_url. This could either be
+ // done here or by the HttpPortAllocator.
+}
+
+void AutoDetectProxy::OnMessage(Message *msg) {
+ if (MSG_TIMEOUT == msg->message_id) {
+ OnCloseEvent(socket_, ETIMEDOUT);
+ } else {
+ SignalThread::OnMessage(msg);
+ }
+}
+
+void AutoDetectProxy::Next() {
+ if (TEST_ORDER[next_] >= PROXY_UNKNOWN) {
+ Complete(PROXY_UNKNOWN);
+ return;
+ }
+
+ LOG(LS_VERBOSE) << "AutoDetectProxy connecting to "
+ << proxy_.address.ToString();
+
+ if (socket_) {
+ Thread::Current()->Clear(this, MSG_TIMEOUT);
+ socket_->Close();
+ Thread::Current()->Dispose(socket_);
+ socket_ = NULL;
+ }
+
+ socket_ = Thread::Current()->socketserver()->CreateAsyncSocket(SOCK_STREAM);
+ socket_->SignalConnectEvent.connect(this, &AutoDetectProxy::OnConnectEvent);
+ socket_->SignalReadEvent.connect(this, &AutoDetectProxy::OnReadEvent);
+ socket_->SignalCloseEvent.connect(this, &AutoDetectProxy::OnCloseEvent);
+ socket_->Connect(proxy_.address);
+
+ // Timeout after 2 seconds
+ Thread::Current()->PostDelayed(2000, this, MSG_TIMEOUT);
+}
+
+void AutoDetectProxy::Complete(ProxyType type) {
+ Thread::Current()->Clear(this, MSG_TIMEOUT);
+ socket_->Close();
+
+ proxy_.type = type;
+ LoggingSeverity sev = (proxy_.type == PROXY_UNKNOWN) ? LS_ERROR : LS_INFO;
+ LOG_V(sev) << "AutoDetectProxy detected " << proxy_.address.ToString()
+ << " as type " << proxy_.type;
+
+ Thread::Current()->Quit();
+}
+
+void AutoDetectProxy::OnConnectEvent(AsyncSocket * socket) {
+ std::string probe;
+
+ switch (TEST_ORDER[next_]) {
+ case PROXY_HTTPS:
+ probe.assign("CONNECT www.google.com:443 HTTP/1.0\r\n"
+ "User-Agent: ");
+ probe.append(agent_);
+ probe.append("\r\n"
+ "Host: www.google.com\r\n"
+ "Content-Length: 0\r\n"
+ "Proxy-Connection: Keep-Alive\r\n"
+ "\r\n");
+ break;
+ case PROXY_SOCKS5:
+ probe.assign("\005\001\000", 3);
+ break;
+ default:
+ ASSERT(false);
+ return;
+ }
+
+ LOG(LS_VERBOSE) << "AutoDetectProxy probing type " << TEST_ORDER[next_]
+ << " sending " << probe.size() << " bytes";
+ socket_->Send(probe.data(), probe.size());
+}
+
+void AutoDetectProxy::OnReadEvent(AsyncSocket * socket) {
+ char data[257];
+ int len = socket_->Recv(data, 256);
+ if (len > 0) {
+ data[len] = 0;
+ LOG(LS_VERBOSE) << "AutoDetectProxy read " << len << " bytes";
+ }
+
+ switch (TEST_ORDER[next_]) {
+ case PROXY_HTTPS:
+ if ((len >= 2) && (data[0] == '\x05')) {
+ Complete(PROXY_SOCKS5);
+ return;
+ }
+ if ((len >= 5) && (strncmp(data, "HTTP/", 5) == 0)) {
+ Complete(PROXY_HTTPS);
+ return;
+ }
+ break;
+ case PROXY_SOCKS5:
+ if ((len >= 2) && (data[0] == '\x05')) {
+ Complete(PROXY_SOCKS5);
+ return;
+ }
+ break;
+ default:
+ ASSERT(false);
+ return;
+ }
+
+ ++next_;
+ Next();
+}
+
+void AutoDetectProxy::OnCloseEvent(AsyncSocket * socket, int error) {
+ LOG(LS_VERBOSE) << "AutoDetectProxy closed with error: " << error;
+ ++next_;
+ Next();
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/autodetectproxy.h b/third_party/libjingle/source/talk/base/autodetectproxy.h
new file mode 100644
index 0000000..6bb2a4b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/autodetectproxy.h
@@ -0,0 +1,90 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_AUTODETECTPROXY_H_
+#define TALK_BASE_AUTODETECTPROXY_H_
+
+#include <string>
+
+#include "talk/base/cryptstring.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/signalthread.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// AutoDetectProxy
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncSocket;
+
+class AutoDetectProxy : public SignalThread {
+ public:
+ explicit AutoDetectProxy(const std::string& user_agent);
+
+ const ProxyInfo& proxy() const { return proxy_; }
+
+ void set_server_url(const std::string& url) {
+ server_url_ = url;
+ }
+ void set_proxy(const SocketAddress& proxy) {
+ proxy_.type = PROXY_UNKNOWN;
+ proxy_.address = proxy;
+ }
+ void set_auth_info(bool use_auth, const std::string& username,
+ const CryptString& password) {
+ if (use_auth) {
+ proxy_.username = username;
+ proxy_.password = password;
+ }
+ }
+
+ protected:
+ virtual ~AutoDetectProxy();
+
+ // SignalThread Interface
+ virtual void DoWork();
+ virtual void OnMessage(Message *msg);
+
+ void Next();
+ void Complete(ProxyType type);
+
+ void OnConnectEvent(AsyncSocket * socket);
+ void OnReadEvent(AsyncSocket * socket);
+ void OnCloseEvent(AsyncSocket * socket, int error);
+
+ private:
+ std::string agent_;
+ std::string server_url_;
+ ProxyInfo proxy_;
+ AsyncSocket* socket_;
+ int next_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_AUTODETECTPROXY_H_
diff --git a/third_party/libjingle/source/talk/base/base64.cc b/third_party/libjingle/source/talk/base/base64.cc
new file mode 100644
index 0000000..363c378
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/base64.cc
@@ -0,0 +1,243 @@
+
+//*********************************************************************
+//* 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"
+#include "talk/base/common.h"
+
+using std::string;
+using std::vector;
+
+namespace talk_base {
+
+static const char kPad = '=';
+static const unsigned char pd = 0xFD; // Padding
+static const unsigned char sp = 0xFE; // Whitespace
+static const unsigned char il = 0xFF; // Illegal base64 character
+
+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 == /
+
+const unsigned char Base64::DecodeTable[] = {
+// 0 1 2 3 4 5 6 7 8 9
+ il,il,il,il,il,il,il,il,il,sp, // 0 - 9
+ sp,sp,sp,sp,il,il,il,il,il,il, // 10 - 19
+ il,il,il,il,il,il,il,il,il,il, // 20 - 29
+ il,il,sp,il,il,il,il,il,il,il, // 30 - 39
+ il,il,il,62,il,il,il,63,52,53, // 40 - 49
+ 54,55,56,57,58,59,60,61,il,il, // 50 - 59
+ il,pd,il,il,il, 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,il,il,il,il,il,il,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,il,il,il,il,il,il,il, // 120 - 129
+ il,il,il,il,il,il,il,il,il,il, // 130 - 139
+ il,il,il,il,il,il,il,il,il,il, // 140 - 149
+ il,il,il,il,il,il,il,il,il,il, // 150 - 159
+ il,il,il,il,il,il,il,il,il,il, // 160 - 169
+ il,il,il,il,il,il,il,il,il,il, // 170 - 179
+ il,il,il,il,il,il,il,il,il,il, // 180 - 189
+ il,il,il,il,il,il,il,il,il,il, // 190 - 199
+ il,il,il,il,il,il,il,il,il,il, // 200 - 209
+ il,il,il,il,il,il,il,il,il,il, // 210 - 219
+ il,il,il,il,il,il,il,il,il,il, // 220 - 229
+ il,il,il,il,il,il,il,il,il,il, // 230 - 239
+ il,il,il,il,il,il,il,il,il,il, // 240 - 249
+ il,il,il,il,il,il // 250 - 255
+};
+
+bool Base64::IsBase64Char(char ch) {
+ return (('A' <= ch) && (ch <= 'Z')) ||
+ (('a' <= ch) && (ch <= 'z')) ||
+ (('0' <= ch) && (ch <= '9')) ||
+ (ch == '+') || (ch == '/');
+}
+
+bool Base64::IsBase64Encoded(const std::string& str) {
+ for (size_t i = 0; i < str.size(); ++i) {
+ if (!IsBase64Char(str.at(i)))
+ return false;
+ }
+ return true;
+}
+
+void Base64::EncodeFromArray(const void* data, size_t len, string* result) {
+ ASSERT(NULL != result);
+ result->clear();
+ result->reserve(((len + 2) / 3) * 4);
+ const unsigned char* byte_data = static_cast<const unsigned char*>(data);
+
+ unsigned char c;
+ size_t i = 0;
+ while (i < len) {
+ c = (byte_data[i] >> 2) & 0x3f;
+ result->push_back(Base64Table[c]);
+
+ c = (byte_data[i] << 4) & 0x3f;
+ if (++i < len) {
+ c |= (byte_data[i] >> 4) & 0x0f;
+ }
+ result->push_back(Base64Table[c]);
+
+ if (i < len) {
+ c = (byte_data[i] << 2) & 0x3f;
+ if (++i < len) {
+ c |= (byte_data[i] >> 6) & 0x03;
+ }
+ result->push_back(Base64Table[c]);
+ } else {
+ result->push_back(kPad);
+ }
+
+ if (i < len) {
+ c = byte_data[i] & 0x3f;
+ result->push_back(Base64Table[c]);
+ ++i;
+ } else {
+ result->push_back(kPad);
+ }
+ }
+}
+
+size_t Base64::GetNextQuantum(DecodeFlags parse_flags, bool illegal_pads,
+ const char* data, size_t len, size_t* dpos,
+ unsigned char qbuf[4], bool* padded)
+{
+ size_t byte_len = 0, pad_len = 0, pad_start = 0;
+ for (; (byte_len < 4) && (*dpos < len); ++*dpos) {
+ qbuf[byte_len] = DecodeTable[static_cast<unsigned char>(data[*dpos])];
+ if ((il == qbuf[byte_len]) || (illegal_pads && (pd == qbuf[byte_len]))) {
+ if (parse_flags != DO_PARSE_ANY)
+ break;
+ // Ignore illegal characters
+ } else if (sp == qbuf[byte_len]) {
+ if (parse_flags == DO_PARSE_STRICT)
+ break;
+ // Ignore spaces
+ } else if (pd == qbuf[byte_len]) {
+ if (byte_len < 2) {
+ if (parse_flags != DO_PARSE_ANY)
+ break;
+ // Ignore unexpected padding
+ } else if (byte_len + pad_len >= 4) {
+ if (parse_flags != DO_PARSE_ANY)
+ break;
+ // Ignore extra pads
+ } else {
+ if (1 == ++pad_len) {
+ pad_start = *dpos;
+ }
+ }
+ } else {
+ if (pad_len > 0) {
+ if (parse_flags != DO_PARSE_ANY)
+ break;
+ // Ignore pads which are followed by data
+ pad_len = 0;
+ }
+ ++byte_len;
+ }
+ }
+ for (size_t i = byte_len; i < 4; ++i) {
+ qbuf[i] = 0;
+ }
+ if (4 == byte_len + pad_len) {
+ *padded = true;
+ } else {
+ *padded = false;
+ if (pad_len) {
+ // Roll back illegal padding
+ *dpos = pad_start;
+ }
+ }
+ return byte_len;
+}
+
+bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags,
+ string* result, size_t* data_used) {
+ return DecodeFromArrayTemplate<string>(data, len, flags, result, data_used);
+}
+
+bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags,
+ vector<char>* result, size_t* data_used) {
+ return DecodeFromArrayTemplate<vector<char> >(data, len, flags, result,
+ data_used);
+}
+
+template<typename T>
+bool Base64::DecodeFromArrayTemplate(const char* data, size_t len,
+ DecodeFlags flags, T* result,
+ size_t* data_used)
+{
+ ASSERT(NULL != result);
+ ASSERT(flags <= (DO_PARSE_MASK | DO_PAD_MASK | DO_TERM_MASK));
+
+ const DecodeFlags parse_flags = flags & DO_PARSE_MASK;
+ const DecodeFlags pad_flags = flags & DO_PAD_MASK;
+ const DecodeFlags term_flags = flags & DO_TERM_MASK;
+ ASSERT(0 != parse_flags);
+ ASSERT(0 != pad_flags);
+ ASSERT(0 != term_flags);
+
+ result->clear();
+ result->reserve(len);
+
+ size_t dpos = 0;
+ bool success = true, padded;
+ unsigned char c, qbuf[4];
+ while (dpos < len) {
+ size_t qlen = GetNextQuantum(parse_flags, (DO_PAD_NO == pad_flags),
+ data, len, &dpos, qbuf, &padded);
+ c = (qbuf[0] << 2) | ((qbuf[1] >> 4) & 0x3);
+ if (qlen >= 2) {
+ result->push_back(c);
+ c = ((qbuf[1] << 4) & 0xf0) | ((qbuf[2] >> 2) & 0xf);
+ if (qlen >= 3) {
+ result->push_back(c);
+ c = ((qbuf[2] << 6) & 0xc0) | qbuf[3];
+ if (qlen >= 4) {
+ result->push_back(c);
+ c = 0;
+ }
+ }
+ }
+ if (qlen < 4) {
+ if ((DO_TERM_ANY != term_flags) && (0 != c)) {
+ success = false; // unused bits
+ }
+ if ((DO_PAD_YES == pad_flags) && !padded) {
+ success = false; // expected padding
+ }
+ break;
+ }
+ }
+ if ((DO_TERM_BUFFER == term_flags) && (dpos != len)) {
+ success = false; // unused chars
+ }
+ if (data_used) {
+ *data_used = dpos;
+ }
+ return success;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/base64.h b/third_party/libjingle/source/talk/base/base64.h
new file mode 100644
index 0000000..73904dc
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/base64.h
@@ -0,0 +1,96 @@
+
+//*********************************************************************
+//* 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 TALK_BASE_BASE64_H__
+#define TALK_BASE_BASE64_H__
+
+#include <string>
+#include <vector>
+
+namespace talk_base {
+
+class Base64
+{
+public:
+ enum DecodeOption {
+ DO_PARSE_STRICT = 1, // Parse only base64 characters
+ DO_PARSE_WHITE = 2, // Parse only base64 and whitespace characters
+ DO_PARSE_ANY = 3, // Parse all characters
+ DO_PARSE_MASK = 3,
+
+ DO_PAD_YES = 4, // Padding is required
+ DO_PAD_ANY = 8, // Padding is optional
+ DO_PAD_NO = 12, // Padding is disallowed
+ DO_PAD_MASK = 12,
+
+ DO_TERM_BUFFER = 16, // Must termiante at end of buffer
+ DO_TERM_CHAR = 32, // May terminate at any character boundary
+ DO_TERM_ANY = 48, // May terminate at a sub-character bit offset
+ DO_TERM_MASK = 48,
+
+ // Strictest interpretation
+ DO_STRICT = DO_PARSE_STRICT | DO_PAD_YES | DO_TERM_BUFFER,
+
+ DO_LAX = DO_PARSE_ANY | DO_PAD_ANY | DO_TERM_CHAR,
+ };
+ typedef int DecodeFlags;
+
+ static bool IsBase64Char(char ch);
+
+ // Determines whether the given string consists entirely of valid base64
+ // encoded characters.
+ static bool IsBase64Encoded(const std::string& str);
+
+ static void EncodeFromArray(const void* data, size_t len,
+ std::string* result);
+ static bool DecodeFromArray(const char* data, size_t len, DecodeFlags flags,
+ std::string* result, size_t* data_used);
+ static bool DecodeFromArray(const char* data, size_t len, DecodeFlags flags,
+ std::vector<char>* result, size_t* data_used);
+
+ // Convenience Methods
+ static inline std::string Encode(const std::string& data) {
+ std::string result;
+ EncodeFromArray(data.data(), data.size(), &result);
+ return result;
+ }
+ static inline std::string Decode(const std::string& data, DecodeFlags flags) {
+ std::string result;
+ DecodeFromArray(data.data(), data.size(), flags, &result, NULL);
+ return result;
+ }
+ static inline bool Decode(const std::string& data, DecodeFlags flags,
+ std::string* result, size_t* data_used)
+ {
+ return DecodeFromArray(data.data(), data.size(), flags, result, data_used);
+ }
+ static inline bool Decode(const std::string& data, DecodeFlags flags,
+ std::vector<char>* result, size_t* data_used)
+ {
+ return DecodeFromArray(data.data(), data.size(), flags, result, data_used);
+ }
+
+private:
+ static const std::string Base64Table;
+ static const unsigned char DecodeTable[];
+
+ static size_t GetNextQuantum(DecodeFlags parse_flags, bool illegal_pads,
+ const char* data, size_t len, size_t* dpos,
+ unsigned char qbuf[4], bool* padded);
+ template<typename T>
+ static bool DecodeFromArrayTemplate(const char* data, size_t len,
+ DecodeFlags flags, T* result,
+ size_t* data_used);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_BASE64_H__
diff --git a/third_party/libjingle/source/talk/base/basicdefs.h b/third_party/libjingle/source/talk/base/basicdefs.h
new file mode 100644
index 0000000..0e58bf6
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/basicdefs.h
@@ -0,0 +1,37 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_BASICDEFS_H__
+#define TALK_BASE_BASICDEFS_H__
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0]))))
+
+#endif // TAKL_BASE_BASICDEFS_H__
diff --git a/third_party/libjingle/source/talk/base/basictypes.h b/third_party/libjingle/source/talk/base/basictypes.h
new file mode 100644
index 0000000..e98898d
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/basictypes.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 TALK_BASE_BASICTYPES_H__
+#define TALK_BASE_BASICTYPES_H__
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "talk/base/constructormagic.h"
+
+#ifndef INT_TYPES_DEFINED
+#define INT_TYPES_DEFINED
+#ifdef COMPILER_MSVC
+typedef __int64 int64;
+#else
+typedef long long int64;
+#endif /* COMPILER_MSVC */
+typedef int int32;
+typedef short int16;
+typedef char int8;
+
+#ifdef COMPILER_MSVC
+typedef unsigned __int64 uint64;
+typedef __int64 int64;
+#define INT64_C(x) x ## I64
+#define UINT64_C(x) x ## UI64
+#define INT64_F "I64"
+#else
+typedef unsigned long long uint64;
+typedef long long int64;
+#define INT64_C(x) x ## LL
+#define UINT64_C(x) x ## ULL
+#define INT64_F "ll"
+#endif /* COMPILER_MSVC */
+typedef unsigned int uint32;
+typedef unsigned short uint16;
+typedef unsigned char uint8;
+#endif // INT_TYPES_DEFINED
+
+#ifdef WIN32
+typedef int socklen_t;
+#endif
+
+namespace talk_base {
+ 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; }
+
+ // For wait functions that take a number of milliseconds, kForever indicates
+ // unlimited time.
+ const int kForever = -1;
+}
+
+#ifdef WIN32
+#define alignof(t) __alignof(t)
+#else // !WIN32
+#define alignof(t) __alignof__(t)
+#endif // !WIN32
+#define IS_ALIGNED(p, t) (0==(reinterpret_cast<uintptr_t>(p) & (alignof(t)-1)))
+
+#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
+
+#if defined(__GNUC__)
+#define GCC_ATTR(x) __attribute__ ((x))
+#else // !__GNUC__
+#define GCC_ATTR(x)
+#endif // !__GNUC__
+
+#endif // TALK_BASE_BASICTYPES_H__
diff --git a/third_party/libjingle/source/talk/base/bytebuffer.cc b/third_party/libjingle/source/talk/base/bytebuffer.cc
new file mode 100644
index 0000000..1eb260d
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/bytebuffer.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/bytebuffer.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/byteorder.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::memcpy;
+}
+#endif
+
+namespace talk_base {
+
+static 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::ReadUInt24(uint32& val) {
+ uint32 v = 0;
+ if (!ReadBytes(reinterpret_cast<char*>(&v) + 1, 3)) {
+ return false;
+ } else {
+ val = NetworkToHost32(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::WriteUInt24(uint32 val) {
+ uint32 v = HostToNetwork32(val);
+ WriteBytes(reinterpret_cast<const char*>(&v) + 1, 3);
+}
+
+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::Consume(size_t size) {
+ if (size > Length())
+ return;
+
+ start_ += size;
+}
+
+void ByteBuffer::Shift(size_t size) {
+ if (size > Length())
+ return;
+
+ end_ = Length() - size;
+ memmove(bytes_, bytes_ + start_ + size, end_);
+ start_ = 0;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/bytebuffer.h b/third_party/libjingle/source/talk/base/bytebuffer.h
new file mode 100644
index 0000000..d5cfb00
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/bytebuffer.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 TALK_BASE_BYTEBUFFER_H_
+#define TALK_BASE_BYTEBUFFER_H_
+
+#include <string>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/constructormagic.h"
+
+namespace talk_base {
+
+class ByteBuffer {
+ public:
+ ByteBuffer();
+ ByteBuffer(const char* bytes, size_t len);
+ explicit ByteBuffer(const char* bytes); // uses strlen
+ ~ByteBuffer();
+
+ const char* Data() const { return bytes_ + start_; }
+ size_t Length() const { return end_ - start_; }
+ size_t Capacity() const { return size_ - start_; }
+
+ bool ReadUInt8(uint8& val);
+ bool ReadUInt16(uint16& val);
+ bool ReadUInt24(uint32& 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 WriteUInt24(uint32 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 Consume(size_t size);
+ void Shift(size_t size);
+
+ private:
+ char* bytes_;
+ size_t size_;
+ size_t start_;
+ size_t end_;
+
+ // There are sensible ways to define these, but they aren't needed in our code
+ // base.
+ DISALLOW_COPY_AND_ASSIGN(ByteBuffer);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_BYTEBUFFER_H_
diff --git a/third_party/libjingle/source/talk/base/byteorder.h b/third_party/libjingle/source/talk/base/byteorder.h
new file mode 100644
index 0000000..4153f5d
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/byteorder.h
@@ -0,0 +1,156 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_BYTEORDER_H__
+#define TALK_BASE_BYTEORDER_H__
+
+#ifdef POSIX
+#include <arpa/inet.h>
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+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);
+}
+
+// Reading and writing of little and big-endian numbers from memory
+// TODO: Add HostEndian #defines (HE)
+// TODO: Consider NetworkEndian as synonym for BigEndian, for clarity in use.
+// TODO: Consider creating optimized versions, such as direct read/writes of
+// integers in host-endian format, when the platform supports it.
+
+inline void Set8(void* memory, size_t offset, uint8 v) {
+ static_cast<uint8*>(memory)[offset] = v;
+}
+inline uint8 Get8(const void* memory, size_t offset) {
+ return static_cast<const uint8*>(memory)[offset];
+}
+
+inline void SetBE16(void* memory, uint16 v) {
+ Set8(memory, 0, static_cast<uint8>(v >> 8));
+ Set8(memory, 1, static_cast<uint8>(v >> 0));
+}
+inline void SetBE32(void* memory, uint32 v) {
+ Set8(memory, 0, static_cast<uint8>(v >> 24));
+ Set8(memory, 1, static_cast<uint8>(v >> 16));
+ Set8(memory, 2, static_cast<uint8>(v >> 8));
+ Set8(memory, 3, static_cast<uint8>(v >> 0));
+}
+inline void SetBE64(void* memory, uint64 v) {
+ Set8(memory, 0, static_cast<uint8>(v >> 56));
+ Set8(memory, 1, static_cast<uint8>(v >> 48));
+ Set8(memory, 2, static_cast<uint8>(v >> 40));
+ Set8(memory, 3, static_cast<uint8>(v >> 32));
+ Set8(memory, 4, static_cast<uint8>(v >> 24));
+ Set8(memory, 5, static_cast<uint8>(v >> 16));
+ Set8(memory, 6, static_cast<uint8>(v >> 8));
+ Set8(memory, 7, static_cast<uint8>(v >> 0));
+}
+inline uint16 GetBE16(const void* memory) {
+ return (static_cast<uint16>(Get8(memory, 0)) << 8)
+ | (static_cast<uint16>(Get8(memory, 1)) << 0);
+}
+inline uint32 GetBE32(const void* memory) {
+ return (static_cast<uint32>(Get8(memory, 0)) << 24)
+ | (static_cast<uint32>(Get8(memory, 1)) << 16)
+ | (static_cast<uint32>(Get8(memory, 2)) << 8)
+ | (static_cast<uint32>(Get8(memory, 3)) << 0);
+}
+inline uint64 GetBE64(const void* memory) {
+ return (static_cast<uint64>(Get8(memory, 0)) << 56)
+ | (static_cast<uint64>(Get8(memory, 1)) << 48)
+ | (static_cast<uint64>(Get8(memory, 2)) << 40)
+ | (static_cast<uint64>(Get8(memory, 3)) << 32)
+ | (static_cast<uint64>(Get8(memory, 4)) << 24)
+ | (static_cast<uint64>(Get8(memory, 5)) << 16)
+ | (static_cast<uint64>(Get8(memory, 6)) << 8)
+ | (static_cast<uint64>(Get8(memory, 7)) << 0);
+}
+
+inline void SetLE16(void* memory, uint16 v) {
+ Set8(memory, 1, static_cast<uint8>(v >> 8));
+ Set8(memory, 0, static_cast<uint8>(v >> 0));
+}
+inline void SetLE32(void* memory, uint32 v) {
+ Set8(memory, 3, static_cast<uint8>(v >> 24));
+ Set8(memory, 2, static_cast<uint8>(v >> 16));
+ Set8(memory, 1, static_cast<uint8>(v >> 8));
+ Set8(memory, 0, static_cast<uint8>(v >> 0));
+}
+inline void SetLE64(void* memory, uint64 v) {
+ Set8(memory, 7, static_cast<uint8>(v >> 56));
+ Set8(memory, 6, static_cast<uint8>(v >> 48));
+ Set8(memory, 5, static_cast<uint8>(v >> 40));
+ Set8(memory, 4, static_cast<uint8>(v >> 32));
+ Set8(memory, 3, static_cast<uint8>(v >> 24));
+ Set8(memory, 2, static_cast<uint8>(v >> 16));
+ Set8(memory, 1, static_cast<uint8>(v >> 8));
+ Set8(memory, 0, static_cast<uint8>(v >> 0));
+}
+inline uint16 GetLE16(const void* memory) {
+ return (static_cast<uint16>(Get8(memory, 1)) << 8)
+ | (static_cast<uint16>(Get8(memory, 0)) << 0);
+}
+inline uint32 GetLE32(const void* memory) {
+ return (static_cast<uint32>(Get8(memory, 3)) << 24)
+ | (static_cast<uint32>(Get8(memory, 2)) << 16)
+ | (static_cast<uint32>(Get8(memory, 1)) << 8)
+ | (static_cast<uint32>(Get8(memory, 0)) << 0);
+}
+inline uint64 GetLE64(const void* memory) {
+ return (static_cast<uint64>(Get8(memory, 7)) << 56)
+ | (static_cast<uint64>(Get8(memory, 6)) << 48)
+ | (static_cast<uint64>(Get8(memory, 5)) << 40)
+ | (static_cast<uint64>(Get8(memory, 4)) << 32)
+ | (static_cast<uint64>(Get8(memory, 3)) << 24)
+ | (static_cast<uint64>(Get8(memory, 2)) << 16)
+ | (static_cast<uint64>(Get8(memory, 1)) << 8)
+ | (static_cast<uint64>(Get8(memory, 0)) << 0);
+}
+
+} // namespace talk_base
+
+#endif // TALK_BASE_BYTEORDER_H__
diff --git a/third_party/libjingle/source/talk/base/checks.cc b/third_party/libjingle/source/talk/base/checks.cc
new file mode 100644
index 0000000..5466783
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/checks.cc
@@ -0,0 +1,47 @@
+/*
+ * libjingle
+ * Copyright 2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "talk/base/checks.h"
+#include "talk/base/logging.h"
+
+void Fatal(const char* file, int line, const char* format, ...) {
+ char msg[256];
+
+ va_list arguments;
+ va_start(arguments, format);
+ vsnprintf(msg, sizeof(msg), format, arguments);
+ va_end(arguments);
+
+ LOG(LS_ERROR) << "\n\n#\n# Fatal error in " << file
+ << ", line " << line << "\n#" << msg
+ << "\n#\n";
+ abort();
+}
diff --git a/third_party/libjingle/source/talk/base/checks.h b/third_party/libjingle/source/talk/base/checks.h
new file mode 100644
index 0000000..12adc25
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/checks.h
@@ -0,0 +1,44 @@
+/*
+ * libjingle
+ * Copyright 2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This module contains some basic debugging facilities.
+
+
+#ifndef SHARED_COMMANDLINEFLAGS_CHECKS_H__
+#define SHARED_COMMANDLINEFLAGS_CHECKS_H__
+
+#include <string.h>
+
+// Prints an error message to stderr and aborts execution.
+void Fatal(const char* file, int line, const char* format, ...);
+
+
+// The UNREACHABLE macro is very useful during development.
+#define UNREACHABLE() \
+ Fatal(__FILE__, __LINE__, "unreachable code")
+
+#endif // SHARED_COMMANDLINEFLAGS_CHECKS_H__
diff --git a/third_party/libjingle/source/talk/base/common.cc b/third_party/libjingle/source/talk/base/common.cc
new file mode 100644
index 0000000..59e73d5
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/common.cc
@@ -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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#if WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif // WIN32
+
+#if OSX
+#include <CoreServices/CoreServices.h>
+#endif // OSX
+
+#include <algorithm>
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+
+//////////////////////////////////////////////////////////////////////
+// Assertions
+//////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+void Break() {
+#if WIN32
+ ::DebugBreak();
+#elif OSX // !WIN32
+ ::Debugger();
+#else // !OSX && !WIN32
+#if _DEBUG_HAVE_BACKTRACE
+ OutputTrace();
+#endif
+ abort();
+#endif // !OSX && !WIN32
+}
+
+void LogAssert(const char * function, const char * file, int line,
+ const char * expression) {
+ // TODO - if we put hooks in here, we can do a lot fancier logging
+ LOG(LS_ERROR) << file << "(" << line << ")" << ": ASSERT FAILED: "
+ << expression << " @ " << function;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/common.h b/third_party/libjingle/source/talk/base/common.h
new file mode 100644
index 0000000..e8dae87
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/common.h
@@ -0,0 +1,131 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_COMMON_H__
+#define TALK_BASE_COMMON_H__
+
+#include "talk/base/constructormagic.h"
+
+#if defined(_MSC_VER)
+// warning C4355: 'this' : used in base member initializer list
+#pragma warning(disable:4355)
+#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
+
+#ifndef WIN32
+#define strnicmp(x,y,n) strncasecmp(x,y,n)
+#define stricmp(x,y) strcasecmp(x,y)
+
+// TODO(sergeyu): Remove this. std::max should be used everywhere in the code.
+// NOMINMAX must be defined where we include <windows.h>.
+#define stdmax(x,y) std::max(x,y)
+#else
+#define stdmax(x,y) talk_base::_max(x,y)
+#endif
+
+
+#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0]))))
+
+/////////////////////////////////////////////////////////////////////////////
+// Assertions
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef ENABLE_DEBUG
+#define ENABLE_DEBUG _DEBUG
+#endif // !defined(ENABLE_DEBUG)
+
+#if ENABLE_DEBUG
+
+namespace talk_base {
+
+// 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 bool Assert(bool result, const char * function, const char * file,
+ int line, const char * expression) {
+ if (!result) {
+ LogAssert(function, file, line, expression);
+ Break();
+ return false;
+ }
+ return true;
+}
+
+} // namespace talk_base
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#define __FUNCTION__ ""
+#endif
+
+#ifndef ASSERT
+#define ASSERT(x) (void)talk_base::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x)
+#endif
+
+#ifndef VERIFY
+#define VERIFY(x) talk_base::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x)
+#endif
+
+#else // !ENABLE_DEBUG
+
+namespace talk_base {
+
+inline bool ImplicitCastToBool(bool result) { return result; }
+
+} // namespace talk_base
+
+#ifndef ASSERT
+#define ASSERT(x) (void)0
+#endif
+
+#ifndef VERIFY
+#define VERIFY(x) talk_base::ImplicitCastToBool(x)
+#endif
+
+#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
+
+#endif // TALK_BASE_COMMON_H__
diff --git a/third_party/libjingle/source/talk/base/constructormagic.h b/third_party/libjingle/source/talk/base/constructormagic.h
new file mode 100644
index 0000000..8b1f7ff
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/constructormagic.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_BASE_CONSTRUCTORMAGIC_H_
+#define TALK_BASE_CONSTRUCTORMAGIC_H_
+
+#define DISALLOW_ASSIGN(TypeName) \
+ void operator=(const TypeName&)
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ DISALLOW_ASSIGN(TypeName)
+
+// Alternative, less-accurate legacy name.
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
+ DISALLOW_COPY_AND_ASSIGN(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)
+
+
+#endif // TALK_BASE_CONSTRUCTORMAGIC_H_
diff --git a/third_party/libjingle/source/talk/base/criticalsection.h b/third_party/libjingle/source/talk/base/criticalsection.h
new file mode 100644
index 0000000..ee3db1a
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/criticalsection.h
@@ -0,0 +1,131 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_CRITICALSECTION_H__
+#define TALK_BASE_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 talk_base {
+
+#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_init(&mutex_attribute);
+ pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutex_, &mutex_attribute);
+ pthread_mutexattr_destroy(&mutex_attribute);
+ TRACK_OWNER(thread_ = 0);
+ }
+ ~CriticalSection() {
+ pthread_mutex_destroy(&mutex_);
+ }
+ void Enter() {
+ pthread_mutex_lock(&mutex_);
+ TRACK_OWNER(thread_ = pthread_self());
+ }
+ void Leave() {
+ TRACK_OWNER(thread_ = 0);
+ pthread_mutex_unlock(&mutex_);
+ }
+
+#if CS_TRACK_OWNER
+ bool CurrentThreadIsOwner() const { return pthread_equal(thread_, pthread_self()); }
+#endif // CS_TRACK_OWNER
+
+private:
+ pthread_mutex_t mutex_;
+ TRACK_OWNER(pthread_t thread_);
+};
+#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 talk_base
+
+#endif // TALK_BASE_CRITICALSECTION_H__
diff --git a/third_party/libjingle/source/talk/base/cryptstring.h b/third_party/libjingle/source/talk/base/cryptstring.h
new file mode 100644
index 0000000..eb39be2
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/cryptstring.h
@@ -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.
+ */
+
+#ifndef _TALK_BASE_CRYPTSTRING_H_
+#define _TALK_BASE_CRYPTSTRING_H_
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include "talk/base/linked_ptr.h"
+#include "talk/base/scoped_ptr.h"
+
+namespace talk_base {
+
+class CryptStringImpl {
+public:
+ virtual ~CryptStringImpl() {}
+ virtual size_t GetLength() const = 0;
+ virtual void CopyTo(char * dest, bool nullterminate) const = 0;
+ virtual std::string UrlEncode() const = 0;
+ virtual CryptStringImpl * Copy() const = 0;
+ virtual void CopyRawTo(std::vector<unsigned char> * dest) const = 0;
+};
+
+class EmptyCryptStringImpl : public CryptStringImpl {
+public:
+ virtual ~EmptyCryptStringImpl() {}
+ 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 CryptStringImpl * Copy() const { return new EmptyCryptStringImpl(); }
+ virtual void CopyRawTo(std::vector<unsigned char> * dest) const {
+ dest->clear();
+ }
+};
+
+class CryptString {
+public:
+ CryptString() : impl_(new EmptyCryptStringImpl()) {}
+ size_t GetLength() const { return impl_->GetLength(); }
+ void CopyTo(char * dest, bool nullterminate) const { impl_->CopyTo(dest, nullterminate); }
+ CryptString(const CryptString & other) : impl_(other.impl_->Copy()) {}
+ explicit CryptString(const CryptStringImpl & impl) : impl_(impl.Copy()) {}
+ CryptString & operator=(const CryptString & other) {
+ if (this != &other) {
+ impl_.reset(other.impl_->Copy());
+ }
+ return *this;
+ }
+ void Clear() { impl_.reset(new EmptyCryptStringImpl()); }
+ std::string UrlEncode() const { return impl_->UrlEncode(); }
+ void CopyRawTo(std::vector<unsigned char> * dest) const {
+ return impl_->CopyRawTo(dest);
+ }
+
+private:
+ scoped_ptr<const CryptStringImpl> impl_;
+};
+
+
+// Used for constructing strings where a password is involved and we
+// need to ensure that we zero memory afterwards
+class FormatCryptString {
+public:
+ FormatCryptString() {
+ 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 CryptString * 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;
+ }
+ }
+
+ ~FormatCryptString() {
+ 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_;
+};
+
+class InsecureCryptStringImpl : public CryptStringImpl {
+ public:
+ std::string& password() { return password_; }
+ const std::string& password() const { return password_; }
+
+ virtual ~InsecureCryptStringImpl() {}
+ virtual size_t GetLength() const { return password_.size(); }
+ virtual void CopyTo(char * dest, bool nullterminate) const {
+ memcpy(dest, password_.data(), password_.size());
+ if (nullterminate) dest[password_.size()] = 0;
+ }
+ virtual std::string UrlEncode() const { return password_; }
+ virtual CryptStringImpl * Copy() const {
+ InsecureCryptStringImpl * copy = new InsecureCryptStringImpl;
+ copy->password() = password_;
+ return copy;
+ }
+ virtual void CopyRawTo(std::vector<unsigned char> * dest) const {
+ dest->resize(password_.size());
+ memcpy(&dest->front(), password_.data(), password_.size());
+ }
+ private:
+ std::string password_;
+};
+
+}
+
+#endif // _TALK_BASE_CRYPTSTRING_H_
diff --git a/third_party/libjingle/source/talk/base/diskcache.cc b/third_party/libjingle/source/talk/base/diskcache.cc
new file mode 100644
index 0000000..4e70543
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/diskcache.cc
@@ -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.
+ */
+
+#include <time.h>
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+#include "talk/base/common.h"
+#include "talk/base/diskcache.h"
+#include "talk/base/fileutils.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+
+#ifdef _DEBUG
+#define TRANSPARENT_CACHE_NAMES 1
+#else // !_DEBUG
+#define TRANSPARENT_CACHE_NAMES 0
+#endif // !_DEBUG
+
+namespace talk_base {
+
+class DiskCache;
+
+///////////////////////////////////////////////////////////////////////////////
+// DiskCacheAdapter
+///////////////////////////////////////////////////////////////////////////////
+
+class DiskCacheAdapter : public StreamAdapterInterface {
+public:
+ DiskCacheAdapter(const DiskCache* cache, const std::string& id, size_t index,
+ StreamInterface* stream)
+ : StreamAdapterInterface(stream), cache_(cache), id_(id), index_(index)
+ { }
+ virtual ~DiskCacheAdapter() {
+ Close();
+ cache_->ReleaseResource(id_, index_);
+ }
+
+private:
+ const DiskCache* cache_;
+ std::string id_;
+ size_t index_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// DiskCache
+///////////////////////////////////////////////////////////////////////////////
+
+DiskCache::DiskCache() : max_cache_(0), total_size_(0), total_accessors_(0) {
+}
+
+DiskCache::~DiskCache() {
+ ASSERT(0 == total_accessors_);
+}
+
+bool DiskCache::Initialize(const std::string& folder, size_t size) {
+ if (!folder_.empty() || !Filesystem::CreateFolder(folder))
+ return false;
+
+ folder_ = folder;
+ max_cache_ = size;
+ ASSERT(0 == total_size_);
+
+ if (!InitializeEntries())
+ return false;
+
+ return CheckLimit();
+}
+
+bool DiskCache::Purge() {
+ if (folder_.empty())
+ return false;
+
+ if (total_accessors_ > 0) {
+ LOG_F(LS_WARNING) << "Cache files open";
+ return false;
+ }
+
+ if (!PurgeFiles())
+ return false;
+
+ map_.clear();
+ return true;
+}
+
+bool DiskCache::LockResource(const std::string& id) {
+ Entry* entry = GetOrCreateEntry(id, true);
+ if (LS_LOCKED == entry->lock_state)
+ return false;
+ if ((LS_UNLOCKED == entry->lock_state) && (entry->accessors > 0))
+ return false;
+ if ((total_size_ > max_cache_) && !CheckLimit()) {
+ LOG_F(LS_WARNING) << "Cache overfull";
+ return false;
+ }
+ entry->lock_state = LS_LOCKED;
+ return true;
+}
+
+StreamInterface* DiskCache::WriteResource(const std::string& id, size_t index) {
+ Entry* entry = GetOrCreateEntry(id, false);
+ if (LS_LOCKED != entry->lock_state)
+ return NULL;
+
+ size_t previous_size = 0;
+ std::string filename(IdToFilename(id, index));
+ FileStream::GetSize(filename, &previous_size);
+ ASSERT(previous_size <= entry->size);
+ if (previous_size > entry->size) {
+ previous_size = entry->size;
+ }
+
+ scoped_ptr<FileStream> file(new FileStream);
+ if (!file->Open(filename, "wb")) {
+ LOG_F(LS_ERROR) << "Couldn't create cache file";
+ return NULL;
+ }
+
+ entry->streams = stdmax(entry->streams, index + 1);
+ entry->size -= previous_size;
+ total_size_ -= previous_size;
+
+ entry->accessors += 1;
+ total_accessors_ += 1;
+ return new DiskCacheAdapter(this, id, index, file.release());
+}
+
+bool DiskCache::UnlockResource(const std::string& id) {
+ Entry* entry = GetOrCreateEntry(id, false);
+ if (LS_LOCKED != entry->lock_state)
+ return false;
+
+ if (entry->accessors > 0) {
+ entry->lock_state = LS_UNLOCKING;
+ } else {
+ entry->lock_state = LS_UNLOCKED;
+ entry->last_modified = time(0);
+ CheckLimit();
+ }
+ return true;
+}
+
+StreamInterface* DiskCache::ReadResource(const std::string& id,
+ size_t index) const {
+ const Entry* entry = GetEntry(id);
+ if (LS_UNLOCKED != entry->lock_state)
+ return NULL;
+ if (index >= entry->streams)
+ return NULL;
+
+ scoped_ptr<FileStream> file(new FileStream);
+ if (!file->Open(IdToFilename(id, index), "rb"))
+ return NULL;
+
+ entry->accessors += 1;
+ total_accessors_ += 1;
+ return new DiskCacheAdapter(this, id, index, file.release());
+}
+
+bool DiskCache::HasResource(const std::string& id) const {
+ const Entry* entry = GetEntry(id);
+ return (NULL != entry) && (entry->streams > 0);
+}
+
+bool DiskCache::HasResourceStream(const std::string& id, size_t index) const {
+ const Entry* entry = GetEntry(id);
+ if ((NULL == entry) || (index >= entry->streams))
+ return false;
+
+ std::string filename = IdToFilename(id, index);
+
+ return FileExists(filename);
+}
+
+bool DiskCache::DeleteResource(const std::string& id) {
+ Entry* entry = GetOrCreateEntry(id, false);
+ if (!entry)
+ return true;
+
+ if ((LS_UNLOCKED != entry->lock_state) || (entry->accessors > 0))
+ return false;
+
+ bool success = true;
+ for (size_t index = 0; index < entry->streams; ++index) {
+ std::string filename = IdToFilename(id, index);
+
+ if (!FileExists(filename))
+ continue;
+
+ if (!DeleteFile(filename)) {
+ LOG_F(LS_ERROR) << "Couldn't remove cache file: " << filename;
+ success = false;
+ }
+ }
+
+ total_size_ -= entry->size;
+ map_.erase(id);
+ return success;
+}
+
+bool DiskCache::CheckLimit() {
+#ifdef _DEBUG
+ // Temporary check to make sure everything is working correctly.
+ size_t cache_size = 0;
+ for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) {
+ cache_size += it->second.size;
+ }
+ ASSERT(cache_size == total_size_);
+#endif // _DEBUG
+
+ // TODO: Replace this with a non-brain-dead algorithm for clearing out the
+ // oldest resources... something that isn't O(n^2)
+ while (total_size_ > max_cache_) {
+ EntryMap::iterator oldest = map_.end();
+ for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) {
+ if ((LS_UNLOCKED != it->second.lock_state) || (it->second.accessors > 0))
+ continue;
+ oldest = it;
+ break;
+ }
+ if (oldest == map_.end()) {
+ LOG_F(LS_WARNING) << "All resources are locked!";
+ return false;
+ }
+ for (EntryMap::iterator it = oldest++; it != map_.end(); ++it) {
+ if (it->second.last_modified < oldest->second.last_modified) {
+ oldest = it;
+ }
+ }
+ if (!DeleteResource(oldest->first)) {
+ LOG_F(LS_ERROR) << "Couldn't delete from cache!";
+ return false;
+ }
+ }
+ return true;
+}
+
+std::string DiskCache::IdToFilename(const std::string& id, size_t index) const {
+#ifdef TRANSPARENT_CACHE_NAMES
+ // This escapes colons and other filesystem characters, so the user can't open
+ // special devices (like "COM1:"), or access other directories.
+ size_t buffer_size = id.length()*3 + 1;
+ char* buffer = new char[buffer_size];
+ encode(buffer, buffer_size, id.data(), id.length(),
+ unsafe_filename_characters(), '%');
+ // TODO: ASSERT(strlen(buffer) < FileSystem::MaxBasenameLength());
+#else // !TRANSPARENT_CACHE_NAMES
+ // We might want to just use a hash of the filename at some point, both for
+ // obfuscation, and to avoid both filename length and escaping issues.
+ ASSERT(false);
+#endif // !TRANSPARENT_CACHE_NAMES
+
+ char extension[32];
+ sprintfn(extension, ARRAY_SIZE(extension), ".%u", index);
+
+ Pathname pathname;
+ pathname.SetFolder(folder_);
+ pathname.SetBasename(buffer);
+ pathname.SetExtension(extension);
+
+#ifdef TRANSPARENT_CACHE_NAMES
+ delete [] buffer;
+#endif // TRANSPARENT_CACHE_NAMES
+
+ return pathname.pathname();
+}
+
+bool DiskCache::FilenameToId(const std::string& filename, std::string* id,
+ size_t* index) const {
+ Pathname pathname(filename);
+ unsigned tempdex;
+ if (1 != sscanf(pathname.extension().c_str(), ".%u", &tempdex))
+ return false;
+
+ *index = static_cast<size_t>(tempdex);
+
+ size_t buffer_size = pathname.basename().length() + 1;
+ char* buffer = new char[buffer_size];
+ decode(buffer, buffer_size, pathname.basename().data(),
+ pathname.basename().length(), '%');
+ id->assign(buffer);
+ delete [] buffer;
+ return true;
+}
+
+DiskCache::Entry* DiskCache::GetOrCreateEntry(const std::string& id,
+ bool create) {
+ EntryMap::iterator it = map_.find(id);
+ if (it != map_.end())
+ return &it->second;
+ if (!create)
+ return NULL;
+ Entry e;
+ e.lock_state = LS_UNLOCKED;
+ e.accessors = 0;
+ e.size = 0;
+ e.streams = 0;
+ e.last_modified = time(0);
+ it = map_.insert(EntryMap::value_type(id, e)).first;
+ return &it->second;
+}
+
+void DiskCache::ReleaseResource(const std::string& id, size_t index) const {
+ const Entry* entry = GetEntry(id);
+ if (!entry) {
+ LOG_F(LS_WARNING) << "Missing cache entry";
+ ASSERT(false);
+ return;
+ }
+
+ entry->accessors -= 1;
+ total_accessors_ -= 1;
+
+ if (LS_UNLOCKED != entry->lock_state) {
+ // This is safe, because locked resources only issue WriteResource, which
+ // is non-const. Think about a better way to handle it.
+ DiskCache* this2 = const_cast<DiskCache*>(this);
+ Entry* entry2 = this2->GetOrCreateEntry(id, false);
+
+ size_t new_size = 0;
+ std::string filename(IdToFilename(id, index));
+ FileStream::GetSize(filename, &new_size);
+ entry2->size += new_size;
+ this2->total_size_ += new_size;
+
+ if ((LS_UNLOCKING == entry->lock_state) && (0 == entry->accessors)) {
+ entry2->last_modified = time(0);
+ entry2->lock_state = LS_UNLOCKED;
+ this2->CheckLimit();
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/diskcache.h b/third_party/libjingle/source/talk/base/diskcache.h
new file mode 100644
index 0000000..c5a1dfc
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/diskcache.h
@@ -0,0 +1,142 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_DISKCACHE_H__
+#define TALK_BASE_DISKCACHE_H__
+
+#include <map>
+#include <string>
+
+#ifdef WIN32
+#undef UnlockResource
+#endif // WIN32
+
+namespace talk_base {
+
+class StreamInterface;
+
+///////////////////////////////////////////////////////////////////////////////
+// DiskCache - An LRU cache of streams, stored on disk.
+//
+// Streams are identified by a unique resource id. Multiple streams can be
+// associated with each resource id, distinguished by an index. When old
+// resources are flushed from the cache, all streams associated with those
+// resources are removed together.
+// DiskCache is designed to persist across executions of the program. It is
+// safe for use from an arbitrary number of users on a single thread, but not
+// from multiple threads or other processes.
+///////////////////////////////////////////////////////////////////////////////
+
+class DiskCache {
+public:
+ DiskCache();
+ virtual ~DiskCache();
+
+ bool Initialize(const std::string& folder, size_t size);
+ bool Purge();
+
+ bool LockResource(const std::string& id);
+ StreamInterface* WriteResource(const std::string& id, size_t index);
+ bool UnlockResource(const std::string& id);
+
+ StreamInterface* ReadResource(const std::string& id, size_t index) const;
+
+ bool HasResource(const std::string& id) const;
+ bool HasResourceStream(const std::string& id, size_t index) const;
+ bool DeleteResource(const std::string& id);
+
+ protected:
+ virtual bool InitializeEntries() = 0;
+ virtual bool PurgeFiles() = 0;
+
+ virtual bool FileExists(const std::string& filename) const = 0;
+ virtual bool DeleteFile(const std::string& filename) const = 0;
+
+ enum LockState { LS_UNLOCKED, LS_LOCKED, LS_UNLOCKING };
+ struct Entry {
+ LockState lock_state;
+ mutable size_t accessors;
+ size_t size;
+ size_t streams;
+ time_t last_modified;
+ };
+ typedef std::map<std::string, Entry> EntryMap;
+ friend class DiskCacheAdapter;
+
+ bool CheckLimit();
+
+ std::string IdToFilename(const std::string& id, size_t index) const;
+ bool FilenameToId(const std::string& filename, std::string* id,
+ size_t* index) const;
+
+ const Entry* GetEntry(const std::string& id) const {
+ return const_cast<DiskCache*>(this)->GetOrCreateEntry(id, false);
+ }
+ Entry* GetOrCreateEntry(const std::string& id, bool create);
+
+ void ReleaseResource(const std::string& id, size_t index) const;
+
+ std::string folder_;
+ size_t max_cache_, total_size_;
+ EntryMap map_;
+ mutable size_t total_accessors_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheLock - Automatically manage locking and unlocking, with optional
+// rollback semantics
+///////////////////////////////////////////////////////////////////////////////
+
+class CacheLock {
+public:
+ CacheLock(DiskCache* cache, const std::string& id, bool rollback = false)
+ : cache_(cache), id_(id), rollback_(rollback)
+ {
+ locked_ = cache_->LockResource(id_);
+ }
+ ~CacheLock() {
+ if (locked_) {
+ cache_->UnlockResource(id_);
+ if (rollback_) {
+ cache_->DeleteResource(id_);
+ }
+ }
+ }
+ bool IsLocked() const { return locked_; }
+ void Commit() { rollback_ = false; }
+
+private:
+ DiskCache* cache_;
+ std::string id_;
+ bool rollback_, locked_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_DISKCACHE_H__
diff --git a/third_party/libjingle/source/talk/base/event.cc b/third_party/libjingle/source/talk/base/event.cc
new file mode 100644
index 0000000..f2fd04d
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/event.cc
@@ -0,0 +1,193 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/event.h"
+
+#if defined(WIN32)
+#include <windows.h>
+#elif defined(POSIX)
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+#else
+#error "Must define either WIN32 or POSIX."
+#endif
+
+namespace talk_base {
+
+#if defined(WIN32)
+
+Event::Event(bool manual_reset, bool initially_signaled)
+ : is_manual_reset_(manual_reset),
+ is_initially_signaled_(initially_signaled),
+ event_handle_(NULL) {
+}
+
+bool Event::EnsureInitialized() {
+ if (event_handle_ == NULL) {
+ event_handle_ = ::CreateEvent(NULL, // Security attributes.
+ is_manual_reset_,
+ is_initially_signaled_,
+ NULL); // Name.
+ }
+
+ return (event_handle_ != NULL);
+}
+
+Event::~Event() {
+ if (event_handle_ != NULL) {
+ CloseHandle(event_handle_);
+ event_handle_ = NULL;
+ }
+}
+
+bool Event::Set() {
+ if (!EnsureInitialized())
+ return false;
+
+ SetEvent(event_handle_);
+ return true;
+}
+
+bool Event::Reset() {
+ if (!EnsureInitialized())
+ return false;
+
+ ResetEvent(event_handle_);
+ return true;
+}
+
+bool Event::Wait(int cms) {
+ DWORD ms = (cms == kForever)? INFINITE : cms;
+
+ if (!EnsureInitialized())
+ return false;
+ else
+ return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0);
+}
+
+#elif defined(POSIX)
+
+Event::Event(bool manual_reset, bool initially_signaled)
+ : is_manual_reset_(manual_reset),
+ event_status_(initially_signaled),
+ event_mutex_initialized_(false),
+ event_cond_initialized_(false) {
+}
+
+bool Event::EnsureInitialized() {
+ if (!event_mutex_initialized_) {
+ if (pthread_mutex_init(&event_mutex_, NULL) == 0)
+ event_mutex_initialized_ = true;
+ }
+
+ if (!event_cond_initialized_) {
+ if (pthread_cond_init(&event_cond_, NULL) == 0)
+ event_cond_initialized_ = true;
+ }
+
+ return (event_mutex_initialized_ && event_cond_initialized_);
+}
+
+Event::~Event() {
+ if (event_mutex_initialized_) {
+ pthread_mutex_destroy(&event_mutex_);
+ event_mutex_initialized_ = false;
+ }
+
+ if (event_cond_initialized_) {
+ pthread_cond_destroy(&event_cond_);
+ event_cond_initialized_ = false;
+ }
+}
+
+bool Event::Set() {
+ if (!EnsureInitialized())
+ return false;
+
+ pthread_mutex_lock(&event_mutex_);
+ event_status_ = true;
+ pthread_cond_broadcast(&event_cond_);
+ pthread_mutex_unlock(&event_mutex_);
+ return true;
+}
+
+bool Event::Reset() {
+ if (!EnsureInitialized())
+ return false;
+
+ pthread_mutex_lock(&event_mutex_);
+ event_status_ = false;
+ pthread_mutex_unlock(&event_mutex_);
+ return true;
+}
+
+bool Event::Wait(int cms) {
+ if (!EnsureInitialized())
+ return false;
+
+ pthread_mutex_lock(&event_mutex_);
+ int error = 0;
+
+ if (cms != kForever) {
+ // Converting from seconds and microseconds (1e-6) plus
+ // milliseconds (1e-3) to seconds and nanoseconds (1e-9).
+
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ struct timespec ts;
+ ts.tv_sec = tv.tv_sec + (cms / 1000);
+ ts.tv_nsec = tv.tv_usec * 1000 + (cms % 1000) * 1000000;
+
+ // Handle overflow.
+ if (ts.tv_nsec >= 1000000000) {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+
+ while (!event_status_ && error == 0)
+ error = pthread_cond_timedwait(&event_cond_, &event_mutex_, &ts);
+ } else {
+ while (!event_status_ && error == 0)
+ error = pthread_cond_wait(&event_cond_, &event_mutex_);
+ }
+
+ // NOTE(liulk): Exactly one thread will auto-reset this event. All
+ // the other threads will think it's unsignaled. This seems to be
+ // consistent with auto-reset events in WIN32.
+ if (error == 0 && !is_manual_reset_)
+ event_status_ = false;
+
+ pthread_mutex_unlock(&event_mutex_);
+
+ return (error == 0);
+}
+
+#endif
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/event.h b/third_party/libjingle/source/talk/base/event.h
new file mode 100644
index 0000000..757164c
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/event.h
@@ -0,0 +1,71 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_EVENT_H__
+#define TALK_BASE_EVENT_H__
+
+#if defined(WIN32)
+#include "talk/base/win32.h" // NOLINT: consider this a system header.
+#elif defined(POSIX)
+#include <pthread.h>
+#else
+#error "Must define either WIN32 or POSIX."
+#endif
+
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+class Event {
+ public:
+ Event(bool manual_reset, bool initially_signaled);
+ ~Event();
+
+ bool Set();
+ bool Reset();
+ bool Wait(int cms);
+
+ private:
+ bool EnsureInitialized();
+
+ bool is_manual_reset_;
+
+#if defined(WIN32)
+ bool is_initially_signaled_;
+ HANDLE event_handle_;
+#elif defined(POSIX)
+ bool event_status_;
+ bool event_mutex_initialized_;
+ pthread_mutex_t event_mutex_;
+ bool event_cond_initialized_;
+ pthread_cond_t event_cond_;
+#endif
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_EVENT_H__
diff --git a/third_party/libjingle/source/talk/base/fileutils.cc b/third_party/libjingle/source/talk/base/fileutils.cc
new file mode 100644
index 0000000..acfd2f8
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/fileutils.cc
@@ -0,0 +1,284 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <cassert>
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+#include "talk/base/pathutils.h"
+#include "talk/base/fileutils.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/stream.h"
+
+#include "talk/base/unixfilesystem.h"
+#include "talk/base/win32filesystem.h"
+
+#ifndef WIN32
+#define MAX_PATH 260
+#endif
+
+namespace talk_base {
+
+//////////////////////////
+// Directory Iterator //
+//////////////////////////
+
+// A DirectoryIterator is created with a given directory. It originally points
+// to the first file in the directory, and can be advanecd with Next(). This
+// allows you to get information about each file.
+
+ // Constructor
+DirectoryIterator::DirectoryIterator()
+#ifdef _WIN32
+ : handle_(INVALID_HANDLE_VALUE) {
+#else
+ : dir_(NULL), dirent_(NULL) {
+#endif
+}
+
+ // Destructor
+DirectoryIterator::~DirectoryIterator() {
+#ifdef WIN32
+ if (handle_ != INVALID_HANDLE_VALUE)
+ ::FindClose(handle_);
+#else
+ if (dir_)
+ closedir(dir_);
+#endif
+}
+
+ // Starts traversing a directory.
+ // dir is the directory to traverse
+ // returns true if the directory exists and is valid
+bool DirectoryIterator::Iterate(const Pathname &dir) {
+ directory_ = dir.pathname();
+#ifdef WIN32
+ if (handle_ != INVALID_HANDLE_VALUE)
+ ::FindClose(handle_);
+ std::string d = dir.pathname() + '*';
+ handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_);
+ if (handle_ == INVALID_HANDLE_VALUE)
+ return false;
+#else
+ if (dir_ != NULL)
+ closedir(dir_);
+ dir_ = ::opendir(directory_.c_str());
+ if (dir_ == NULL)
+ return false;
+ dirent_ = readdir(dir_);
+ if (dirent_ == NULL)
+ return false;
+
+ if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0)
+ return false;
+#endif
+ return true;
+}
+
+ // Advances to the next file
+ // returns true if there were more files in the directory.
+bool DirectoryIterator::Next() {
+#ifdef WIN32
+ return ::FindNextFile(handle_, &data_) == TRUE;
+#else
+ dirent_ = ::readdir(dir_);
+ if (dirent_ == NULL)
+ return false;
+
+ return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0;
+#endif
+}
+
+ // returns true if the file currently pointed to is a directory
+bool DirectoryIterator::IsDirectory() const {
+#ifdef WIN32
+ return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE;
+#else
+ return S_ISDIR(stat_.st_mode);
+#endif
+}
+
+ // returns the name of the file currently pointed to
+std::string DirectoryIterator::Name() const {
+#ifdef WIN32
+ return ToUtf8(data_.cFileName);
+#else
+ assert(dirent_ != NULL);
+ return dirent_->d_name;
+#endif
+}
+
+ // returns the size of the file currently pointed to
+size_t DirectoryIterator::FileSize() const {
+#ifndef WIN32
+ return stat_.st_size;
+#else
+ return data_.nFileSizeLow;
+#endif
+}
+
+ // returns the last modified time of this file
+time_t DirectoryIterator::FileModifyTime() const {
+#ifdef WIN32
+ time_t val;
+ FileTimeToUnixTime(data_.ftLastWriteTime, &val);
+ return val;
+#else
+ return stat_.st_mtime;
+#endif
+}
+
+scoped_ptr<FilesystemInterface> Filesystem::default_filesystem_;
+FilesystemInterface *Filesystem::EnsureDefaultFilesystem() {
+ if (!default_filesystem_.get())
+#ifdef WIN32
+ default_filesystem_.reset(new Win32Filesystem());
+#else
+ default_filesystem_.reset(new UnixFilesystem());
+#endif
+ return default_filesystem_.get();
+}
+
+bool FilesystemInterface::CopyFolder(const Pathname &old_path,
+ const Pathname &new_path) {
+ VERIFY(IsFolder(old_path));
+ Pathname new_dir;
+ new_dir.SetFolder(new_path.pathname());
+ Pathname old_dir;
+ old_dir.SetFolder(old_path.pathname());
+ if (!CreateFolder(new_dir))
+ return false;
+ DirectoryIterator di;
+ di.Iterate(old_dir.pathname());
+ while (di.Next()) {
+ if (di.Name() == "." || di.Name() == "..")
+ continue;
+ Pathname source;
+ Pathname dest;
+ source.SetFolder(old_dir.pathname());
+ dest.SetFolder(new_path.pathname());
+ source.SetFilename(di.Name());
+ dest.SetFilename(di.Name());
+ if (!CopyFileOrFolder(source, dest))
+ return false;
+ }
+ return true;
+}
+
+bool FilesystemInterface::DeleteFolderContents(const Pathname &folder) {
+ bool success = true;
+ VERIFY(IsFolder(folder));
+ DirectoryIterator *di = IterateDirectory();
+ di->Iterate(folder);
+ while (di->Next()) {
+ if (di->Name() == "." || di->Name() == "..")
+ continue;
+ Pathname subdir;
+ subdir.SetFolder(folder.pathname());
+ if (di->IsDirectory()) {
+ subdir.AppendFolder(di->Name());
+ if (!DeleteFolderAndContents(subdir)) {
+ success = false;
+ }
+ } else {
+ subdir.SetFilename(di->Name());
+ if (!DeleteFile(subdir)) {
+ success = false;
+ }
+ }
+ }
+ delete di;
+ return success;
+}
+
+bool FilesystemInterface::CleanAppTempFolder() {
+ Pathname path;
+ if (!GetAppTempFolder(&path))
+ return false;
+ if (IsAbsent(path))
+ return true;
+ if (!IsTemporaryPath(path)) {
+ ASSERT(false);
+ return false;
+ }
+ return DeleteFolderContents(path);
+}
+
+Pathname Filesystem::GetCurrentDirectory() {
+ return EnsureDefaultFilesystem()->GetCurrentDirectory();
+}
+
+bool CreateUniqueFile(Pathname& path, bool create_empty) {
+ LOG(LS_INFO) << "Path " << path.pathname() << std::endl;
+ // If no folder is supplied, use the temporary folder
+ if (path.folder().empty()) {
+ Pathname temporary_path;
+ if (!Filesystem::GetTemporaryFolder(temporary_path, true, NULL)) {
+ printf("Get temp failed\n");
+ return false;
+ }
+ path.SetFolder(temporary_path.pathname());
+ }
+
+ // If no filename is supplied, use a temporary name
+ if (path.filename().empty()) {
+ std::string folder(path.folder());
+ std::string filename = Filesystem::TempFilename(folder, "gt");
+ path.SetPathname(filename);
+ if (!create_empty) {
+ Filesystem::DeleteFile(path.pathname());
+ }
+ return true;
+ }
+
+ // Otherwise, create a unique name based on the given filename
+ // foo.txt -> foo-N.txt
+ const std::string basename = path.basename();
+ const size_t MAX_VERSION = 100;
+ size_t version = 0;
+ while (version < MAX_VERSION) {
+ std::string pathname = path.pathname();
+
+ if (!Filesystem::IsFile(pathname)) {
+ if (create_empty) {
+ FileStream* fs = Filesystem::OpenFile(pathname, "w");
+ delete fs;
+ }
+ return true;
+ }
+ version += 1;
+ char version_base[MAX_PATH];
+ sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u",
+ basename.c_str(), version);
+ path.SetBasename(version_base);
+ }
+ return true;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/fileutils.h b/third_party/libjingle/source/talk/base/fileutils.h
new file mode 100644
index 0000000..b87a224
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/fileutils.h
@@ -0,0 +1,439 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_FILEUTILS_H_
+#define TALK_BASE_FILEUTILS_H_
+
+#include <string>
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#else
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/scoped_ptr.h"
+
+namespace talk_base {
+
+class FileStream;
+class Pathname;
+
+//////////////////////////
+// Directory Iterator //
+//////////////////////////
+
+// A DirectoryIterator is created with a given directory. It originally points
+// to the first file in the directory, and can be advanecd with Next(). This
+// allows you to get information about each file.
+
+class DirectoryIterator {
+ friend class Filesystem;
+ public:
+ // Constructor
+ DirectoryIterator();
+ // Destructor
+ virtual ~DirectoryIterator();
+
+ // Starts traversing a directory
+ // dir is the directory to traverse
+ // returns true if the directory exists and is valid
+ // The iterator will point to the first entry in the directory
+ virtual bool Iterate(const Pathname &path);
+
+ // Advances to the next file
+ // returns true if there were more files in the directory.
+ virtual bool Next();
+
+ // returns true if the file currently pointed to is a directory
+ virtual bool IsDirectory() const;
+
+ // returns the name of the file currently pointed to
+ virtual std::string Name() const;
+
+ // returns the size of the file currently pointed to
+ virtual size_t FileSize() const;
+
+ // returns the last modified time of the file currently pointed to
+ virtual time_t FileModifyTime() const;
+
+ // checks whether current file is a special directory file "." or ".."
+ bool IsDots() const {
+ std::string filename(Name());
+ return (filename.compare(".") == 0) || (filename.compare("..") == 0);
+ }
+
+ private:
+ std::string directory_;
+#ifdef WIN32
+ WIN32_FIND_DATA data_;
+ HANDLE handle_;
+#else
+ DIR *dir_;
+ struct dirent *dirent_;
+ struct stat stat_;
+#endif
+};
+
+enum FileTimeType { FTT_CREATED, FTT_MODIFIED, FTT_ACCESSED };
+
+class FilesystemInterface {
+ public:
+ virtual ~FilesystemInterface() {}
+
+ // Returns a DirectoryIterator for a given pathname.
+ // TODO: Do fancy abstracted stuff
+ virtual DirectoryIterator *IterateDirectory() {
+ return new DirectoryIterator();
+ }
+
+ // Opens a file. Returns an open StreamInterface if function succeeds.
+ // Otherwise, returns NULL.
+ virtual FileStream *OpenFile(const Pathname &filename,
+ const std::string &mode) = 0;
+
+ // This will attempt to delete the path located at filename.
+ // It ASSERTS and returns false if the path points to a folder or a
+ // non-existent file.
+ virtual bool DeleteFile(const Pathname &filename) = 0;
+
+ // This will attempt to delete the empty folder located at 'folder'
+ // It ASSERTS and returns false if the path points to a file or a non-existent
+ // folder. It fails normally if the folder is not empty or can otherwise
+ // not be deleted.
+ virtual bool DeleteEmptyFolder(const Pathname &folder) = 0;
+
+ // This will call IterateDirectory, to get a directory iterator, and then
+ // call DeleteFolderAndContents and DeleteFile on every path contained in this
+ // folder. If the folder is empty, this returns true.
+ virtual bool DeleteFolderContents(const Pathname &folder);
+
+ // This deletes the contents of a folder, recursively, and then deletes
+ // the folder itself.
+ virtual bool DeleteFolderAndContents(const Pathname &folder) {
+ return DeleteFolderContents(folder) && DeleteEmptyFolder(folder);
+ }
+
+ // This will delete whatever is located at path, be it a file or a folder.
+ // If it is a folder, it will delete it recursively by calling
+ // DeleteFolderAndContents
+ bool DeleteFileOrFolder(const Pathname &path) {
+ if (IsFolder(path))
+ return DeleteFolderAndContents(path);
+ else
+ return DeleteFile(path);
+ }
+
+ // Creates a directory. This will call itself recursively to create /foo/bar
+ // even if /foo does not exist. Returns true if the function succeeds.
+ virtual bool CreateFolder(const Pathname &pathname) = 0;
+
+ // This moves a file from old_path to new_path, where "old_path" is a
+ // plain file. This ASSERTs and returns false if old_path points to a
+ // directory, and returns true if the function succeeds.
+ // If the new path is on a different volume than the old path, this function
+ // will attempt to copy and, if that succeeds, delete the old path.
+ virtual bool MoveFolder(const Pathname &old_path,
+ const Pathname &new_path) = 0;
+
+ // This moves a directory from old_path to new_path, where "old_path" is a
+ // directory. This ASSERTs and returns false if old_path points to a plain
+ // file, and returns true if the function succeeds.
+ // If the new path is on a different volume, this function will attempt to
+ // copy and if that succeeds, delete the old path.
+ virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path) = 0;
+
+ // This attempts to move whatever is located at old_path to new_path,
+ // be it a file or folder.
+ bool MoveFileOrFolder(const Pathname &old_path, const Pathname &new_path) {
+ if (IsFile(old_path)) {
+ return MoveFile(old_path, new_path);
+ } else {
+ return MoveFolder(old_path, new_path);
+ }
+ }
+
+ // This copies a file from old_path to new_path. This method ASSERTs and
+ // returns false if old_path is a folder, and returns true if the copy
+ // succeeds.
+ virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path) = 0;
+
+ // This copies a folder from old_path to new_path.
+ bool CopyFolder(const Pathname &old_path, const Pathname &new_path);
+
+ bool CopyFileOrFolder(const Pathname &old_path, const Pathname &new_path) {
+ if (IsFile(old_path))
+ return CopyFile(old_path, new_path);
+ else
+ return CopyFolder(old_path, new_path);
+ }
+
+ // Returns true if pathname refers to a directory
+ virtual bool IsFolder(const Pathname& pathname) = 0;
+
+ // Returns true if pathname refers to a file
+ virtual bool IsFile(const Pathname& pathname) = 0;
+
+ // Returns true if pathname refers to no filesystem object, every parent
+ // directory either exists, or is also absent.
+ virtual bool IsAbsent(const Pathname& pathname) = 0;
+
+ // Returns true if pathname represents a temporary location on the system.
+ virtual bool IsTemporaryPath(const Pathname& pathname) = 0;
+
+ // A folder appropriate for storing temporary files (Contents are
+ // automatically deleted when the program exits)
+ virtual bool GetTemporaryFolder(Pathname &path, bool create,
+ const std::string *append) = 0;
+
+ virtual std::string TempFilename(const Pathname &dir,
+ const std::string &prefix) = 0;
+
+ // Determines the size of the file indicated by path.
+ virtual bool GetFileSize(const Pathname& path, size_t* size) = 0;
+
+ // Determines a timestamp associated with the file indicated by path.
+ virtual bool GetFileTime(const Pathname& path, FileTimeType which,
+ time_t* time) = 0;
+
+ // Returns the path to the running application.
+ // Note: This is not guaranteed to work on all platforms. Be aware of the
+ // limitations before using it, and robustly handle failure.
+ virtual bool GetAppPathname(Pathname* path) = 0;
+
+ // Get a folder that is unique to the current application, which is suitable
+ // for sharing data between executions of the app. If the per_user arg is
+ // true, the folder is also specific to the current user.
+ virtual bool GetAppDataFolder(Pathname* path, bool per_user) = 0;
+
+ // Get a temporary folder that is unique to the current user and application.
+ // TODO: Re-evaluate the goals of this function. We probably just need any
+ // directory that won't collide with another existing directory, and which
+ // will be cleaned up when the program exits.
+ virtual bool GetAppTempFolder(Pathname* path) = 0;
+
+ // Delete the contents of the folder returned by GetAppTempFolder
+ bool CleanAppTempFolder();
+
+ virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) = 0;
+
+ // Returns the absolute path of the current directory.
+ virtual Pathname GetCurrentDirectory() = 0;
+
+ // Note: These might go into some shared config section later, but they're
+ // used by some methods in this interface, so we're leaving them here for now.
+ void SetOrganizationName(const std::string& organization) {
+ organization_name_ = organization;
+ }
+ void GetOrganizationName(std::string* organization) {
+ ASSERT(NULL != organization);
+ *organization = organization_name_;
+ }
+ void SetApplicationName(const std::string& application) {
+ application_name_ = application;
+ }
+ void GetApplicationName(std::string* application) {
+ ASSERT(NULL != application);
+ *application = application_name_;
+ }
+
+ protected:
+ std::string organization_name_;
+ std::string application_name_;
+};
+
+class Filesystem {
+ public:
+ static FilesystemInterface *default_filesystem() {
+ ASSERT(default_filesystem_.get() != NULL);
+ return default_filesystem_.get();
+ }
+
+ static void set_default_filesystem(FilesystemInterface *filesystem) {
+ default_filesystem_.reset(filesystem);
+ }
+
+ static FilesystemInterface *swap_default_filesystem(
+ FilesystemInterface *filesystem) {
+ FilesystemInterface *cur = default_filesystem_.release();
+ default_filesystem_.reset(filesystem);
+ return cur;
+ }
+
+ static DirectoryIterator *IterateDirectory() {
+ return EnsureDefaultFilesystem()->IterateDirectory();
+ }
+
+ static bool CreateFolder(const Pathname &pathname) {
+ return EnsureDefaultFilesystem()->CreateFolder(pathname);
+ }
+
+ static FileStream *OpenFile(const Pathname &filename,
+ const std::string &mode) {
+ return EnsureDefaultFilesystem()->OpenFile(filename, mode);
+ }
+
+ static bool DeleteFile(const Pathname &filename) {
+ return EnsureDefaultFilesystem()->DeleteFile(filename);
+ }
+
+ static bool DeleteEmptyFolder(const Pathname &folder) {
+ return EnsureDefaultFilesystem()->DeleteEmptyFolder(folder);
+ }
+
+ static bool DeleteFolderContents(const Pathname &folder) {
+ return EnsureDefaultFilesystem()->DeleteFolderContents(folder);
+ }
+
+ static bool DeleteFolderAndContents(const Pathname &folder) {
+ return EnsureDefaultFilesystem()->DeleteFolderAndContents(folder);
+ }
+
+ static bool MoveFolder(const Pathname &old_path, const Pathname &new_path) {
+ return EnsureDefaultFilesystem()->MoveFolder(old_path, new_path);
+ }
+
+ static bool MoveFile(const Pathname &old_path, const Pathname &new_path) {
+ return EnsureDefaultFilesystem()->MoveFile(old_path, new_path);
+ }
+
+ static bool CopyFile(const Pathname &old_path, const Pathname &new_path) {
+ return EnsureDefaultFilesystem()->CopyFile(old_path, new_path);
+ }
+
+ static bool IsFolder(const Pathname& pathname) {
+ return EnsureDefaultFilesystem()->IsFolder(pathname);
+ }
+
+ static bool IsFile(const Pathname &pathname) {
+ return EnsureDefaultFilesystem()->IsFile(pathname);
+ }
+
+ static bool IsAbsent(const Pathname &pathname) {
+ return EnsureDefaultFilesystem()->IsAbsent(pathname);
+ }
+
+ static bool IsTemporaryPath(const Pathname& pathname) {
+ return EnsureDefaultFilesystem()->IsTemporaryPath(pathname);
+ }
+
+ static bool GetTemporaryFolder(Pathname &path, bool create,
+ const std::string *append) {
+ return EnsureDefaultFilesystem()->GetTemporaryFolder(path, create, append);
+ }
+
+ static std::string TempFilename(const Pathname &dir,
+ const std::string &prefix) {
+ return EnsureDefaultFilesystem()->TempFilename(dir, prefix);
+ }
+
+ static bool GetFileSize(const Pathname& path, size_t* size) {
+ return EnsureDefaultFilesystem()->GetFileSize(path, size);
+ }
+
+ static bool GetFileTime(const Pathname& path, FileTimeType which,
+ time_t* time) {
+ return EnsureDefaultFilesystem()->GetFileTime(path, which, time);
+ }
+
+ static bool GetAppPathname(Pathname* path) {
+ return EnsureDefaultFilesystem()->GetAppPathname(path);
+ }
+
+ static bool GetAppDataFolder(Pathname* path, bool per_user) {
+ return EnsureDefaultFilesystem()->GetAppDataFolder(path, per_user);
+ }
+
+ static bool GetAppTempFolder(Pathname* path) {
+ return EnsureDefaultFilesystem()->GetAppTempFolder(path);
+ }
+
+ static bool CleanAppTempFolder() {
+ return EnsureDefaultFilesystem()->CleanAppTempFolder();
+ }
+
+ static bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) {
+ return EnsureDefaultFilesystem()->GetDiskFreeSpace(path, freebytes);
+ }
+
+ // Definition has to be in the .cc file due to returning forward-declared
+ // Pathname by value.
+ static Pathname GetCurrentDirectory();
+
+ static void SetOrganizationName(const std::string& organization) {
+ EnsureDefaultFilesystem()->SetOrganizationName(organization);
+ }
+
+ static void GetOrganizationName(std::string* organization) {
+ EnsureDefaultFilesystem()->GetOrganizationName(organization);
+ }
+
+ static void SetApplicationName(const std::string& application) {
+ EnsureDefaultFilesystem()->SetApplicationName(application);
+ }
+
+ static void GetApplicationName(std::string* application) {
+ EnsureDefaultFilesystem()->GetApplicationName(application);
+ }
+
+ private:
+ static scoped_ptr<FilesystemInterface> default_filesystem_;
+
+ static FilesystemInterface *EnsureDefaultFilesystem();
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Filesystem);
+};
+
+class FilesystemScope{
+ public:
+ explicit FilesystemScope(FilesystemInterface *new_fs) {
+ old_fs_ = Filesystem::swap_default_filesystem(new_fs);
+ }
+ ~FilesystemScope() {
+ Filesystem::set_default_filesystem(old_fs_);
+ }
+ private:
+ FilesystemInterface* old_fs_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FilesystemScope);
+};
+
+// Generates a unique filename based on the input path. If no path component
+// is specified, it uses the temporary directory. If a filename is provided,
+// up to 100 variations of form basename-N.extension are tried. When
+// create_empty is true, an empty file of this name is created (which
+// decreases the chance of a temporary filename collision with another
+// process).
+bool CreateUniqueFile(Pathname& path, bool create_empty);
+
+} // namespace talk_base
+
+#endif // TALK_BASE_FILEUTILS_H_
+
diff --git a/third_party/libjingle/source/talk/base/firewallsocketserver.cc b/third_party/libjingle/source/talk/base/firewallsocketserver.cc
new file mode 100644
index 0000000..819ec5f
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/firewallsocketserver.cc
@@ -0,0 +1,246 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/firewallsocketserver.h"
+
+#include <cassert>
+#include <algorithm>
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+class FirewallSocket : public AsyncSocketAdapter {
+ public:
+ FirewallSocket(FirewallSocketServer* server, AsyncSocket* socket, int type)
+ : AsyncSocketAdapter(socket), server_(server), type_(type) {
+ }
+
+ virtual int Connect(const SocketAddress& addr) {
+ if (type_ == SOCK_STREAM) {
+ if (!server_->Check(FP_TCP, GetLocalAddress(), addr)) {
+ LOG(LS_VERBOSE) << "FirewallSocket outbound TCP connection from "
+ << GetLocalAddress().ToString() << " to "
+ << addr.ToString() << " denied";
+ // TODO(juberti): Handle this asynchronously.
+ SetError(EHOSTUNREACH);
+ return SOCKET_ERROR;
+ }
+ }
+ return AsyncSocketAdapter::Connect(addr);
+ }
+ virtual int Send(const void* pv, size_t cb) {
+ return SendTo(pv, cb, GetRemoteAddress());
+ }
+ virtual int SendTo(const void* pv, size_t cb, const SocketAddress& addr) {
+ if (type_ == SOCK_DGRAM) {
+ if (!server_->Check(FP_UDP, GetLocalAddress(), addr)) {
+ LOG(LS_VERBOSE) << "FirewallSocket outbound UDP packet from "
+ << GetLocalAddress().ToString() << " to "
+ << addr.ToString() << " dropped";
+ return static_cast<int>(cb);
+ }
+ }
+ return AsyncSocketAdapter::SendTo(pv, cb, addr);
+ }
+ virtual int Recv(void* pv, size_t cb) {
+ SocketAddress addr;
+ return RecvFrom(pv, cb, &addr);
+ }
+ virtual int RecvFrom(void* pv, size_t cb, SocketAddress* paddr) {
+ if (type_ == SOCK_DGRAM) {
+ while (true) {
+ int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr);
+ if (res <= 0)
+ return res;
+ if (server_->Check(FP_UDP, *paddr, GetLocalAddress()))
+ return res;
+ LOG(LS_VERBOSE) << "FirewallSocket inbound UDP packet from "
+ << paddr->ToString() << " to "
+ << GetLocalAddress().ToString() << " dropped";
+ }
+ }
+ return AsyncSocketAdapter::RecvFrom(pv, cb, paddr);
+ }
+
+ virtual int Listen(int backlog) {
+ if (!server_->tcp_listen_enabled()) {
+ LOG(LS_VERBOSE) << "FirewallSocket listen attempt denied";
+ return -1;
+ }
+
+ return AsyncSocketAdapter::Listen(backlog);
+ }
+ virtual AsyncSocket* Accept(SocketAddress* paddr) {
+ SocketAddress addr;
+ while (AsyncSocket* sock = AsyncSocketAdapter::Accept(&addr)) {
+ if (server_->Check(FP_TCP, addr, GetLocalAddress())) {
+ if (paddr)
+ *paddr = addr;
+ return sock;
+ }
+ sock->Close();
+ delete sock;
+ LOG(LS_VERBOSE) << "FirewallSocket inbound TCP connection from "
+ << addr.ToString() << " to "
+ << GetLocalAddress().ToString() << " denied";
+ }
+ return 0;
+ }
+
+ private:
+ FirewallSocketServer* server_;
+ int type_;
+};
+
+FirewallSocketServer::FirewallSocketServer(SocketServer* server,
+ FirewallManager* manager,
+ bool should_delete_server)
+ : server_(server), manager_(manager),
+ should_delete_server_(should_delete_server),
+ udp_sockets_enabled_(true), tcp_sockets_enabled_(true),
+ tcp_listen_enabled_(true) {
+ if (manager_)
+ manager_->AddServer(this);
+}
+
+FirewallSocketServer::~FirewallSocketServer() {
+ if (manager_)
+ manager_->RemoveServer(this);
+
+ if (server_ && should_delete_server_) {
+ delete server_;
+ server_ = NULL;
+ }
+}
+
+void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p,
+ FirewallDirection d,
+ const SocketAddress& addr) {
+ SocketAddress src, dst;
+ if (d == FD_IN) {
+ dst = addr;
+ } else {
+ src = addr;
+ }
+ AddRule(allow, p, src, dst);
+}
+
+
+void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p,
+ const SocketAddress& src,
+ const SocketAddress& dst) {
+ Rule r;
+ r.allow = allow;
+ r.p = p;
+ r.src = src;
+ r.dst = dst;
+ CritScope scope(&crit_);
+ rules_.push_back(r);
+}
+
+void FirewallSocketServer::ClearRules() {
+ CritScope scope(&crit_);
+ rules_.clear();
+}
+
+bool FirewallSocketServer::Check(FirewallProtocol p,
+ const SocketAddress& src,
+ const SocketAddress& dst) {
+ CritScope scope(&crit_);
+ for (size_t i = 0; i < rules_.size(); ++i) {
+ const Rule& r = rules_[i];
+ if ((r.p != p) && (r.p != FP_ANY))
+ continue;
+ if ((r.src.ip() != src.ip()) && !r.src.IsAny())
+ continue;
+ if ((r.src.port() != src.port()) && (r.src.port() != 0))
+ continue;
+ if ((r.dst.ip() != dst.ip()) && !r.dst.IsAny())
+ continue;
+ if ((r.dst.port() != dst.port()) && (r.dst.port() != 0))
+ continue;
+ return r.allow;
+ }
+ return true;
+}
+
+Socket* FirewallSocketServer::CreateSocket(int type) {
+ return WrapSocket(server_->CreateAsyncSocket(type), type);
+}
+
+AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int type) {
+ return WrapSocket(server_->CreateAsyncSocket(type), type);
+}
+
+AsyncSocket* FirewallSocketServer::WrapSocket(AsyncSocket* sock, int type) {
+ if (!sock ||
+ (type == SOCK_STREAM && !tcp_sockets_enabled_) ||
+ (type == SOCK_DGRAM && !udp_sockets_enabled_)) {
+ LOG(LS_VERBOSE) << "FirewallSocketServer socket creation denied";
+ return NULL;
+ }
+ return new FirewallSocket(this, sock, type);
+}
+
+FirewallManager::FirewallManager() {
+}
+
+FirewallManager::~FirewallManager() {
+ assert(servers_.empty());
+}
+
+void FirewallManager::AddServer(FirewallSocketServer* server) {
+ CritScope scope(&crit_);
+ servers_.push_back(server);
+}
+
+void FirewallManager::RemoveServer(FirewallSocketServer* server) {
+ CritScope scope(&crit_);
+ servers_.erase(std::remove(servers_.begin(), servers_.end(), server),
+ servers_.end());
+}
+
+void FirewallManager::AddRule(bool allow, FirewallProtocol p,
+ FirewallDirection d, const SocketAddress& addr) {
+ CritScope scope(&crit_);
+ for (std::vector<FirewallSocketServer*>::const_iterator it =
+ servers_.begin(); it != servers_.end(); ++it) {
+ (*it)->AddRule(allow, p, d, addr);
+ }
+}
+
+void FirewallManager::ClearRules() {
+ CritScope scope(&crit_);
+ for (std::vector<FirewallSocketServer*>::const_iterator it =
+ servers_.begin(); it != servers_.end(); ++it) {
+ (*it)->ClearRules();
+ }
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/firewallsocketserver.h b/third_party/libjingle/source/talk/base/firewallsocketserver.h
new file mode 100644
index 0000000..94ba2d2
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/firewallsocketserver.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 TALK_BASE_FIREWALLSOCKETSERVER_H_
+#define TALK_BASE_FIREWALLSOCKETSERVER_H_
+
+#include <vector>
+#include "talk/base/socketserver.h"
+#include "talk/base/criticalsection.h"
+
+namespace talk_base {
+
+class FirewallManager;
+
+// This SocketServer shim simulates a rule-based firewall server.
+
+enum FirewallProtocol { FP_UDP, FP_TCP, FP_ANY };
+enum FirewallDirection { FD_IN, FD_OUT, FD_ANY };
+
+class FirewallSocketServer : public SocketServer {
+ public:
+ FirewallSocketServer(SocketServer * server,
+ FirewallManager * manager = NULL,
+ bool should_delete_server = false);
+ virtual ~FirewallSocketServer();
+
+ SocketServer* socketserver() const { return server_; }
+ void set_socketserver(SocketServer* server) {
+ if (server_ && should_delete_server_) {
+ delete server_;
+ server_ = NULL;
+ should_delete_server_ = false;
+ }
+ server_ = server;
+ }
+
+ // Settings to control whether CreateSocket or Socket::Listen succeed.
+ void set_udp_sockets_enabled(bool enabled) { udp_sockets_enabled_ = enabled; }
+ void set_tcp_sockets_enabled(bool enabled) { tcp_sockets_enabled_ = enabled; }
+ bool tcp_listen_enabled() const { return tcp_listen_enabled_; }
+ void set_tcp_listen_enabled(bool enabled) { tcp_listen_enabled_ = enabled; }
+
+ // Rules govern the behavior of Connect/Accept/Send/Recv attempts.
+ void AddRule(bool allow, FirewallProtocol p = FP_ANY,
+ FirewallDirection d = FD_ANY,
+ const SocketAddress& addr = SocketAddress());
+ void AddRule(bool allow, FirewallProtocol p,
+ const SocketAddress& src, const SocketAddress& dst);
+ void ClearRules();
+
+ bool Check(FirewallProtocol p,
+ const SocketAddress& src, const SocketAddress& dst);
+
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+ virtual void SetMessageQueue(MessageQueue* queue) {
+ server_->SetMessageQueue(queue);
+ }
+ virtual bool Wait(int cms, bool process_io) {
+ return server_->Wait(cms, process_io);
+ }
+ virtual void WakeUp() {
+ return server_->WakeUp();
+ }
+
+ Socket * WrapSocket(Socket * sock, int type);
+ AsyncSocket * WrapSocket(AsyncSocket * sock, int type);
+
+ private:
+ SocketServer * server_;
+ FirewallManager * manager_;
+ CriticalSection crit_;
+ struct Rule {
+ bool allow;
+ FirewallProtocol p;
+ FirewallDirection d;
+ SocketAddress src;
+ SocketAddress dst;
+ };
+ std::vector<Rule> rules_;
+ bool should_delete_server_;
+ bool udp_sockets_enabled_;
+ bool tcp_sockets_enabled_;
+ bool tcp_listen_enabled_;
+};
+
+// FirewallManager allows you to manage firewalls in multiple threads together
+
+class FirewallManager {
+ public:
+ FirewallManager();
+ ~FirewallManager();
+
+ void AddServer(FirewallSocketServer * server);
+ void RemoveServer(FirewallSocketServer * server);
+
+ void AddRule(bool allow, FirewallProtocol p = FP_ANY,
+ FirewallDirection d = FD_ANY,
+ const SocketAddress& addr = SocketAddress());
+ void ClearRules();
+
+ private:
+ CriticalSection crit_;
+ std::vector<FirewallSocketServer *> servers_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_FIREWALLSOCKETSERVER_H_
diff --git a/third_party/libjingle/source/talk/base/flags.cc b/third_party/libjingle/source/talk/base/flags.cc
new file mode 100644
index 0000000..09a8edf
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/flags.cc
@@ -0,0 +1,324 @@
+/*
+ * libjingle
+ * Copyright 2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#include <shellapi.h>
+#endif
+
+#include "talk/base/flags.h"
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Flag
+
+Flag::Flag(const char* file, const char* name, const char* comment,
+ Type type, void* variable, FlagValue default__)
+ : file_(file),
+ name_(name),
+ comment_(comment),
+ type_(type),
+ variable_(reinterpret_cast<FlagValue*>(variable)),
+ default_(default__) {
+ FlagList::Register(this);
+}
+
+
+void Flag::SetToDefault() {
+ // Note that we cannot simply do '*variable_ = default_;' since
+ // flag variables are not really of type FlagValue and thus may
+ // be smaller! The FlagValue union is simply 'overlayed' on top
+ // of a flag variable for convenient access. Since union members
+ // are guarantee to be aligned at the beginning, this works.
+ switch (type_) {
+ case Flag::BOOL:
+ variable_->b = default_.b;
+ return;
+ case Flag::INT:
+ variable_->i = default_.i;
+ return;
+ case Flag::FLOAT:
+ variable_->f = default_.f;
+ return;
+ case Flag::STRING:
+ variable_->s = default_.s;
+ return;
+ }
+ UNREACHABLE();
+}
+
+
+static const char* Type2String(Flag::Type type) {
+ switch (type) {
+ case Flag::BOOL: return "bool";
+ case Flag::INT: return "int";
+ case Flag::FLOAT: return "float";
+ case Flag::STRING: return "string";
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+static void PrintFlagValue(Flag::Type type, FlagValue* p) {
+ switch (type) {
+ case Flag::BOOL:
+ printf("%s", (p->b ? "true" : "false"));
+ return;
+ case Flag::INT:
+ printf("%d", p->i);
+ return;
+ case Flag::FLOAT:
+ printf("%f", p->f);
+ return;
+ case Flag::STRING:
+ printf("%s", p->s);
+ return;
+ }
+ UNREACHABLE();
+}
+
+
+void Flag::Print(bool print_current_value) {
+ printf(" --%s (%s) type: %s default: ", name_, comment_,
+ Type2String(type_));
+ PrintFlagValue(type_, &default_);
+ if (print_current_value) {
+ printf(" current value: ");
+ PrintFlagValue(type_, variable_);
+ }
+ printf("\n");
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of FlagList
+
+Flag* FlagList::list_ = NULL;
+
+
+FlagList::FlagList() {
+ list_ = NULL;
+}
+
+void FlagList::Print(const char* file, bool print_current_value) {
+ // Since flag registration is likely by file (= C++ file),
+ // we don't need to sort by file and still get grouped output.
+ const char* current = NULL;
+ for (Flag* f = list_; f != NULL; f = f->next()) {
+ if (file == NULL || file == f->file()) {
+ if (current != f->file()) {
+ printf("Flags from %s:\n", f->file());
+ current = f->file();
+ }
+ f->Print(print_current_value);
+ }
+ }
+}
+
+
+Flag* FlagList::Lookup(const char* name) {
+ Flag* f = list_;
+ while (f != NULL && strcmp(name, f->name()) != 0)
+ f = f->next();
+ return f;
+}
+
+
+void FlagList::SplitArgument(const char* arg,
+ char* buffer, int buffer_size,
+ const char** name, const char** value,
+ bool* is_bool) {
+ *name = NULL;
+ *value = NULL;
+ *is_bool = false;
+
+ if (*arg == '-') {
+ // find the begin of the flag name
+ arg++; // remove 1st '-'
+ if (*arg == '-')
+ arg++; // remove 2nd '-'
+ if (arg[0] == 'n' && arg[1] == 'o') {
+ arg += 2; // remove "no"
+ *is_bool = true;
+ }
+ *name = arg;
+
+ // find the end of the flag name
+ while (*arg != '\0' && *arg != '=')
+ arg++;
+
+ // get the value if any
+ if (*arg == '=') {
+ // make a copy so we can NUL-terminate flag name
+ int n = arg - *name;
+ if (n >= buffer_size)
+ Fatal(__FILE__, __LINE__, "CHECK(%s) failed", "n < buffer_size");
+ memcpy(buffer, *name, n * sizeof(char));
+ buffer[n] = '\0';
+ *name = buffer;
+ // get the value
+ *value = arg + 1;
+ }
+ }
+}
+
+
+int FlagList::SetFlagsFromCommandLine(int* argc, const char** argv,
+ bool remove_flags) {
+ // parse arguments
+ for (int i = 1; i < *argc; /* see below */) {
+ int j = i; // j > 0
+ const char* arg = argv[i++];
+
+ // split arg into flag components
+ char buffer[1024];
+ const char* name;
+ const char* value;
+ bool is_bool;
+ SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);
+
+ if (name != NULL) {
+ // lookup the flag
+ Flag* flag = Lookup(name);
+ if (flag == NULL) {
+ fprintf(stderr, "Error: unrecognized flag %s\n", arg);
+ return j;
+ }
+
+ // if we still need a flag value, use the next argument if available
+ if (flag->type() != Flag::BOOL && value == NULL) {
+ if (i < *argc) {
+ value = argv[i++];
+ } else {
+ fprintf(stderr, "Error: missing value for flag %s of type %s\n",
+ arg, Type2String(flag->type()));
+ return j;
+ }
+ }
+
+ // set the flag
+ char empty[] = { '\0' };
+ char* endp = empty;
+ switch (flag->type()) {
+ case Flag::BOOL:
+ *flag->bool_variable() = !is_bool;
+ break;
+ case Flag::INT:
+ *flag->int_variable() = strtol(value, &endp, 10);
+ break;
+ case Flag::FLOAT:
+ *flag->float_variable() = strtod(value, &endp);
+ break;
+ case Flag::STRING:
+ *flag->string_variable() = value;
+ break;
+ }
+
+ // handle errors
+ if ((flag->type() == Flag::BOOL && value != NULL) ||
+ (flag->type() != Flag::BOOL && is_bool) ||
+ *endp != '\0') {
+ fprintf(stderr, "Error: illegal value for flag %s of type %s\n",
+ arg, Type2String(flag->type()));
+ return j;
+ }
+
+ // remove the flag & value from the command
+ if (remove_flags)
+ while (j < i)
+ argv[j++] = NULL;
+ }
+ }
+
+ // shrink the argument list
+ if (remove_flags) {
+ int j = 1;
+ for (int i = 1; i < *argc; i++) {
+ if (argv[i] != NULL)
+ argv[j++] = argv[i];
+ }
+ *argc = j;
+ }
+
+ // parsed all flags successfully
+ return 0;
+}
+
+void FlagList::Register(Flag* flag) {
+ assert(flag != NULL && strlen(flag->name()) > 0);
+ if (Lookup(flag->name()) != NULL)
+ Fatal(flag->file(), 0, "flag %s declared twice", flag->name());
+ flag->next_ = list_;
+ list_ = flag;
+}
+
+#ifdef WIN32
+WindowsCommandLineArguments::WindowsCommandLineArguments() {
+ // start by getting the command line.
+ LPTSTR command_line = ::GetCommandLine();
+ // now, convert it to a list of wide char strings.
+ LPWSTR *wide_argv = ::CommandLineToArgvW(command_line, &argc_);
+ // now allocate an array big enough to hold that many string pointers.
+ argv_ = new char*[argc_];
+
+ // iterate over the returned wide strings;
+ for(int i = 0; i < argc_; ++i) {
+ // for each, create a char buffer big enough to hold it; so, find out
+ // how much space we need.
+ int len8 = WideCharToMultiByte(CP_UTF8, 0, wide_argv[i],
+ wcslen(wide_argv[i]), NULL, 0,
+ NULL, NULL);
+ // then allocate the buffer...
+ char *buffer = new char[1 + len8]; // +1 for trailing \0
+ // and do the conversion.
+ WideCharToMultiByte(CP_UTF8, 0, wide_argv[i],
+ wcslen(wide_argv[i]), buffer, len8,
+ NULL, NULL);
+ // WideCharToMultibyte doesn't give us a trailing \0, so we add it.
+ buffer[len8] = '\0';
+ // make sure the argv array has the right string at this point.
+ argv_[i] = buffer;
+ }
+ LocalFree(wide_argv);
+}
+
+WindowsCommandLineArguments::~WindowsCommandLineArguments() {
+ // need to free each string in the array, and then the array.
+ for(int i = 0; i < argc_; i++) {
+ delete[] argv_[i];
+ }
+
+ delete[] argv_;
+}
+#endif // WIN32
+
diff --git a/third_party/libjingle/source/talk/base/flags.h b/third_party/libjingle/source/talk/base/flags.h
new file mode 100644
index 0000000..a6eee00
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/flags.h
@@ -0,0 +1,281 @@
+/*
+ * libjingle
+ * Copyright 2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Flags are defined and declared using DEFINE_xxx and DECLARE_xxx macros,
+// where xxx is the flag type. Flags are referred to via FLAG_yyy,
+// where yyy is the flag name. For intialization and iteration of flags,
+// see the FlagList class. For full programmatic access to any
+// flag, see the Flag class.
+//
+// The implementation only relies and basic C++ functionality
+// and needs no special library or STL support.
+
+#ifndef TALK_BASE_FLAGS_H__
+#define TALK_BASE_FLAGS_H__
+
+#include <assert.h>
+
+#include "talk/base/checks.h"
+#include "talk/base/common.h"
+
+// Internal use only.
+union FlagValue {
+ // Note: Because in C++ non-bool values are silently converted into
+ // bool values ('bool b = "false";' results in b == true!), we pass
+ // and int argument to New_BOOL as this appears to be safer - sigh.
+ // In particular, it prevents the (not uncommon!) bug where a bool
+ // flag is defined via: DEFINE_bool(flag, "false", "some comment");.
+ static FlagValue New_BOOL(int b) {
+ FlagValue v;
+ v.b = (b != 0);
+ return v;
+ }
+
+ static FlagValue New_INT(int i) {
+ FlagValue v;
+ v.i = i;
+ return v;
+ }
+
+ static FlagValue New_FLOAT(float f) {
+ FlagValue v;
+ v.f = f;
+ return v;
+ }
+
+ static FlagValue New_STRING(const char* s) {
+ FlagValue v;
+ v.s = s;
+ return v;
+ }
+
+ bool b;
+ int i;
+ double f;
+ const char* s;
+};
+
+
+// Each flag can be accessed programmatically via a Flag object.
+class Flag {
+ public:
+ enum Type { BOOL, INT, FLOAT, STRING };
+
+ // Internal use only.
+ Flag(const char* file, const char* name, const char* comment,
+ Type type, void* variable, FlagValue default_);
+
+ // General flag information
+ const char* file() const { return file_; }
+ const char* name() const { return name_; }
+ const char* comment() const { return comment_; }
+
+ // Flag type
+ Type type() const { return type_; }
+
+ // Flag variables
+ bool* bool_variable() const {
+ assert(type_ == BOOL);
+ return &variable_->b;
+ }
+
+ int* int_variable() const {
+ assert(type_ == INT);
+ return &variable_->i;
+ }
+
+ double* float_variable() const {
+ assert(type_ == FLOAT);
+ return &variable_->f;
+ }
+
+ const char** string_variable() const {
+ assert(type_ == STRING);
+ return &variable_->s;
+ }
+
+ // Default values
+ bool bool_default() const {
+ assert(type_ == BOOL);
+ return default_.b;
+ }
+
+ int int_default() const {
+ assert(type_ == INT);
+ return default_.i;
+ }
+
+ double float_default() const {
+ assert(type_ == FLOAT);
+ return default_.f;
+ }
+
+ const char* string_default() const {
+ assert(type_ == STRING);
+ return default_.s;
+ }
+
+ // Resets a flag to its default value
+ void SetToDefault();
+
+ // Iteration support
+ Flag* next() const { return next_; }
+
+ // Prints flag information. The current flag value is only printed
+ // if print_current_value is set.
+ void Print(bool print_current_value);
+
+ private:
+ const char* file_;
+ const char* name_;
+ const char* comment_;
+
+ Type type_;
+ FlagValue* variable_;
+ FlagValue default_;
+
+ Flag* next_;
+
+ friend class FlagList; // accesses next_
+};
+
+
+// Internal use only.
+#define DEFINE_FLAG(type, c_type, name, default, comment) \
+ /* define and initialize the flag */ \
+ c_type FLAG_##name = (default); \
+ /* register the flag */ \
+ static Flag Flag_##name(__FILE__, #name, (comment), \
+ Flag::type, &FLAG_##name, \
+ FlagValue::New_##type(default))
+
+
+// Internal use only.
+#define DECLARE_FLAG(c_type, name) \
+ /* declare the external flag */ \
+ extern c_type FLAG_##name
+
+
+// Use the following macros to define a new flag:
+#define DEFINE_bool(name, default, comment) \
+ DEFINE_FLAG(BOOL, bool, name, default, comment)
+#define DEFINE_int(name, default, comment) \
+ DEFINE_FLAG(INT, int, name, default, comment)
+#define DEFINE_float(name, default, comment) \
+ DEFINE_FLAG(FLOAT, double, name, default, comment)
+#define DEFINE_string(name, default, comment) \
+ DEFINE_FLAG(STRING, const char*, name, default, comment)
+
+
+// Use the following macros to declare a flag defined elsewhere:
+#define DECLARE_bool(name) DECLARE_FLAG(bool, name)
+#define DECLARE_int(name) DECLARE_FLAG(int, name)
+#define DECLARE_float(name) DECLARE_FLAG(double, name)
+#define DECLARE_string(name) DECLARE_FLAG(const char*, name)
+
+
+// The global list of all flags.
+class FlagList {
+ public:
+ FlagList();
+
+ // The NULL-terminated list of all flags. Traverse with Flag::next().
+ static Flag* list() { return list_; }
+
+ // If file != NULL, prints information for all flags defined in file;
+ // otherwise prints information for all flags in all files. The current
+ // flag value is only printed if print_current_value is set.
+ static void Print(const char* file, bool print_current_value);
+
+ // Lookup a flag by name. Returns the matching flag or NULL.
+ static Flag* Lookup(const char* name);
+
+ // Helper function to parse flags: Takes an argument arg and splits it into
+ // a flag name and flag value (or NULL if they are missing). is_bool is set
+ // if the arg started with "-no" or "--no". The buffer may be used to NUL-
+ // terminate the name, it must be large enough to hold any possible name.
+ static void SplitArgument(const char* arg,
+ char* buffer, int buffer_size,
+ const char** name, const char** value,
+ bool* is_bool);
+
+ // Set the flag values by parsing the command line. If remove_flags
+ // is set, the flags and associated values are removed from (argc,
+ // argv). Returns 0 if no error occurred. Otherwise, returns the
+ // argv index > 0 for the argument where an error occurred. In that
+ // case, (argc, argv) will remain unchanged indepdendent of the
+ // remove_flags value, and no assumptions about flag settings should
+ // be made.
+ //
+ // The following syntax for flags is accepted (both '-' and '--' are ok):
+ //
+ // --flag (bool flags only)
+ // --noflag (bool flags only)
+ // --flag=value (non-bool flags only, no spaces around '=')
+ // --flag value (non-bool flags only)
+ static int SetFlagsFromCommandLine(int* argc,
+ const char** argv,
+ bool remove_flags);
+ static inline int SetFlagsFromCommandLine(int* argc,
+ char** argv,
+ bool remove_flags) {
+ return SetFlagsFromCommandLine(argc, const_cast<const char**>(argv),
+ remove_flags);
+ }
+
+ // Registers a new flag. Called during program initialization. Not
+ // thread-safe.
+ static void Register(Flag* flag);
+
+ private:
+ static Flag* list_;
+};
+
+#ifdef WIN32
+// A helper class to translate Windows command line arguments into UTF8,
+// which then allows us to just pass them to the flags system.
+// This encapsulates all the work of getting the command line and translating
+// it to an array of 8-bit strings; all you have to do is create one of these,
+// and then call argc() and argv().
+class WindowsCommandLineArguments {
+ public:
+ WindowsCommandLineArguments();
+ ~WindowsCommandLineArguments();
+
+ int argc() { return argc_; }
+ char **argv() { return argv_; }
+ private:
+ int argc_;
+ char **argv_;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(WindowsCommandLineArguments);
+};
+#endif // WIN32
+
+
+#endif // SHARED_COMMANDLINEFLAGS_FLAGS_H__
diff --git a/third_party/libjingle/source/talk/base/helpers.cc b/third_party/libjingle/source/talk/base/helpers.cc
new file mode 100644
index 0000000..ff38db0
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/helpers.cc
@@ -0,0 +1,249 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/helpers.h"
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <ntsecapi.h>
+#else
+#ifdef USE_OPENSSL
+#include <openssl/rand.h>
+#endif
+#endif
+
+#include "talk/base/base64.h"
+#include "talk/base/logging.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/time.h"
+
+namespace talk_base {
+
+// Base class for RNG implementations.
+class RandomGenerator {
+ public:
+ virtual ~RandomGenerator() {}
+ virtual bool Init(const void* seed, size_t len) = 0;
+ virtual bool Generate(void* buf, size_t len) = 0;
+};
+
+// The real random generators, using either CryptoAPI or OpenSSL.
+// We also support the 'old' generator on Mac/Linux until we have time to
+// fully test the OpenSSL one.
+#ifdef WIN32
+class SecureRandomGenerator : public RandomGenerator {
+ public:
+ SecureRandomGenerator() : advapi32_(NULL), rtl_gen_random_(NULL) {}
+ ~SecureRandomGenerator() {
+ FreeLibrary(advapi32_);
+ }
+
+ virtual bool Init(const void* seed, size_t seed_len) {
+ // We don't do any additional seeding on Win32, we just use the CryptoAPI
+ // RNG (which is exposed as a hidden function off of ADVAPI32 so that we
+ // don't need to drag in all of CryptoAPI)
+ if (rtl_gen_random_) {
+ return true;
+ }
+
+ advapi32_ = LoadLibrary(L"advapi32.dll");
+ if (!advapi32_) {
+ return false;
+ }
+
+ rtl_gen_random_ = reinterpret_cast<RtlGenRandomProc>(
+ GetProcAddress(advapi32_, "SystemFunction036"));
+ if (!rtl_gen_random_) {
+ FreeLibrary(advapi32_);
+ return false;
+ }
+
+ return true;
+ }
+ virtual bool Generate(void* buf, size_t len) {
+ if (!rtl_gen_random_ && !Init(NULL, 0)) {
+ return false;
+ }
+ return (rtl_gen_random_(buf, len) != FALSE);
+ }
+
+ private:
+ typedef BOOL (WINAPI *RtlGenRandomProc)(PVOID, ULONG);
+ HINSTANCE advapi32_;
+ RtlGenRandomProc rtl_gen_random_;
+};
+#else
+#ifndef USE_OPENSSL
+// The old RNG.
+class SecureRandomGenerator : public RandomGenerator {
+ public:
+ SecureRandomGenerator() : seed_(1) {
+ }
+ ~SecureRandomGenerator() {
+ }
+ virtual bool Init(const void* seed, size_t len) {
+ uint32 hash = 0;
+ for (size_t i = 0; i < len; ++i) {
+ hash = ((hash << 2) + hash) + static_cast<const char*>(seed)[i];
+ }
+
+ seed_ = Time() ^ hash;
+ return true;
+ }
+ virtual bool Generate(void* buf, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ static_cast<uint8*>(buf)[i] = static_cast<uint8>(GetRandom());
+ }
+ return true;
+ }
+
+ private:
+ int GetRandom() {
+ return ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff;
+ }
+ int seed_;
+};
+#else
+// The OpenSSL RNG. Need to make sure it doesn't run out of entropy.
+class SecureRandomGenerator : public RandomGenerator {
+ public:
+ SecureRandomGenerator() : inited_(false) {
+ }
+ ~SecureRandomGenerator() {
+ }
+ virtual bool Init(const void* seed, size_t len) {
+ // By default, seed from the system state.
+ if (!inited_) {
+ if (RAND_poll() != 0) {
+ return false;
+ }
+ inited_ = true;
+ }
+ // Allow app data to be mixed in, if provided.
+ if (seed) {
+ RAND_add(seed, len);
+ }
+ return true;
+ }
+ virtual bool Generate(void* buf, size_t len) {
+ if (!inited_ && !Init(NULL, 0)) {
+ return false;
+ }
+ return (RAND_bytes(buf, len) == 0);
+ }
+
+ private:
+ bool inited_;
+};
+#endif // USE_OPENSSL
+#endif // WIN32
+
+// A test random generator, for predictable output.
+class TestRandomGenerator : public RandomGenerator {
+ public:
+ TestRandomGenerator() : seed_(7) {
+ }
+ ~TestRandomGenerator() {
+ }
+ virtual bool Init(const void* seed, size_t len) {
+ return true;
+ }
+ virtual bool Generate(void* buf, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ static_cast<uint8*>(buf)[i] = static_cast<uint8>(GetRandom());
+ }
+ return true;
+ }
+
+ private:
+ int GetRandom() {
+ return ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff;
+ }
+ int seed_;
+};
+
+// TODO(juberti): Use Base64::Base64Table instead.
+static 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', '+', '/'
+};
+
+static scoped_ptr<RandomGenerator> g_rng(new SecureRandomGenerator());
+
+void SetRandomTestMode(bool test) {
+ if (!test) {
+ g_rng.reset(new SecureRandomGenerator());
+ } else {
+ g_rng.reset(new TestRandomGenerator());
+ }
+}
+
+bool InitRandom(int seed) {
+ return InitRandom(reinterpret_cast<const char*>(&seed), sizeof(seed));
+}
+
+bool InitRandom(const char* seed, size_t len) {
+ if (!g_rng->Init(seed, len)) {
+ LOG(LS_ERROR) << "Failed to init random generator!";
+ return false;
+ }
+ return true;
+}
+
+std::string CreateRandomString(size_t len) {
+ std::string str;
+ scoped_array<uint8> bytes(new uint8[len]);
+ if (!g_rng->Generate(bytes.get(), len)) {
+ LOG(LS_ERROR) << "Failed to generate random string!";
+ }
+ for (size_t i = 0; i < len; i++) {
+ str.push_back(BASE64[bytes[i] & 63]);
+ }
+ return str;
+}
+
+uint32 CreateRandomId() {
+ uint32 id;
+ if (!g_rng->Generate(&id, sizeof(id))) {
+ LOG(LS_ERROR) << "Failed to generate random id!";
+ }
+ return id;
+}
+
+uint32 CreateRandomNonZeroId() {
+ uint32 id;
+ do {
+ id = CreateRandomId();
+ } while (id == 0);
+ return id;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/helpers.h b/third_party/libjingle/source/talk/base/helpers.h
new file mode 100644
index 0000000..878efc1
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/helpers.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_BASE_HELPERS_H_
+#define TALK_BASE_HELPERS_H_
+
+#include <string>
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+// For testing, we can return predictable data.
+void SetRandomTestMode(bool test);
+
+// Initializes the RNG, and seeds it with the specified entropy.
+bool InitRandom(int seed);
+bool InitRandom(const char* seed, size_t len);
+
+// Generates a (cryptographically) random string of the given length.
+// We generate base64 values so that they will be printable.
+std::string CreateRandomString(size_t length);
+
+// Generates a random id.
+uint32 CreateRandomId();
+
+// Generates a random id > 0.
+uint32 CreateRandomNonZeroId();
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HELPERS_H_
diff --git a/third_party/libjingle/source/talk/base/host.cc b/third_party/libjingle/source/talk/base/host.cc
new file mode 100644
index 0000000..51c37d2
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/host.cc
@@ -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.
+ */
+
+#include "talk/base/host.h"
+
+#ifdef POSIX
+#include <sys/utsname.h>
+#endif // POSIX
+
+#include <string>
+
+namespace talk_base {
+
+std::string GetHostName() {
+ // TODO(juberti): fix or get rid of this
+#if 0
+ struct utsname nm;
+ if (uname(&nm) < 0)
+ FatalError("uname", LAST_SYSTEM_ERROR);
+ return std::string(nm.nodename);
+#endif
+ return "cricket";
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/host.h b/third_party/libjingle/source/talk/base/host.h
new file mode 100644
index 0000000..8528240
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/host.h
@@ -0,0 +1,40 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_HOST_H_
+#define TALK_BASE_HOST_H_
+
+#include <string>
+
+namespace talk_base {
+
+// Returns the name of the local host.
+std::string GetHostName();
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HOST_H_
diff --git a/third_party/libjingle/source/talk/base/httpbase.cc b/third_party/libjingle/source/talk/base/httpbase.cc
new file mode 100644
index 0000000..ac90a30
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httpbase.cc
@@ -0,0 +1,895 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Author: <bpm@google.com> (Brian McBarron)
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#else // !WIN32
+#define SEC_E_CERT_EXPIRED (-2146893016)
+#endif // !WIN32
+
+#include "talk/base/common.h"
+#include "talk/base/httpbase.h"
+#include "talk/base/logging.h"
+#include "talk/base/socket.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/thread.h"
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Helpers
+//////////////////////////////////////////////////////////////////////
+
+bool MatchHeader(const char* str, size_t len, HttpHeader header) {
+ const char* const header_str = ToString(header);
+ const size_t header_len = strlen(header_str);
+ return (len == header_len) && (_strnicmp(str, header_str, header_len) == 0);
+}
+
+enum {
+ MSG_READ
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpParser
+//////////////////////////////////////////////////////////////////////
+
+HttpParser::HttpParser() {
+ reset();
+}
+
+HttpParser::~HttpParser() {
+}
+
+void
+HttpParser::reset() {
+ state_ = ST_LEADER;
+ chunked_ = false;
+ data_size_ = SIZE_UNKNOWN;
+}
+
+HttpParser::ProcessResult
+HttpParser::Process(const char* buffer, size_t len, size_t* processed,
+ HttpError* error) {
+ *processed = 0;
+ *error = HE_NONE;
+
+ if (state_ >= ST_COMPLETE) {
+ ASSERT(false);
+ return PR_COMPLETE;
+ }
+
+ while (true) {
+ if (state_ < ST_DATA) {
+ size_t pos = *processed;
+ while ((pos < len) && (buffer[pos] != '\n')) {
+ pos += 1;
+ }
+ if (pos >= len) {
+ break; // don't have a full header
+ }
+ const char* line = buffer + *processed;
+ size_t len = (pos - *processed);
+ *processed = pos + 1;
+ while ((len > 0) && isspace(static_cast<unsigned char>(line[len-1]))) {
+ len -= 1;
+ }
+ ProcessResult result = ProcessLine(line, len, error);
+ LOG(LS_VERBOSE) << "Processed line, result=" << result;
+
+ if (PR_CONTINUE != result) {
+ return result;
+ }
+ } else if (data_size_ == 0) {
+ if (chunked_) {
+ state_ = ST_CHUNKTERM;
+ } else {
+ return PR_COMPLETE;
+ }
+ } else {
+ size_t available = len - *processed;
+ if (available <= 0) {
+ break; // no more data
+ }
+ if ((data_size_ != SIZE_UNKNOWN) && (available > data_size_)) {
+ available = data_size_;
+ }
+ size_t read = 0;
+ ProcessResult result = ProcessData(buffer + *processed, available, read,
+ error);
+ LOG(LS_VERBOSE) << "Processed data, result: " << result << " read: "
+ << read << " error: " << error;
+
+ if (PR_CONTINUE != result) {
+ return result;
+ }
+ *processed += read;
+ if (data_size_ != SIZE_UNKNOWN) {
+ data_size_ -= read;
+ }
+ }
+ }
+
+ return PR_CONTINUE;
+}
+
+HttpParser::ProcessResult
+HttpParser::ProcessLine(const char* line, size_t len, HttpError* error) {
+ LOG_F(LS_VERBOSE) << " state: " << state_ << " line: "
+ << std::string(line, len) << " len: " << len << " error: "
+ << error;
+
+ switch (state_) {
+ case ST_LEADER:
+ state_ = ST_HEADERS;
+ return ProcessLeader(line, len, error);
+
+ case ST_HEADERS:
+ if (len > 0) {
+ const char* value = strchrn(line, len, ':');
+ if (!value) {
+ *error = HE_PROTOCOL;
+ return PR_COMPLETE;
+ }
+ size_t nlen = (value - line);
+ const char* eol = line + len;
+ do {
+ value += 1;
+ } while ((value < eol) && isspace(static_cast<unsigned char>(*value)));
+ size_t vlen = eol - value;
+ if (MatchHeader(line, nlen, HH_CONTENT_LENGTH)) {
+ unsigned int temp_size;
+ if (sscanf(value, "%u", &temp_size) != 1) {
+ *error = HE_PROTOCOL;
+ return PR_COMPLETE;
+ }
+ data_size_ = static_cast<size_t>(temp_size);
+ } else if (MatchHeader(line, nlen, HH_TRANSFER_ENCODING)) {
+ if ((vlen == 7) && (_strnicmp(value, "chunked", 7) == 0)) {
+ chunked_ = true;
+ } else if ((vlen == 8) && (_strnicmp(value, "identity", 8) == 0)) {
+ chunked_ = false;
+ } else {
+ *error = HE_PROTOCOL;
+ return PR_COMPLETE;
+ }
+ }
+ return ProcessHeader(line, nlen, value, vlen, error);
+ } else {
+ state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA;
+ return ProcessHeaderComplete(chunked_, data_size_, error);
+ }
+ break;
+
+ case ST_CHUNKSIZE:
+ if (len > 0) {
+ char* ptr = NULL;
+ data_size_ = strtoul(line, &ptr, 16);
+ if (ptr != line + len) {
+ *error = HE_PROTOCOL;
+ return PR_COMPLETE;
+ }
+ state_ = (data_size_ == 0) ? ST_TRAILERS : ST_DATA;
+ } else {
+ *error = HE_PROTOCOL;
+ return PR_COMPLETE;
+ }
+ break;
+
+ case ST_CHUNKTERM:
+ if (len > 0) {
+ *error = HE_PROTOCOL;
+ return PR_COMPLETE;
+ } else {
+ state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA;
+ }
+ break;
+
+ case ST_TRAILERS:
+ if (len == 0) {
+ return PR_COMPLETE;
+ }
+ // *error = onHttpRecvTrailer();
+ break;
+
+ default:
+ ASSERT(false);
+ break;
+ }
+
+ return PR_CONTINUE;
+}
+
+bool
+HttpParser::is_valid_end_of_input() const {
+ return (state_ == ST_DATA) && (data_size_ == SIZE_UNKNOWN);
+}
+
+void
+HttpParser::complete(HttpError error) {
+ if (state_ < ST_COMPLETE) {
+ state_ = ST_COMPLETE;
+ OnComplete(error);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpBase::DocumentStream
+//////////////////////////////////////////////////////////////////////
+
+class BlockingMemoryStream : public ExternalMemoryStream {
+public:
+ BlockingMemoryStream(char* buffer, size_t size)
+ : ExternalMemoryStream(buffer, size) { }
+
+ virtual StreamResult DoReserve(size_t size, int* error) {
+ return (buffer_length_ >= size) ? SR_SUCCESS : SR_BLOCK;
+ }
+};
+
+class HttpBase::DocumentStream : public StreamInterface {
+public:
+ DocumentStream(HttpBase* base) : base_(base), error_(HE_DEFAULT) { }
+
+ virtual StreamState GetState() const {
+ if (NULL == base_)
+ return SS_CLOSED;
+ if (HM_RECV == base_->mode_)
+ return SS_OPEN;
+ return SS_OPENING;
+ }
+
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ if (!base_) {
+ if (error) *error = error_;
+ return (HE_NONE == error_) ? SR_EOS : SR_ERROR;
+ }
+
+ if (HM_RECV != base_->mode_) {
+ return SR_BLOCK;
+ }
+
+ // DoReceiveLoop writes http document data to the StreamInterface* document
+ // member of HttpData. In this case, we want this data to be written
+ // directly to our buffer. To accomplish this, we wrap our buffer with a
+ // StreamInterface, and replace the existing document with our wrapper.
+ // When the method returns, we restore the old document. Ideally, we would
+ // pass our StreamInterface* to DoReceiveLoop, but due to the callbacks
+ // of HttpParser, we would still need to store the pointer temporarily.
+
+ BlockingMemoryStream stream(reinterpret_cast<char*>(buffer), buffer_len);
+
+ // Replace the existing document with our wrapped buffer.
+ StreamInterface* prior_stream = base_->data_->document.release();
+ base_->data_->document.reset(&stream);
+
+ // Pump the I/O loop. DoReceiveLoop is guaranteed not to attempt to
+ // complete the I/O process, which means that our wrapper is not in danger
+ // of being deleted. To ensure this, DoReceiveLoop returns true when it
+ // wants complete to be called. We make sure to uninstall our wrapper
+ // before calling complete().
+ HttpError http_error;
+ bool complete = base_->DoReceiveLoop(&http_error);
+
+ // Reinstall the original output document.
+ base_->data_->document.release();
+ base_->data_->document.reset(prior_stream);
+
+ // If we reach the end of the receive stream, we disconnect our stream
+ // adapter from the HttpBase, and further calls to read will either return
+ // EOS or ERROR, appropriately. Finally, we call complete().
+ StreamResult result = SR_BLOCK;
+ if (complete) {
+ HttpBase* base = Disconnect(http_error);
+ if (error) *error = error_;
+ result = (HE_NONE == error_) ? SR_EOS : SR_ERROR;
+ base->complete(http_error);
+ }
+
+ // Even if we are complete, if some data was read we must return SUCCESS.
+ // Future Reads will return EOS or ERROR based on the error_ variable.
+ size_t position;
+ stream.GetPosition(&position);
+ if (position > 0) {
+ if (read) *read = position;
+ result = SR_SUCCESS;
+ }
+ return result;
+ }
+
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (error) *error = -1;
+ return SR_ERROR;
+ }
+
+ virtual void Close() {
+ if (base_) {
+ HttpBase* base = Disconnect(HE_NONE);
+ if (HM_RECV == base->mode_ && base->http_stream_) {
+ // Read I/O could have been stalled on the user of this DocumentStream,
+ // so restart the I/O process now that we've removed ourselves.
+ base->http_stream_->PostEvent(SE_READ, 0);
+ }
+ }
+ }
+
+ virtual bool GetAvailable(size_t* size) const {
+ if (!base_ || HM_RECV != base_->mode_)
+ return false;
+ size_t data_size = base_->GetDataRemaining();
+ if (SIZE_UNKNOWN == data_size)
+ return false;
+ if (size)
+ *size = data_size;
+ return true;
+ }
+
+ HttpBase* Disconnect(HttpError error) {
+ ASSERT(NULL != base_);
+ ASSERT(NULL != base_->doc_stream_);
+ HttpBase* base = base_;
+ base_->doc_stream_ = NULL;
+ base_ = NULL;
+ error_ = error;
+ return base;
+ }
+
+private:
+ HttpBase* base_;
+ HttpError error_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpBase
+//////////////////////////////////////////////////////////////////////
+
+HttpBase::HttpBase() : mode_(HM_NONE), data_(NULL), notify_(NULL),
+ http_stream_(NULL), doc_stream_(NULL) {
+}
+
+HttpBase::~HttpBase() {
+ ASSERT(HM_NONE == mode_);
+}
+
+bool
+HttpBase::isConnected() const {
+ return (http_stream_ != NULL) && (http_stream_->GetState() == SS_OPEN);
+}
+
+bool
+HttpBase::attach(StreamInterface* stream) {
+ if ((mode_ != HM_NONE) || (http_stream_ != NULL) || (stream == NULL)) {
+ ASSERT(false);
+ return false;
+ }
+ http_stream_ = stream;
+ http_stream_->SignalEvent.connect(this, &HttpBase::OnHttpStreamEvent);
+ mode_ = (http_stream_->GetState() == SS_OPENING) ? HM_CONNECT : HM_NONE;
+ return true;
+}
+
+StreamInterface*
+HttpBase::detach() {
+ ASSERT(HM_NONE == mode_);
+ if (mode_ != HM_NONE) {
+ return NULL;
+ }
+ StreamInterface* stream = http_stream_;
+ http_stream_ = NULL;
+ if (stream) {
+ stream->SignalEvent.disconnect(this);
+ }
+ return stream;
+}
+
+void
+HttpBase::send(HttpData* data) {
+ ASSERT(HM_NONE == mode_);
+ if (mode_ != HM_NONE) {
+ return;
+ } else if (!isConnected()) {
+ OnHttpStreamEvent(http_stream_, SE_CLOSE, HE_DISCONNECTED);
+ return;
+ }
+
+ mode_ = HM_SEND;
+ data_ = data;
+ len_ = 0;
+ ignore_data_ = chunk_data_ = false;
+
+ if (data_->document.get()) {
+ data_->document->SignalEvent.connect(this, &HttpBase::OnDocumentEvent);
+ }
+
+ std::string encoding;
+ if (data_->hasHeader(HH_TRANSFER_ENCODING, &encoding)
+ && (encoding == "chunked")) {
+ chunk_data_ = true;
+ }
+
+ len_ = data_->formatLeader(buffer_, sizeof(buffer_));
+ len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n");
+
+ header_ = data_->begin();
+ if (header_ == data_->end()) {
+ // We must call this at least once, in the case where there are no headers.
+ queue_headers();
+ }
+
+ flush_data();
+}
+
+void
+HttpBase::recv(HttpData* data) {
+ ASSERT(HM_NONE == mode_);
+ if (mode_ != HM_NONE) {
+ return;
+ } else if (!isConnected()) {
+ OnHttpStreamEvent(http_stream_, SE_CLOSE, HE_DISCONNECTED);
+ return;
+ }
+
+ mode_ = HM_RECV;
+ data_ = data;
+ len_ = 0;
+ ignore_data_ = chunk_data_ = false;
+
+ reset();
+ if (doc_stream_) {
+ doc_stream_->SignalEvent(doc_stream_, SE_OPEN | SE_READ, 0);
+ } else {
+ read_and_process_data();
+ }
+}
+
+void
+HttpBase::abort(HttpError err) {
+ if (mode_ != HM_NONE) {
+ if (http_stream_ != NULL) {
+ http_stream_->Close();
+ }
+ do_complete(err);
+ }
+}
+
+StreamInterface* HttpBase::GetDocumentStream() {
+ if (doc_stream_)
+ return NULL;
+ doc_stream_ = new DocumentStream(this);
+ return doc_stream_;
+}
+
+HttpError HttpBase::HandleStreamClose(int error) {
+ if (http_stream_ != NULL) {
+ http_stream_->Close();
+ }
+ if (error == 0) {
+ if ((mode_ == HM_RECV) && is_valid_end_of_input()) {
+ return HE_NONE;
+ } else {
+ return HE_DISCONNECTED;
+ }
+ } else if (error == SOCKET_EACCES) {
+ return HE_AUTH;
+ } else if (error == SEC_E_CERT_EXPIRED) {
+ return HE_CERTIFICATE_EXPIRED;
+ }
+ LOG_F(LS_ERROR) << "(" << error << ")";
+ return (HM_CONNECT == mode_) ? HE_CONNECT_FAILED : HE_SOCKET_ERROR;
+}
+
+bool HttpBase::DoReceiveLoop(HttpError* error) {
+ ASSERT(HM_RECV == mode_);
+ ASSERT(NULL != error);
+
+ // Do to the latency between receiving read notifications from
+ // pseudotcpchannel, we rely on repeated calls to read in order to acheive
+ // ideal throughput. The number of reads is limited to prevent starving
+ // the caller.
+
+ size_t loop_count = 0;
+ const size_t kMaxReadCount = 20;
+ bool process_requires_more_data = false;
+ do {
+ // The most frequent use of this function is response to new data available
+ // on http_stream_. Therefore, we optimize by attempting to read from the
+ // network first (as opposed to processing existing data first).
+
+ if (len_ < sizeof(buffer_)) {
+ // Attempt to buffer more data.
+ size_t read;
+ int read_error;
+ StreamResult read_result = http_stream_->Read(buffer_ + len_,
+ sizeof(buffer_) - len_,
+ &read, &read_error);
+ switch (read_result) {
+ case SR_SUCCESS:
+ ASSERT(len_ + read <= sizeof(buffer_));
+ len_ += read;
+ break;
+ case SR_BLOCK:
+ if (process_requires_more_data) {
+ // We're can't make progress until more data is available.
+ return false;
+ }
+ // Attempt to process the data already in our buffer.
+ break;
+ case SR_EOS:
+ // Clean close, with no error. Fall through to HandleStreamClose.
+ read_error = 0;
+ case SR_ERROR:
+ *error = HandleStreamClose(read_error);
+ return true;
+ }
+ } else if (process_requires_more_data) {
+ // We have too much unprocessed data in our buffer. This should only
+ // occur when a single HTTP header is longer than the buffer size (32K).
+ // Anything longer than that is almost certainly an error.
+ *error = HE_OVERFLOW;
+ return true;
+ }
+
+ // Process data in our buffer. Process is not guaranteed to process all
+ // the buffered data. In particular, it will wait until a complete
+ // protocol element (such as http header, or chunk size) is available,
+ // before processing it in its entirety. Also, it is valid and sometimes
+ // necessary to call Process with an empty buffer, since the state machine
+ // may have interrupted state transitions to complete.
+ size_t processed;
+ ProcessResult process_result = Process(buffer_, len_, &processed,
+ error);
+ ASSERT(processed <= len_);
+ len_ -= processed;
+ memmove(buffer_, buffer_ + processed, len_);
+ switch (process_result) {
+ case PR_CONTINUE:
+ // We need more data to make progress.
+ process_requires_more_data = true;
+ break;
+ case PR_BLOCK:
+ // We're stalled on writing the processed data.
+ return false;
+ case PR_COMPLETE:
+ // *error already contains the correct code.
+ return true;
+ }
+ } while (++loop_count <= kMaxReadCount);
+
+ LOG_F(LS_WARNING) << "danger of starvation";
+ return false;
+}
+
+void
+HttpBase::read_and_process_data() {
+ HttpError error;
+ if (DoReceiveLoop(&error)) {
+ complete(error);
+ }
+}
+
+void
+HttpBase::flush_data() {
+ ASSERT(HM_SEND == mode_);
+
+ // When send_required is true, no more buffering can occur without a network
+ // write.
+ bool send_required = (len_ >= sizeof(buffer_));
+
+ while (true) {
+ ASSERT(len_ <= sizeof(buffer_));
+
+ // HTTP is inherently sensitive to round trip latency, since a frequent use
+ // case is for small requests and responses to be sent back and forth, and
+ // the lack of pipelining forces a single request to take a minimum of the
+ // round trip time. As a result, it is to our benefit to pack as much data
+ // into each packet as possible. Thus, we defer network writes until we've
+ // buffered as much data as possible.
+
+ if (!send_required && (header_ != data_->end())) {
+ // First, attempt to queue more header data.
+ send_required = queue_headers();
+ }
+
+ if (!send_required && (NULL != data_->document.get())) {
+ // Next, attempt to queue document data.
+
+ const size_t kChunkDigits = 8;
+ size_t offset, reserve;
+ if (chunk_data_) {
+ // Reserve characters at the start for X-byte hex value and \r\n
+ offset = len_ + kChunkDigits + 2;
+ // ... and 2 characters at the end for \r\n
+ reserve = offset + 2;
+ } else {
+ offset = len_;
+ reserve = offset;
+ }
+
+ if (reserve >= sizeof(buffer_)) {
+ send_required = true;
+ } else {
+ size_t read;
+ int error;
+ StreamResult result = data_->document->Read(buffer_ + offset,
+ sizeof(buffer_) - reserve,
+ &read, &error);
+ if (result == SR_SUCCESS) {
+ ASSERT(reserve + read <= sizeof(buffer_));
+ if (chunk_data_) {
+ // Prepend the chunk length in hex.
+ // Note: sprintfn appends a null terminator, which is why we can't
+ // combine it with the line terminator.
+ sprintfn(buffer_ + len_, kChunkDigits + 1, "%.*x",
+ kChunkDigits, read);
+ // Add line terminator to the chunk length.
+ memcpy(buffer_ + len_ + kChunkDigits, "\r\n", 2);
+ // Add line terminator to the end of the chunk.
+ memcpy(buffer_ + offset + read, "\r\n", 2);
+ }
+ len_ = reserve + read;
+ } else if (result == SR_BLOCK) {
+ // Nothing to do but flush data to the network.
+ send_required = true;
+ } else if (result == SR_EOS) {
+ if (chunk_data_) {
+ // Append the empty chunk and empty trailers, then turn off
+ // chunking.
+ ASSERT(len_ + 5 <= sizeof(buffer_));
+ memcpy(buffer_ + len_, "0\r\n\r\n", 5);
+ len_ += 5;
+ chunk_data_ = false;
+ } else if (0 == len_) {
+ // No more data to read, and no more data to write.
+ do_complete();
+ return;
+ }
+ // Although we are done reading data, there is still data which needs
+ // to be flushed to the network.
+ send_required = true;
+ } else {
+ LOG_F(LS_ERROR) << "Read error: " << error;
+ do_complete(HE_STREAM);
+ return;
+ }
+ }
+ }
+
+ if (0 == len_) {
+ // No data currently available to send.
+ if (NULL == data_->document.get()) {
+ // If there is no source document, that means we're done.
+ do_complete();
+ }
+ return;
+ }
+
+ size_t written;
+ int error;
+ StreamResult result = http_stream_->Write(buffer_, len_, &written, &error);
+ if (result == SR_SUCCESS) {
+ ASSERT(written <= len_);
+ len_ -= written;
+ memmove(buffer_, buffer_ + written, len_);
+ send_required = false;
+ } else if (result == SR_BLOCK) {
+ if (send_required) {
+ // Nothing more we can do until network is writeable.
+ return;
+ }
+ } else {
+ ASSERT(result == SR_ERROR);
+ LOG_F(LS_ERROR) << "error";
+ OnHttpStreamEvent(http_stream_, SE_CLOSE, error);
+ return;
+ }
+ }
+
+ ASSERT(false);
+}
+
+bool
+HttpBase::queue_headers() {
+ ASSERT(HM_SEND == mode_);
+ while (header_ != data_->end()) {
+ size_t len = sprintfn(buffer_ + len_, sizeof(buffer_) - len_,
+ "%.*s: %.*s\r\n",
+ header_->first.size(), header_->first.data(),
+ header_->second.size(), header_->second.data());
+ if (len_ + len < sizeof(buffer_) - 3) {
+ len_ += len;
+ ++header_;
+ } else if (len_ == 0) {
+ LOG(WARNING) << "discarding header that is too long: " << header_->first;
+ ++header_;
+ } else {
+ // Not enough room for the next header, write to network first.
+ return true;
+ }
+ }
+ // End of headers
+ len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n");
+ return false;
+}
+
+void
+HttpBase::do_complete(HttpError err) {
+ ASSERT(mode_ != HM_NONE);
+ HttpMode mode = mode_;
+ mode_ = HM_NONE;
+ if (data_ && data_->document.get()) {
+ data_->document->SignalEvent.disconnect(this);
+ }
+ data_ = NULL;
+ if ((HM_RECV == mode) && doc_stream_) {
+ ASSERT(HE_NONE != err); // We should have Disconnected doc_stream_ already.
+ DocumentStream* ds = doc_stream_;
+ ds->Disconnect(err);
+ ds->SignalEvent(ds, SE_CLOSE, err);
+ }
+ if (notify_) {
+ notify_->onHttpComplete(mode, err);
+ }
+}
+
+//
+// Stream Signals
+//
+
+void
+HttpBase::OnHttpStreamEvent(StreamInterface* stream, int events, int error) {
+ ASSERT(stream == http_stream_);
+ if ((events & SE_OPEN) && (mode_ == HM_CONNECT)) {
+ do_complete();
+ return;
+ }
+
+ if ((events & SE_WRITE) && (mode_ == HM_SEND)) {
+ flush_data();
+ return;
+ }
+
+ if ((events & SE_READ) && (mode_ == HM_RECV)) {
+ if (doc_stream_) {
+ doc_stream_->SignalEvent(doc_stream_, SE_READ, 0);
+ } else {
+ read_and_process_data();
+ }
+ return;
+ }
+
+ if ((events & SE_CLOSE) == 0)
+ return;
+
+ HttpError http_error = HandleStreamClose(error);
+ if (mode_ == HM_RECV) {
+ complete(http_error);
+ } else if (mode_ != HM_NONE) {
+ do_complete(http_error);
+ } else if (notify_) {
+ notify_->onHttpClosed(http_error);
+ }
+}
+
+void
+HttpBase::OnDocumentEvent(StreamInterface* stream, int events, int error) {
+ ASSERT(stream == data_->document.get());
+ if ((events & SE_WRITE) && (mode_ == HM_RECV)) {
+ read_and_process_data();
+ return;
+ }
+
+ if ((events & SE_READ) && (mode_ == HM_SEND)) {
+ flush_data();
+ return;
+ }
+
+ if (events & SE_CLOSE) {
+ LOG_F(LS_ERROR) << "Read error: " << error;
+ do_complete(HE_STREAM);
+ return;
+ }
+}
+
+//
+// HttpParser Implementation
+//
+
+HttpParser::ProcessResult
+HttpBase::ProcessLeader(const char* line, size_t len, HttpError* error) {
+ *error = data_->parseLeader(line, len);
+ return (HE_NONE == *error) ? PR_CONTINUE : PR_COMPLETE;
+}
+
+HttpParser::ProcessResult
+HttpBase::ProcessHeader(const char* name, size_t nlen, const char* value,
+ size_t vlen, HttpError* error) {
+ std::string sname(name, nlen), svalue(value, vlen);
+ data_->addHeader(sname, svalue);
+ return PR_CONTINUE;
+}
+
+HttpParser::ProcessResult
+HttpBase::ProcessHeaderComplete(bool chunked, size_t& data_size,
+ HttpError* error) {
+ StreamInterface* old_docstream = doc_stream_;
+ if (notify_) {
+ *error = notify_->onHttpHeaderComplete(chunked, data_size);
+ // The request must not be aborted as a result of this callback.
+ ASSERT(NULL != data_);
+ }
+ if ((HE_NONE == *error) && (NULL != data_->document.get())) {
+ data_->document->SignalEvent.connect(this, &HttpBase::OnDocumentEvent);
+ }
+ if (HE_NONE != *error) {
+ return PR_COMPLETE;
+ }
+ if (old_docstream != doc_stream_) {
+ // Break out of Process loop, since our I/O model just changed.
+ return PR_BLOCK;
+ }
+ return PR_CONTINUE;
+}
+
+HttpParser::ProcessResult
+HttpBase::ProcessData(const char* data, size_t len, size_t& read,
+ HttpError* error) {
+ LOG_F(LS_VERBOSE) << "data: " << std::string(data, len);
+ if (ignore_data_ || !data_->document.get()) {
+ read = len;
+ return PR_CONTINUE;
+ }
+ int write_error = 0;
+ switch (data_->document->Write(data, len, &read, &write_error)) {
+ case SR_SUCCESS:
+ return PR_CONTINUE;
+ case SR_BLOCK:
+ return PR_BLOCK;
+ case SR_EOS:
+ LOG_F(LS_ERROR) << "Unexpected EOS";
+ *error = HE_STREAM;
+ return PR_COMPLETE;
+ case SR_ERROR:
+ default:
+ LOG_F(LS_ERROR) << "Write error: " << write_error;
+ *error = HE_STREAM;
+ return PR_COMPLETE;
+ }
+}
+
+void
+HttpBase::OnComplete(HttpError err) {
+ LOG_F(LS_VERBOSE);
+ do_complete(err);
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/httpbase.h b/third_party/libjingle/source/talk/base/httpbase.h
new file mode 100644
index 0000000..d457e6b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httpbase.h
@@ -0,0 +1,201 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Author: <bpm@google.com> (Brian McBarron)
+
+#ifndef TALK_BASE_HTTPBASE_H__
+#define TALK_BASE_HTTPBASE_H__
+
+#include "talk/base/httpcommon.h"
+
+namespace talk_base {
+
+class StreamInterface;
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpParser - Parses an HTTP stream provided via Process and end_of_input, and
+// generates events for:
+// Structural Elements: Leader, Headers, Document Data
+// Events: End of Headers, End of Document, Errors
+///////////////////////////////////////////////////////////////////////////////
+
+class HttpParser {
+public:
+ enum ProcessResult { PR_CONTINUE, PR_BLOCK, PR_COMPLETE };
+ HttpParser();
+ virtual ~HttpParser();
+
+ void reset();
+ ProcessResult Process(const char* buffer, size_t len, size_t* processed,
+ HttpError* error);
+ bool is_valid_end_of_input() const;
+ void complete(HttpError err);
+
+ size_t GetDataRemaining() const { return data_size_; }
+
+protected:
+ ProcessResult ProcessLine(const char* line, size_t len, HttpError* error);
+
+ // HttpParser Interface
+ virtual ProcessResult ProcessLeader(const char* line, size_t len,
+ HttpError* error) = 0;
+ virtual ProcessResult ProcessHeader(const char* name, size_t nlen,
+ const char* value, size_t vlen,
+ HttpError* error) = 0;
+ virtual ProcessResult ProcessHeaderComplete(bool chunked, size_t& data_size,
+ HttpError* error) = 0;
+ virtual ProcessResult ProcessData(const char* data, size_t len, size_t& read,
+ HttpError* error) = 0;
+ virtual void OnComplete(HttpError err) = 0;
+
+private:
+ enum State {
+ ST_LEADER, ST_HEADERS,
+ ST_CHUNKSIZE, ST_CHUNKTERM, ST_TRAILERS,
+ ST_DATA, ST_COMPLETE
+ } state_;
+ bool chunked_;
+ size_t data_size_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// IHttpNotify
+///////////////////////////////////////////////////////////////////////////////
+
+enum HttpMode { HM_NONE, HM_CONNECT, HM_RECV, HM_SEND };
+
+class IHttpNotify {
+public:
+ virtual ~IHttpNotify() {}
+ virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) = 0;
+ virtual void onHttpComplete(HttpMode mode, HttpError err) = 0;
+ virtual void onHttpClosed(HttpError err) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpBase - Provides a state machine for implementing HTTP-based components.
+// Attach HttpBase to a StreamInterface which represents a bidirectional HTTP
+// stream, and then call send() or recv() to initiate sending or receiving one
+// side of an HTTP transaction. By default, HttpBase operates as an I/O pump,
+// moving data from the HTTP stream to the HttpData object and vice versa.
+// However, it can also operate in stream mode, in which case the user of the
+// stream interface drives I/O via calls to Read().
+///////////////////////////////////////////////////////////////////////////////
+
+class HttpBase
+: private HttpParser,
+ public sigslot::has_slots<>
+{
+public:
+ HttpBase();
+ virtual ~HttpBase();
+
+ void notify(IHttpNotify* notify) { notify_ = notify; }
+ bool attach(StreamInterface* stream);
+ StreamInterface* stream() { return http_stream_; }
+ StreamInterface* detach();
+ bool isConnected() const;
+
+ void send(HttpData* data);
+ void recv(HttpData* data);
+ void abort(HttpError err);
+
+ HttpMode mode() const { return mode_; }
+
+ void set_ignore_data(bool ignore) { ignore_data_ = ignore; }
+ bool ignore_data() const { return ignore_data_; }
+
+ // Obtaining this stream puts HttpBase into stream mode until the stream
+ // is closed. HttpBase can only expose one open stream interface at a time.
+ // Further calls will return NULL.
+ StreamInterface* GetDocumentStream();
+
+protected:
+ // Do cleanup when the http stream closes (error may be 0 for a clean
+ // shutdown), and return the error code to signal.
+ HttpError HandleStreamClose(int error);
+
+ // DoReceiveLoop acts as a data pump, pulling data from the http stream,
+ // pushing it through the HttpParser, and then populating the HttpData object
+ // based on the callbacks from the parser. One of the most interesting
+ // callbacks is ProcessData, which provides the actual http document body.
+ // This data is then written to the HttpData::document. As a result, data
+ // flows from the network to the document, with some incidental protocol
+ // parsing in between.
+ // Ideally, we would pass in the document* to DoReceiveLoop, to more easily
+ // support GetDocumentStream(). However, since the HttpParser is callback
+ // driven, we are forced to store the pointer somewhere until the callback
+ // is triggered.
+ // Returns true if the received document has finished, and
+ // HttpParser::complete should be called.
+ bool DoReceiveLoop(HttpError* err);
+
+ void read_and_process_data();
+ void flush_data();
+ bool queue_headers();
+ void do_complete(HttpError err = HE_NONE);
+
+ void OnHttpStreamEvent(StreamInterface* stream, int events, int error);
+ void OnDocumentEvent(StreamInterface* stream, int events, int error);
+
+ // HttpParser Interface
+ virtual ProcessResult ProcessLeader(const char* line, size_t len,
+ HttpError* error);
+ virtual ProcessResult ProcessHeader(const char* name, size_t nlen,
+ const char* value, size_t vlen,
+ HttpError* error);
+ virtual ProcessResult ProcessHeaderComplete(bool chunked, size_t& data_size,
+ HttpError* error);
+ virtual ProcessResult ProcessData(const char* data, size_t len, size_t& read,
+ HttpError* error);
+ virtual void OnComplete(HttpError err);
+
+private:
+ class DocumentStream;
+ friend class DocumentStream;
+
+ enum { kBufferSize = 32 * 1024 };
+
+ HttpMode mode_;
+ HttpData* data_;
+ IHttpNotify* notify_;
+ StreamInterface* http_stream_;
+ DocumentStream* doc_stream_;
+ char buffer_[kBufferSize];
+ size_t len_;
+
+ bool ignore_data_, chunk_data_;
+ HttpData::const_iterator header_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPBASE_H__
diff --git a/third_party/libjingle/source/talk/base/httpclient.cc b/third_party/libjingle/source/talk/base/httpclient.cc
new file mode 100644
index 0000000..3b6e97e
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httpclient.cc
@@ -0,0 +1,813 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <time.h>
+
+#include "talk/base/httpcommon-inl.h"
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/common.h"
+#include "talk/base/diskcache.h"
+#include "talk/base/httpclient.h"
+#include "talk/base/logging.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/socketstream.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/thread.h"
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Helpers
+//////////////////////////////////////////////////////////////////////
+
+namespace {
+
+const size_t kCacheHeader = 0;
+const size_t kCacheBody = 1;
+
+// Convert decimal string to integer
+bool HttpStringToInt(const std::string& str, unsigned long* val) {
+ ASSERT(NULL != val);
+ char* eos = NULL;
+ *val = strtoul(str.c_str(), &eos, 10);
+ return (*eos == '\0');
+}
+
+bool HttpShouldCache(const HttpTransaction& t) {
+ bool verb_allows_cache = (t.request.verb == HV_GET)
+ || (t.request.verb == HV_HEAD);
+ bool is_range_response = t.response.hasHeader(HH_CONTENT_RANGE, NULL);
+ bool has_expires = t.response.hasHeader(HH_EXPIRES, NULL);
+ bool request_allows_cache =
+ has_expires || (std::string::npos != t.request.path.find('?'));
+ bool response_allows_cache =
+ has_expires || HttpCodeIsCacheable(t.response.scode);
+
+ bool may_cache = verb_allows_cache
+ && request_allows_cache
+ && response_allows_cache
+ && !is_range_response;
+
+ std::string value;
+ if (t.response.hasHeader(HH_CACHE_CONTROL, &value)) {
+ HttpAttributeList directives;
+ HttpParseAttributes(value.data(), value.size(), directives);
+ // Response Directives Summary:
+ // public - always cacheable
+ // private - do not cache in a shared cache
+ // no-cache - may cache, but must revalidate whether fresh or stale
+ // no-store - sensitive information, do not cache or store in any way
+ // max-age - supplants Expires for staleness
+ // s-maxage - use as max-age for shared caches, ignore otherwise
+ // must-revalidate - may cache, but must revalidate after stale
+ // proxy-revalidate - shared cache must revalidate
+ if (HttpHasAttribute(directives, "no-store", NULL)) {
+ may_cache = false;
+ } else if (HttpHasAttribute(directives, "public", NULL)) {
+ may_cache = true;
+ }
+ }
+ return may_cache;
+}
+
+enum HttpCacheState {
+ HCS_FRESH, // In cache, may use
+ HCS_STALE, // In cache, must revalidate
+ HCS_NONE // Not in cache
+};
+
+HttpCacheState HttpGetCacheState(const HttpTransaction& t) {
+ // Temporaries
+ std::string s_temp;
+ unsigned long i_temp;
+
+ // Current time
+ unsigned long now = time(0);
+
+ HttpAttributeList cache_control;
+ if (t.response.hasHeader(HH_CACHE_CONTROL, &s_temp)) {
+ HttpParseAttributes(s_temp.data(), s_temp.size(), cache_control);
+ }
+
+ // Compute age of cache document
+ unsigned long date;
+ if (!t.response.hasHeader(HH_DATE, &s_temp)
+ || !HttpDateToSeconds(s_temp, &date))
+ return HCS_NONE;
+
+ // TODO: Timestamp when cache request sent and response received?
+ unsigned long request_time = date;
+ unsigned long response_time = date;
+
+ unsigned long apparent_age = 0;
+ if (response_time > date) {
+ apparent_age = response_time - date;
+ }
+
+ unsigned long corrected_received_age = apparent_age;
+ if (t.response.hasHeader(HH_AGE, &s_temp)
+ && HttpStringToInt(s_temp, &i_temp)) {
+ corrected_received_age = stdmax(apparent_age, i_temp);
+ }
+
+ unsigned long response_delay = response_time - request_time;
+ unsigned long corrected_initial_age = corrected_received_age + response_delay;
+ unsigned long resident_time = now - response_time;
+ unsigned long current_age = corrected_initial_age + resident_time;
+
+ // Compute lifetime of document
+ unsigned long lifetime;
+ if (HttpHasAttribute(cache_control, "max-age", &s_temp)) {
+ lifetime = atoi(s_temp.c_str());
+ } else if (t.response.hasHeader(HH_EXPIRES, &s_temp)
+ && HttpDateToSeconds(s_temp, &i_temp)) {
+ lifetime = i_temp - date;
+ } else if (t.response.hasHeader(HH_LAST_MODIFIED, &s_temp)
+ && HttpDateToSeconds(s_temp, &i_temp)) {
+ // TODO: Issue warning 113 if age > 24 hours
+ lifetime = (now - i_temp) / 10;
+ } else {
+ return HCS_STALE;
+ }
+
+ return (lifetime > current_age) ? HCS_FRESH : HCS_STALE;
+}
+
+enum HttpValidatorStrength {
+ HVS_NONE,
+ HVS_WEAK,
+ HVS_STRONG
+};
+
+HttpValidatorStrength
+HttpRequestValidatorLevel(const HttpRequestData& request) {
+ if (HV_GET != request.verb)
+ return HVS_STRONG;
+ return request.hasHeader(HH_RANGE, NULL) ? HVS_STRONG : HVS_WEAK;
+}
+
+HttpValidatorStrength
+HttpResponseValidatorLevel(const HttpResponseData& response) {
+ std::string value;
+ if (response.hasHeader(HH_ETAG, &value)) {
+ bool is_weak = (strnicmp(value.c_str(), "W/", 2) == 0);
+ return is_weak ? HVS_WEAK : HVS_STRONG;
+ }
+ if (response.hasHeader(HH_LAST_MODIFIED, &value)) {
+ unsigned long last_modified, date;
+ if (HttpDateToSeconds(value, &last_modified)
+ && response.hasHeader(HH_DATE, &value)
+ && HttpDateToSeconds(value, &date)
+ && (last_modified + 60 < date)) {
+ return HVS_STRONG;
+ }
+ return HVS_WEAK;
+ }
+ return HVS_NONE;
+}
+
+std::string GetCacheID(const HttpRequestData& request) {
+ std::string id, url;
+ id.append(ToString(request.verb));
+ id.append("_");
+ request.getAbsoluteUri(&url);
+ id.append(url);
+ return id;
+}
+
+} // anonymous namespace
+
+//////////////////////////////////////////////////////////////////////
+// Public Helpers
+//////////////////////////////////////////////////////////////////////
+
+bool HttpWriteCacheHeaders(const HttpResponseData* response,
+ StreamInterface* output, size_t* size) {
+ size_t length = 0;
+ // Write all unknown and end-to-end headers to a cache file
+ for (HttpData::const_iterator it = response->begin();
+ it != response->end(); ++it) {
+ HttpHeader header;
+ if (FromString(header, it->first) && !HttpHeaderIsEndToEnd(header))
+ continue;
+ length += it->first.length() + 2 + it->second.length() + 2;
+ if (!output)
+ continue;
+ std::string formatted_header(it->first);
+ formatted_header.append(": ");
+ formatted_header.append(it->second);
+ formatted_header.append("\r\n");
+ StreamResult result = output->WriteAll(formatted_header.data(),
+ formatted_header.length(),
+ NULL, NULL);
+ if (SR_SUCCESS != result) {
+ return false;
+ }
+ }
+ if (output && (SR_SUCCESS != output->WriteAll("\r\n", 2, NULL, NULL))) {
+ return false;
+ }
+ length += 2;
+ if (size)
+ *size = length;
+ return true;
+}
+
+bool HttpReadCacheHeaders(StreamInterface* input, HttpResponseData* response,
+ HttpData::HeaderCombine combine) {
+ while (true) {
+ std::string formatted_header;
+ StreamResult result = input->ReadLine(&formatted_header);
+ if ((SR_EOS == result) || (1 == formatted_header.size())) {
+ break;
+ }
+ if (SR_SUCCESS != result) {
+ return false;
+ }
+ size_t end_of_name = formatted_header.find(':');
+ if (std::string::npos == end_of_name) {
+ LOG_F(LS_WARNING) << "Malformed cache header";
+ continue;
+ }
+ size_t start_of_value = end_of_name + 1;
+ size_t end_of_value = formatted_header.length();
+ while ((start_of_value < end_of_value)
+ && isspace(formatted_header[start_of_value]))
+ ++start_of_value;
+ while ((start_of_value < end_of_value)
+ && isspace(formatted_header[end_of_value-1]))
+ --end_of_value;
+ size_t value_length = end_of_value - start_of_value;
+
+ std::string name(formatted_header.substr(0, end_of_name));
+ std::string value(formatted_header.substr(start_of_value, value_length));
+ response->changeHeader(name, value, combine);
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpClient
+//////////////////////////////////////////////////////////////////////
+
+const size_t kDefaultRetries = 1;
+const size_t kMaxRedirects = 5;
+
+HttpClient::HttpClient(const std::string& agent, StreamPool* pool,
+ HttpTransaction* transaction)
+ : agent_(agent), pool_(pool),
+ transaction_(transaction), free_transaction_(false),
+ retries_(kDefaultRetries), attempt_(0), redirects_(0),
+ redirect_action_(REDIRECT_DEFAULT),
+ uri_form_(URI_DEFAULT), cache_(NULL), cache_state_(CS_READY) {
+ base_.notify(this);
+ if (NULL == transaction_) {
+ free_transaction_ = true;
+ transaction_ = new HttpTransaction;
+ }
+}
+
+HttpClient::~HttpClient() {
+ base_.notify(NULL);
+ base_.abort(HE_SHUTDOWN);
+ release();
+ if (free_transaction_)
+ delete transaction_;
+}
+
+void HttpClient::reset() {
+ server_.Clear();
+ request().clear(true);
+ response().clear(true);
+ context_.reset();
+ redirects_ = 0;
+ base_.abort(HE_OPERATION_CANCELLED);
+}
+
+void HttpClient::set_server(const SocketAddress& address) {
+ server_ = address;
+ // Setting 'Host' here allows it to be overridden before starting the request,
+ // if necessary.
+ request().setHeader(HH_HOST, HttpAddress(server_, false), true);
+}
+
+StreamInterface* HttpClient::GetDocumentStream() {
+ return base_.GetDocumentStream();
+}
+
+void HttpClient::start() {
+ if (base_.mode() != HM_NONE) {
+ // call reset() to abort an in-progress request
+ ASSERT(false);
+ return;
+ }
+
+ ASSERT(!IsCacheActive());
+
+ if (request().hasHeader(HH_TRANSFER_ENCODING, NULL)) {
+ // Exact size must be known on the client. Instead of using chunked
+ // encoding, wrap data with auto-caching file or memory stream.
+ ASSERT(false);
+ return;
+ }
+
+ attempt_ = 0;
+
+ // If no content has been specified, using length of 0.
+ request().setHeader(HH_CONTENT_LENGTH, "0", false);
+
+ if (!agent_.empty()) {
+ request().setHeader(HH_USER_AGENT, agent_, false);
+ }
+
+ UriForm uri_form = uri_form_;
+ if (PROXY_HTTPS == proxy_.type) {
+ // Proxies require absolute form
+ uri_form = URI_ABSOLUTE;
+ request().version = HVER_1_0;
+ request().setHeader(HH_PROXY_CONNECTION, "Keep-Alive", false);
+ } else {
+ request().setHeader(HH_CONNECTION, "Keep-Alive", false);
+ }
+
+ if (URI_ABSOLUTE == uri_form) {
+ // Convert to absolute uri form
+ std::string url;
+ if (request().getAbsoluteUri(&url)) {
+ request().path = url;
+ } else {
+ LOG(LS_WARNING) << "Couldn't obtain absolute uri";
+ }
+ } else if (URI_RELATIVE == uri_form) {
+ // Convert to relative uri form
+ std::string host, path;
+ if (request().getRelativeUri(&host, &path)) {
+ request().setHeader(HH_HOST, host);
+ request().path = path;
+ } else {
+ LOG(LS_WARNING) << "Couldn't obtain relative uri";
+ }
+ }
+
+ if ((NULL != cache_) && CheckCache()) {
+ return;
+ }
+
+ connect();
+}
+
+void HttpClient::connect() {
+ int stream_err;
+ StreamInterface* stream = pool_->RequestConnectedStream(server_, &stream_err);
+ if (stream == NULL) {
+ ASSERT(0 != stream_err);
+ LOG(LS_ERROR) << "RequestConnectedStream error: " << stream_err;
+ onHttpComplete(HM_CONNECT, HE_CONNECT_FAILED);
+ } else {
+ base_.attach(stream);
+ if (stream->GetState() == SS_OPEN) {
+ base_.send(&transaction_->request);
+ }
+ }
+}
+
+void HttpClient::prepare_get(const std::string& url) {
+ reset();
+ Url<char> purl(url);
+ set_server(SocketAddress(purl.host(), purl.port()));
+ request().verb = HV_GET;
+ request().path = purl.full_path();
+}
+
+void HttpClient::prepare_post(const std::string& url,
+ const std::string& content_type,
+ StreamInterface* request_doc) {
+ reset();
+ Url<char> purl(url);
+ set_server(SocketAddress(purl.host(), purl.port()));
+ request().verb = HV_POST;
+ request().path = purl.full_path();
+ request().setContent(content_type, request_doc);
+}
+
+void HttpClient::release() {
+ if (StreamInterface* stream = base_.detach()) {
+ pool_->ReturnConnectedStream(stream);
+ }
+}
+
+bool HttpClient::ShouldRedirect(std::string* location) const {
+ // TODO: Unittest redirection.
+ if ((REDIRECT_NEVER == redirect_action_)
+ || !HttpCodeIsRedirection(response().scode)
+ || !response().hasHeader(HH_LOCATION, location)
+ || (redirects_ >= kMaxRedirects))
+ return false;
+ return (REDIRECT_ALWAYS == redirect_action_)
+ || (HC_SEE_OTHER == response().scode)
+ || (HV_HEAD == request().verb)
+ || (HV_GET == request().verb);
+}
+
+bool HttpClient::BeginCacheFile() {
+ ASSERT(NULL != cache_);
+ ASSERT(CS_READY == cache_state_);
+
+ std::string id = GetCacheID(request());
+ CacheLock lock(cache_, id, true);
+ if (!lock.IsLocked()) {
+ LOG_F(LS_WARNING) << "Couldn't lock cache";
+ return false;
+ }
+
+ if (HE_NONE != WriteCacheHeaders(id)) {
+ return false;
+ }
+
+ scoped_ptr<StreamInterface> stream(cache_->WriteResource(id, kCacheBody));
+ if (!stream.get()) {
+ LOG_F(LS_ERROR) << "Couldn't open body cache";
+ return false;
+ }
+ lock.Commit();
+
+ // Let's secretly replace the response document with Folgers Crystals,
+ // er, StreamTap, so that we can mirror the data to our cache.
+ StreamInterface* output = response().document.release();
+ if (!output) {
+ output = new NullStream;
+ }
+ StreamTap* tap = new StreamTap(output, stream.release());
+ response().document.reset(tap);
+ return true;
+}
+
+HttpError HttpClient::WriteCacheHeaders(const std::string& id) {
+ scoped_ptr<StreamInterface> stream(cache_->WriteResource(id, kCacheHeader));
+ if (!stream.get()) {
+ LOG_F(LS_ERROR) << "Couldn't open header cache";
+ return HE_CACHE;
+ }
+
+ if (!HttpWriteCacheHeaders(&transaction_->response, stream.get(), NULL)) {
+ LOG_F(LS_ERROR) << "Couldn't write header cache";
+ return HE_CACHE;
+ }
+
+ return HE_NONE;
+}
+
+void HttpClient::CompleteCacheFile() {
+ // Restore previous response document
+ StreamTap* tap = static_cast<StreamTap*>(response().document.release());
+ response().document.reset(tap->Detach());
+
+ int error;
+ StreamResult result = tap->GetTapResult(&error);
+
+ // Delete the tap and cache stream (which completes cache unlock)
+ delete tap;
+
+ if (SR_SUCCESS != result) {
+ LOG(LS_ERROR) << "Cache file error: " << error;
+ cache_->DeleteResource(GetCacheID(request()));
+ }
+}
+
+bool HttpClient::CheckCache() {
+ ASSERT(NULL != cache_);
+ ASSERT(CS_READY == cache_state_);
+
+ std::string id = GetCacheID(request());
+ if (!cache_->HasResource(id)) {
+ // No cache file available
+ return false;
+ }
+
+ HttpError error = ReadCacheHeaders(id, true);
+
+ if (HE_NONE == error) {
+ switch (HttpGetCacheState(*transaction_)) {
+ case HCS_FRESH:
+ // Cache content is good, read from cache
+ break;
+ case HCS_STALE:
+ // Cache content may be acceptable. Issue a validation request.
+ if (PrepareValidate()) {
+ return false;
+ }
+ // Couldn't validate, fall through.
+ case HCS_NONE:
+ // Cache content is not useable. Issue a regular request.
+ response().clear(false);
+ return false;
+ }
+ }
+
+ if (HE_NONE == error) {
+ error = ReadCacheBody(id);
+ cache_state_ = CS_READY;
+ }
+
+ if (HE_CACHE == error) {
+ LOG_F(LS_WARNING) << "Cache failure, continuing with normal request";
+ response().clear(false);
+ return false;
+ }
+
+ SignalHttpClientComplete(this, error);
+ return true;
+}
+
+HttpError HttpClient::ReadCacheHeaders(const std::string& id, bool override) {
+ scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheHeader));
+ if (!stream.get()) {
+ return HE_CACHE;
+ }
+
+ HttpData::HeaderCombine combine =
+ override ? HttpData::HC_REPLACE : HttpData::HC_AUTO;
+
+ if (!HttpReadCacheHeaders(stream.get(), &transaction_->response, combine)) {
+ LOG_F(LS_ERROR) << "Error reading cache headers";
+ return HE_CACHE;
+ }
+
+ response().scode = HC_OK;
+ return HE_NONE;
+}
+
+HttpError HttpClient::ReadCacheBody(const std::string& id) {
+ cache_state_ = CS_READING;
+
+ HttpError error = HE_NONE;
+
+ size_t data_size;
+ scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheBody));
+ if (!stream.get() || !stream->GetAvailable(&data_size)) {
+ LOG_F(LS_ERROR) << "Unavailable cache body";
+ error = HE_CACHE;
+ } else {
+ error = OnHeaderAvailable(false, false, data_size);
+ }
+
+ if ((HE_NONE == error)
+ && (HV_HEAD != request().verb)
+ && (NULL != response().document.get())) {
+ char buffer[1024 * 64];
+ StreamResult result = Flow(stream.get(), buffer, ARRAY_SIZE(buffer),
+ response().document.get());
+ if (SR_SUCCESS != result) {
+ error = HE_STREAM;
+ }
+ }
+
+ return error;
+}
+
+bool HttpClient::PrepareValidate() {
+ ASSERT(CS_READY == cache_state_);
+ // At this point, request() contains the pending request, and response()
+ // contains the cached response headers. Reformat the request to validate
+ // the cached content.
+ HttpValidatorStrength vs_required = HttpRequestValidatorLevel(request());
+ HttpValidatorStrength vs_available = HttpResponseValidatorLevel(response());
+ if (vs_available < vs_required) {
+ return false;
+ }
+ std::string value;
+ if (response().hasHeader(HH_ETAG, &value)) {
+ request().addHeader(HH_IF_NONE_MATCH, value);
+ }
+ if (response().hasHeader(HH_LAST_MODIFIED, &value)) {
+ request().addHeader(HH_IF_MODIFIED_SINCE, value);
+ }
+ response().clear(false);
+ cache_state_ = CS_VALIDATING;
+ return true;
+}
+
+HttpError HttpClient::CompleteValidate() {
+ ASSERT(CS_VALIDATING == cache_state_);
+
+ std::string id = GetCacheID(request());
+
+ // Merge cached headers with new headers
+ HttpError error = ReadCacheHeaders(id, false);
+ if (HE_NONE != error) {
+ // Rewrite merged headers to cache
+ CacheLock lock(cache_, id);
+ error = WriteCacheHeaders(id);
+ }
+ if (HE_NONE != error) {
+ error = ReadCacheBody(id);
+ }
+ return error;
+}
+
+HttpError HttpClient::OnHeaderAvailable(bool ignore_data, bool chunked,
+ size_t data_size) {
+ // If we are ignoring the data, this is an intermediate header.
+ // TODO: don't signal intermediate headers. Instead, do all header-dependent
+ // processing now, and either set up the next request, or fail outright.
+ // TODO: by default, only write response documents with a success code.
+ SignalHeaderAvailable(this, !ignore_data, ignore_data ? 0 : data_size);
+ if (!ignore_data && !chunked && (data_size != SIZE_UNKNOWN)
+ && response().document.get()) {
+ // Attempt to pre-allocate space for the downloaded data.
+ if (!response().document->ReserveSize(data_size)) {
+ return HE_OVERFLOW;
+ }
+ }
+ return HE_NONE;
+}
+
+//
+// HttpBase Implementation
+//
+
+HttpError HttpClient::onHttpHeaderComplete(bool chunked, size_t& data_size) {
+ if (CS_VALIDATING == cache_state_) {
+ if (HC_NOT_MODIFIED == response().scode) {
+ return CompleteValidate();
+ }
+ // Should we remove conditional headers from request?
+ cache_state_ = CS_READY;
+ cache_->DeleteResource(GetCacheID(request()));
+ // Continue processing response as normal
+ }
+
+ ASSERT(!IsCacheActive());
+ if ((request().verb == HV_HEAD) || !HttpCodeHasBody(response().scode)) {
+ // HEAD requests and certain response codes contain no body
+ data_size = 0;
+ }
+ if (ShouldRedirect(NULL)
+ || ((HC_PROXY_AUTHENTICATION_REQUIRED == response().scode)
+ && (PROXY_HTTPS == proxy_.type))) {
+ // We're going to issue another request, so ignore the incoming data.
+ base_.set_ignore_data(true);
+ }
+
+ HttpError error = OnHeaderAvailable(base_.ignore_data(), chunked, data_size);
+ if (HE_NONE != error) {
+ return error;
+ }
+
+ if ((NULL != cache_)
+ && !base_.ignore_data()
+ && HttpShouldCache(*transaction_)) {
+ if (BeginCacheFile()) {
+ cache_state_ = CS_WRITING;
+ }
+ }
+ return HE_NONE;
+}
+
+void HttpClient::onHttpComplete(HttpMode mode, HttpError err) {
+ if (((HE_DISCONNECTED == err) || (HE_CONNECT_FAILED == err)
+ || (HE_SOCKET_ERROR == err))
+ && (HC_INTERNAL_SERVER_ERROR == response().scode)
+ && (attempt_ < retries_)) {
+ // If the response code has not changed from the default, then we haven't
+ // received anything meaningful from the server, so we are eligible for a
+ // retry.
+ ++attempt_;
+ if (request().document.get() && !request().document->Rewind()) {
+ // Unable to replay the request document.
+ err = HE_STREAM;
+ } else {
+ release();
+ connect();
+ return;
+ }
+ } else if (err != HE_NONE) {
+ // fall through
+ } else if (mode == HM_CONNECT) {
+ base_.send(&transaction_->request);
+ return;
+ } else if ((mode == HM_SEND) || HttpCodeIsInformational(response().scode)) {
+ // If you're interested in informational headers, catch
+ // SignalHeaderAvailable.
+ base_.recv(&transaction_->response);
+ return;
+ } else {
+ if (!HttpShouldKeepAlive(response())) {
+ LOG(LS_VERBOSE) << "HttpClient: closing socket";
+ base_.stream()->Close();
+ }
+ std::string location;
+ if (ShouldRedirect(&location)) {
+ Url<char> purl(location);
+ set_server(SocketAddress(purl.host(), purl.port()));
+ request().path = purl.full_path();
+ if (response().scode == HC_SEE_OTHER) {
+ request().verb = HV_GET;
+ request().clearHeader(HH_CONTENT_TYPE);
+ request().clearHeader(HH_CONTENT_LENGTH);
+ request().document.reset();
+ } else if (request().document.get() && !request().document->Rewind()) {
+ // Unable to replay the request document.
+ ASSERT(REDIRECT_ALWAYS == redirect_action_);
+ err = HE_STREAM;
+ }
+ if (err == HE_NONE) {
+ ++redirects_;
+ context_.reset();
+ response().clear(false);
+ release();
+ start();
+ return;
+ }
+ } else if ((HC_PROXY_AUTHENTICATION_REQUIRED == response().scode)
+ && (PROXY_HTTPS == proxy_.type)) {
+ std::string authorization, auth_method;
+ HttpData::const_iterator begin = response().begin(HH_PROXY_AUTHENTICATE);
+ HttpData::const_iterator end = response().end(HH_PROXY_AUTHENTICATE);
+ for (HttpData::const_iterator it = begin; it != end; ++it) {
+ HttpAuthContext *context = context_.get();
+ HttpAuthResult res = HttpAuthenticate(
+ it->second.data(), it->second.size(),
+ proxy_.address,
+ ToString(request().verb), request().path,
+ proxy_.username, proxy_.password,
+ context, authorization, auth_method);
+ context_.reset(context);
+ if (res == HAR_RESPONSE) {
+ request().setHeader(HH_PROXY_AUTHORIZATION, authorization);
+ if (request().document.get() && !request().document->Rewind()) {
+ err = HE_STREAM;
+ } else {
+ // Explicitly do not reset the HttpAuthContext
+ response().clear(false);
+ // TODO: Reuse socket when authenticating?
+ release();
+ start();
+ return;
+ }
+ } else if (res == HAR_IGNORE) {
+ LOG(INFO) << "Ignoring Proxy-Authenticate: " << auth_method;
+ continue;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if (CS_WRITING == cache_state_) {
+ CompleteCacheFile();
+ cache_state_ = CS_READY;
+ } else if (CS_READING == cache_state_) {
+ cache_state_ = CS_READY;
+ }
+ release();
+ SignalHttpClientComplete(this, err);
+}
+
+void HttpClient::onHttpClosed(HttpError err) {
+ // This shouldn't occur, since we return the stream to the pool upon command
+ // completion.
+ ASSERT(false);
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpClientDefault
+//////////////////////////////////////////////////////////////////////
+
+HttpClientDefault::HttpClientDefault(SocketFactory* factory,
+ const std::string& agent,
+ HttpTransaction* transaction)
+ : ReuseSocketPool(factory ? factory : Thread::Current()->socketserver()),
+ HttpClient(agent, NULL, transaction) {
+ set_pool(this);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/httpclient.h b/third_party/libjingle/source/talk/base/httpclient.h
new file mode 100644
index 0000000..b64e55f
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httpclient.h
@@ -0,0 +1,213 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_HTTPCLIENT_H__
+#define TALK_BASE_HTTPCLIENT_H__
+
+#include "talk/base/common.h"
+#include "talk/base/httpbase.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/socketpool.h"
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Client-specific http utilities
+//////////////////////////////////////////////////////////////////////
+
+// Write cache-relevant response headers to output stream. If size is non-null,
+// it contains the length of the output in bytes. output may be null if only
+// the length is desired.
+bool HttpWriteCacheHeaders(const HttpResponseData* response,
+ StreamInterface* output, size_t* size);
+// Read cached headers from a stream, and them merge them into the response
+// object using the specified combine operation.
+bool HttpReadCacheHeaders(StreamInterface* input,
+ HttpResponseData* response,
+ HttpData::HeaderCombine combine);
+
+//////////////////////////////////////////////////////////////////////
+// HttpClient
+// Implements an HTTP 1.1 client.
+//////////////////////////////////////////////////////////////////////
+
+class DiskCache;
+class HttpClient;
+class IPNetPool;
+
+// What to do: Define STRICT_HTTP_ERROR=1 in your makefile. Use HttpError in
+// your code (HttpErrorType should only be used for code that is shared
+// with groups which have not yet migrated).
+#if STRICT_HTTP_ERROR
+typedef HttpError HttpErrorType;
+#else // !STRICT_HTTP_ERROR
+typedef int HttpErrorType;
+#endif // !STRICT_HTTP_ERROR
+
+class HttpClient : private IHttpNotify {
+public:
+ // If HttpRequestData and HttpResponseData objects are provided, they must
+ // be freed by the caller. Otherwise, an internal object is allocated.
+ HttpClient(const std::string& agent, StreamPool* pool,
+ HttpTransaction* transaction = NULL);
+ virtual ~HttpClient();
+
+ void set_pool(StreamPool* pool) { pool_ = pool; }
+
+ void set_agent(const std::string& agent) { agent_ = agent; }
+ const std::string& agent() const { return agent_; }
+
+ void set_proxy(const ProxyInfo& proxy) { proxy_ = proxy; }
+ const ProxyInfo& proxy() const { return proxy_; }
+
+ // Request retries occur when the connection closes before the beginning of
+ // an http response is received. In these cases, the http server may have
+ // timed out the keepalive connection before it received our request. Note
+ // that if a request document cannot be rewound, no retry is made. The
+ // default is 1.
+ void set_request_retries(size_t retries) { retries_ = retries; }
+ size_t request_retries() const { return retries_; }
+
+ enum RedirectAction { REDIRECT_DEFAULT, REDIRECT_ALWAYS, REDIRECT_NEVER };
+ void set_redirect_action(RedirectAction action) { redirect_action_ = action; }
+ RedirectAction redirect_action() const { return redirect_action_; }
+ // Deprecated
+ void set_fail_redirect(bool fail_redirect) {
+ redirect_action_ = REDIRECT_NEVER;
+ }
+ bool fail_redirect() const { return (REDIRECT_NEVER == redirect_action_); }
+
+ enum UriForm { URI_DEFAULT, URI_ABSOLUTE, URI_RELATIVE };
+ void set_uri_form(UriForm form) { uri_form_ = form; }
+ UriForm uri_form() const { return uri_form_; }
+
+ void set_cache(DiskCache* cache) { ASSERT(!IsCacheActive()); cache_ = cache; }
+ bool cache_enabled() const { return (NULL != cache_); }
+
+ // reset clears the server, request, and response structures. It will also
+ // abort an active request.
+ void reset();
+
+ void set_server(const SocketAddress& address);
+ const SocketAddress& server() const { return server_; }
+
+ // Note: in order for HttpClient to retry a POST in response to
+ // an authentication challenge, a redirect response, or socket disconnection,
+ // the request document must support 'replaying' by calling Rewind() on it.
+ // In the case where just a subset of a stream should be used as the request
+ // document, the stream may be wrapped with the StreamSegment adapter.
+ HttpTransaction* transaction() { return transaction_; }
+ const HttpTransaction* transaction() const { return transaction_; }
+ HttpRequestData& request() { return transaction_->request; }
+ const HttpRequestData& request() const { return transaction_->request; }
+ HttpResponseData& response() { return transaction_->response; }
+ const HttpResponseData& response() const { return transaction_->response; }
+
+ // convenience methods
+ void prepare_get(const std::string& url);
+ void prepare_post(const std::string& url, const std::string& content_type,
+ StreamInterface* request_doc);
+
+ // Convert HttpClient to a pull-based I/O model.
+ StreamInterface* GetDocumentStream();
+
+ // After you finish setting up your request, call start.
+ void start();
+
+ // Signalled when the header has finished downloading, before the document
+ // content is processed. You may change the response document in response
+ // to this signal. The second parameter indicates whether this is an
+ // intermediate (false) or final (true) header. An intermediate header is
+ // one that generates another request, such as a redirect or authentication
+ // challenge. The third parameter indicates the length of the response
+ // document, or else SIZE_UNKNOWN. Note: Do NOT abort the request in response
+ // to this signal.
+ sigslot::signal3<HttpClient*,bool,size_t> SignalHeaderAvailable;
+ // Signalled when the current request finishes. On success, err is 0.
+ sigslot::signal2<HttpClient*,HttpErrorType> SignalHttpClientComplete;
+
+protected:
+ void connect();
+ void release();
+
+ bool ShouldRedirect(std::string* location) const;
+
+ bool BeginCacheFile();
+ HttpError WriteCacheHeaders(const std::string& id);
+ void CompleteCacheFile();
+
+ bool CheckCache();
+ HttpError ReadCacheHeaders(const std::string& id, bool override);
+ HttpError ReadCacheBody(const std::string& id);
+
+ bool PrepareValidate();
+ HttpError CompleteValidate();
+
+ HttpError OnHeaderAvailable(bool ignore_data, bool chunked, size_t data_size);
+
+ // IHttpNotify Interface
+ virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size);
+ virtual void onHttpComplete(HttpMode mode, HttpError err);
+ virtual void onHttpClosed(HttpError err);
+
+private:
+ enum CacheState { CS_READY, CS_WRITING, CS_READING, CS_VALIDATING };
+ bool IsCacheActive() const { return (cache_state_ > CS_READY); }
+
+ std::string agent_;
+ StreamPool* pool_;
+ HttpBase base_;
+ SocketAddress server_;
+ ProxyInfo proxy_;
+ HttpTransaction* transaction_;
+ bool free_transaction_;
+ size_t retries_, attempt_, redirects_;
+ RedirectAction redirect_action_;
+ UriForm uri_form_;
+ scoped_ptr<HttpAuthContext> context_;
+ DiskCache* cache_;
+ CacheState cache_state_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpClientDefault - Default implementation of HttpClient
+//////////////////////////////////////////////////////////////////////
+
+class HttpClientDefault : public ReuseSocketPool, public HttpClient {
+public:
+ HttpClientDefault(SocketFactory* factory, const std::string& agent,
+ HttpTransaction* transaction = NULL);
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPCLIENT_H__
diff --git a/third_party/libjingle/source/talk/base/httpcommon-inl.h b/third_party/libjingle/source/talk/base/httpcommon-inl.h
new file mode 100644
index 0000000..a33a643
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httpcommon-inl.h
@@ -0,0 +1,143 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_HTTPCOMMON_INL_H__
+#define TALK_BASE_HTTPCOMMON_INL_H__
+
+#include "talk/base/common.h"
+#include "talk/base/httpcommon.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Url
+///////////////////////////////////////////////////////////////////////////////
+
+template<class CTYPE>
+void Url<CTYPE>::do_set_url(const CTYPE* val, size_t len) {
+ if (ascnicmp(val, "http://", 7) == 0) {
+ val += 7; len -= 7;
+ secure_ = false;
+ } else if (ascnicmp(val, "https://", 8) == 0) {
+ val += 8; len -= 8;
+ secure_ = true;
+ } else {
+ clear();
+ return;
+ }
+ const CTYPE* path = strchrn(val, len, static_cast<CTYPE>('/'));
+ if (!path) {
+ path = val + len;
+ }
+ size_t address_length = (path - val);
+ do_set_address(val, address_length);
+ do_set_full_path(path, len - address_length);
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_set_address(const CTYPE* val, size_t len) {
+ if (const CTYPE* colon = strchrn(val, len, static_cast<CTYPE>(':'))) {
+ host_.assign(val, colon - val);
+ // Note: In every case, we're guaranteed that colon is followed by a null,
+ // or non-numeric character.
+ port_ = static_cast<uint16>(::strtoul(colon + 1, NULL, 10));
+ // TODO: Consider checking for invalid data following port number.
+ } else {
+ host_.assign(val, len);
+ port_ = HttpDefaultPort(secure_);
+ }
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_set_full_path(const CTYPE* val, size_t len) {
+ const CTYPE* query = strchrn(val, len, static_cast<CTYPE>('?'));
+ if (!query) {
+ query = val + len;
+ }
+ size_t path_length = (query - val);
+ if (0 == path_length) {
+ // TODO: consider failing in this case.
+ path_.assign(1, static_cast<CTYPE>('/'));
+ } else {
+ ASSERT(val[0] == static_cast<CTYPE>('/'));
+ path_.assign(val, path_length);
+ }
+ query_.assign(query, len - path_length);
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_get_url(string* val) const {
+ CTYPE protocol[9];
+ asccpyn(protocol, ARRAY_SIZE(protocol), secure_ ? "https://" : "http://");
+ val->append(protocol);
+ do_get_address(val);
+ do_get_full_path(val);
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_get_address(string* val) const {
+ val->append(host_);
+ if (port_ != HttpDefaultPort(secure_)) {
+ CTYPE format[5], port[32];
+ asccpyn(format, ARRAY_SIZE(format), ":%hu");
+ sprintfn(port, ARRAY_SIZE(port), format, port_);
+ val->append(port);
+ }
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_get_full_path(string* val) const {
+ val->append(path_);
+ val->append(query_);
+}
+
+template<class CTYPE>
+bool Url<CTYPE>::get_attribute(const string& name, string* value) const {
+ if (query_.empty())
+ return false;
+
+ std::string::size_type pos = query_.find(name, 1);
+ if (std::string::npos == pos)
+ return false;
+
+ pos += name.length() + 1;
+ if ((pos > query_.length()) || (static_cast<CTYPE>('=') != query_[pos-1]))
+ return false;
+
+ std::string::size_type end = query_.find(static_cast<CTYPE>('&'), pos);
+ if (std::string::npos == end) {
+ end = query_.length();
+ }
+ value->assign(query_.substr(pos, end - pos));
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPCOMMON_INL_H__
diff --git a/third_party/libjingle/source/talk/base/httpcommon.cc b/third_party/libjingle/source/talk/base/httpcommon.cc
new file mode 100644
index 0000000..24c09e4
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httpcommon.cc
@@ -0,0 +1,1054 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <time.h>
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define SECURITY_WIN32
+#include <security.h>
+#endif
+
+#include "talk/base/httpcommon-inl.h"
+
+#include "talk/base/base64.h"
+#include "talk/base/common.h"
+#include "talk/base/cryptstring.h"
+#include "talk/base/httpcommon.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/stringdigest.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+
+namespace talk_base {
+
+#ifdef WIN32
+extern const ConstantLabel SECURITY_ERRORS[];
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Enum - TODO: expose globally later?
+//////////////////////////////////////////////////////////////////////
+
+bool find_string(size_t& index, const std::string& needle,
+ const char* const haystack[], size_t max_index) {
+ for (index=0; index<max_index; ++index) {
+ if (_stricmp(needle.c_str(), haystack[index]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template<class E>
+struct Enum {
+ static const char** Names;
+ static size_t Size;
+
+ static inline const char* Name(E val) { return Names[val]; }
+ static inline bool Parse(E& val, const std::string& name) {
+ size_t index;
+ if (!find_string(index, name, Names, Size))
+ return false;
+ val = static_cast<E>(index);
+ return true;
+ }
+
+ E val;
+
+ inline operator E&() { return val; }
+ inline Enum& operator=(E rhs) { val = rhs; return *this; }
+
+ inline const char* name() const { return Name(val); }
+ inline bool assign(const std::string& name) { return Parse(val, name); }
+ inline Enum& operator=(const std::string& rhs) { assign(rhs); return *this; }
+};
+
+#define ENUM(e,n) \
+ template<> const char** Enum<e>::Names = n; \
+ template<> size_t Enum<e>::Size = sizeof(n)/sizeof(n[0])
+
+//////////////////////////////////////////////////////////////////////
+// HttpCommon
+//////////////////////////////////////////////////////////////////////
+
+static const char* kHttpVersions[HVER_LAST+1] = {
+ "1.0", "1.1", "Unknown"
+};
+ENUM(HttpVersion, kHttpVersions);
+
+static const char* kHttpVerbs[HV_LAST+1] = {
+ "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD"
+};
+ENUM(HttpVerb, kHttpVerbs);
+
+static const char* kHttpHeaders[HH_LAST+1] = {
+ "Age",
+ "Cache-Control",
+ "Connection",
+ "Content-Disposition",
+ "Content-Length",
+ "Content-Range",
+ "Content-Type",
+ "Cookie",
+ "Date",
+ "ETag",
+ "Expires",
+ "Host",
+ "If-Modified-Since",
+ "If-None-Match",
+ "Keep-Alive",
+ "Last-Modified",
+ "Location",
+ "Proxy-Authenticate",
+ "Proxy-Authorization",
+ "Proxy-Connection",
+ "Range",
+ "Set-Cookie",
+ "TE",
+ "Trailers",
+ "Transfer-Encoding",
+ "Upgrade",
+ "User-Agent",
+ "WWW-Authenticate",
+};
+ENUM(HttpHeader, kHttpHeaders);
+
+const char* ToString(HttpVersion version) {
+ return Enum<HttpVersion>::Name(version);
+}
+
+bool FromString(HttpVersion& version, const std::string& str) {
+ return Enum<HttpVersion>::Parse(version, str);
+}
+
+const char* ToString(HttpVerb verb) {
+ return Enum<HttpVerb>::Name(verb);
+}
+
+bool FromString(HttpVerb& verb, const std::string& str) {
+ return Enum<HttpVerb>::Parse(verb, str);
+}
+
+const char* ToString(HttpHeader header) {
+ return Enum<HttpHeader>::Name(header);
+}
+
+bool FromString(HttpHeader& header, const std::string& str) {
+ return Enum<HttpHeader>::Parse(header, str);
+}
+
+bool HttpCodeHasBody(uint32 code) {
+ return !HttpCodeIsInformational(code)
+ && (code != HC_NO_CONTENT) && (code != HC_NOT_MODIFIED);
+}
+
+bool HttpCodeIsCacheable(uint32 code) {
+ switch (code) {
+ case HC_OK:
+ case HC_NON_AUTHORITATIVE:
+ case HC_PARTIAL_CONTENT:
+ case HC_MULTIPLE_CHOICES:
+ case HC_MOVED_PERMANENTLY:
+ case HC_GONE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool HttpHeaderIsEndToEnd(HttpHeader header) {
+ switch (header) {
+ case HH_CONNECTION:
+ case HH_KEEP_ALIVE:
+ case HH_PROXY_AUTHENTICATE:
+ case HH_PROXY_AUTHORIZATION:
+ case HH_PROXY_CONNECTION: // Note part of RFC... this is non-standard header
+ case HH_TE:
+ case HH_TRAILERS:
+ case HH_TRANSFER_ENCODING:
+ case HH_UPGRADE:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool HttpHeaderIsCollapsible(HttpHeader header) {
+ switch (header) {
+ case HH_SET_COOKIE:
+ case HH_PROXY_AUTHENTICATE:
+ case HH_WWW_AUTHENTICATE:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool HttpShouldKeepAlive(const HttpData& data) {
+ std::string connection;
+ if ((data.hasHeader(HH_PROXY_CONNECTION, &connection)
+ || data.hasHeader(HH_CONNECTION, &connection))) {
+ return (_stricmp(connection.c_str(), "Keep-Alive") == 0);
+ }
+ return (data.version >= HVER_1_1);
+}
+
+namespace {
+
+inline bool IsEndOfAttributeName(size_t pos, size_t len, const char * data) {
+ if (pos >= len)
+ return true;
+ if (isspace(static_cast<unsigned char>(data[pos])))
+ return true;
+ // The reason for this complexity is that some attributes may contain trailing
+ // equal signs (like base64 tokens in Negotiate auth headers)
+ if ((pos+1 < len) && (data[pos] == '=') &&
+ !isspace(static_cast<unsigned char>(data[pos+1])) &&
+ (data[pos+1] != '=')) {
+ return true;
+ }
+ return false;
+}
+
+// TODO: unittest for EscapeAttribute and HttpComposeAttributes.
+
+std::string EscapeAttribute(const std::string& attribute) {
+ const size_t kMaxLength = attribute.length() * 2 + 1;
+ char* buffer = STACK_ARRAY(char, kMaxLength);
+ size_t len = escape(buffer, kMaxLength, attribute.data(), attribute.length(),
+ "\"", '\\');
+ return std::string(buffer, len);
+}
+
+} // anonymous namespace
+
+void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
+ std::string* composed) {
+ std::stringstream ss;
+ for (size_t i=0; i<attributes.size(); ++i) {
+ if (i > 0) {
+ ss << separator << " ";
+ }
+ ss << attributes[i].first;
+ if (!attributes[i].second.empty()) {
+ ss << "=\"" << EscapeAttribute(attributes[i].second) << "\"";
+ }
+ }
+ *composed = ss.str();
+}
+
+void HttpParseAttributes(const char * data, size_t len,
+ HttpAttributeList& attributes) {
+ size_t pos = 0;
+ while (true) {
+ // Skip leading whitespace
+ while ((pos < len) && isspace(static_cast<unsigned char>(data[pos]))) {
+ ++pos;
+ }
+
+ // End of attributes?
+ if (pos >= len)
+ return;
+
+ // Find end of attribute name
+ size_t start = pos;
+ while (!IsEndOfAttributeName(pos, len, data)) {
+ ++pos;
+ }
+
+ HttpAttribute attribute;
+ attribute.first.assign(data + start, data + pos);
+
+ // Attribute has 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;
+ attribute.second.append(1, data[pos]);
+ }
+ } else {
+ while ((pos < len) &&
+ !isspace(static_cast<unsigned char>(data[pos])) &&
+ (data[pos] != ',')) {
+ attribute.second.append(1, data[pos++]);
+ }
+ }
+ }
+
+ attributes.push_back(attribute);
+ if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ','
+ }
+}
+
+bool HttpHasAttribute(const HttpAttributeList& attributes,
+ const std::string& name,
+ std::string* value) {
+ for (HttpAttributeList::const_iterator it = attributes.begin();
+ it != attributes.end(); ++it) {
+ if (it->first == name) {
+ if (value) {
+ *value = it->second;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool HttpHasNthAttribute(HttpAttributeList& attributes,
+ size_t index,
+ std::string* name,
+ std::string* value) {
+ if (index >= attributes.size())
+ return false;
+
+ if (name)
+ *name = attributes[index].first;
+ if (value)
+ *value = attributes[index].second;
+ return true;
+}
+
+bool HttpDateToSeconds(const std::string& date, unsigned long* seconds) {
+ const char* const kTimeZones[] = {
+ "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT",
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M",
+ "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"
+ };
+ const int kTimeZoneOffsets[] = {
+ 0, 0, -5, -4, -6, -5, -7, -6, -8, -7,
+ -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
+ };
+
+ ASSERT(NULL != seconds);
+ struct tm tval;
+ memset(&tval, 0, sizeof(tval));
+ char month[4], zone[6];
+ memset(month, 0, sizeof(month));
+ memset(zone, 0, sizeof(zone));
+
+ if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c",
+ &tval.tm_mday, month, &tval.tm_year,
+ &tval.tm_hour, &tval.tm_min, &tval.tm_sec, zone)) {
+ return false;
+ }
+ switch (toupper(month[2])) {
+ case 'N': tval.tm_mon = (month[1] == 'A') ? 0 : 5; break;
+ case 'B': tval.tm_mon = 1; break;
+ case 'R': tval.tm_mon = (month[0] == 'M') ? 2 : 3; break;
+ case 'Y': tval.tm_mon = 4; break;
+ case 'L': tval.tm_mon = 6; break;
+ case 'G': tval.tm_mon = 7; break;
+ case 'P': tval.tm_mon = 8; break;
+ case 'T': tval.tm_mon = 9; break;
+ case 'V': tval.tm_mon = 10; break;
+ case 'C': tval.tm_mon = 11; break;
+ }
+ tval.tm_year -= 1900;
+ unsigned long gmt, non_gmt = mktime(&tval);
+ if ((zone[0] == '+') || (zone[0] == '-')) {
+ if (!isdigit(zone[1]) || !isdigit(zone[2])
+ || !isdigit(zone[3]) || !isdigit(zone[4])) {
+ return false;
+ }
+ int hours = (zone[1] - '0') * 10 + (zone[2] - '0');
+ int minutes = (zone[3] - '0') * 10 + (zone[4] - '0');
+ int offset = (hours * 60 + minutes) * 60;
+ gmt = non_gmt + (zone[0] == '+') ? offset : -offset;
+ } else {
+ size_t zindex;
+ if (!find_string(zindex, zone, kTimeZones, ARRAY_SIZE(kTimeZones))) {
+ return false;
+ }
+ gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60;
+ }
+ // TODO: Android should support timezone, see b/2441195
+#if defined(OSX) || defined(ANDROID)
+ tm *tm_for_timezone = localtime((time_t *)&gmt);
+ *seconds = gmt + tm_for_timezone->tm_gmtoff;
+#else
+ *seconds = gmt - timezone;
+#endif
+ return true;
+}
+
+std::string HttpAddress(const SocketAddress& address, bool secure) {
+ return (address.port() == HttpDefaultPort(secure))
+ ? address.hostname() : address.ToString();
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpData
+//////////////////////////////////////////////////////////////////////
+
+void
+HttpData::clear(bool release_document) {
+ // Clear headers first, since releasing a document may have far-reaching
+ // effects.
+ headers_.clear();
+ if (release_document) {
+ document.reset();
+ }
+}
+
+void
+HttpData::copy(const HttpData& src) {
+ headers_ = src.headers_;
+}
+
+void
+HttpData::changeHeader(const std::string& name, const std::string& value,
+ HeaderCombine combine) {
+ if (combine == HC_AUTO) {
+ HttpHeader header;
+ // Unrecognized headers are collapsible
+ combine = !FromString(header, name) || HttpHeaderIsCollapsible(header)
+ ? HC_YES : HC_NO;
+ } else if (combine == HC_REPLACE) {
+ headers_.erase(name);
+ combine = HC_NO;
+ }
+ // At this point, combine is one of (YES, NO, NEW)
+ if (combine != HC_NO) {
+ HeaderMap::iterator it = headers_.find(name);
+ if (it != headers_.end()) {
+ if (combine == HC_YES) {
+ it->second.append(",");
+ it->second.append(value);
+ }
+ return;
+ }
+ }
+ headers_.insert(HeaderMap::value_type(name, value));
+}
+
+size_t HttpData::clearHeader(const std::string& name) {
+ return headers_.erase(name);
+}
+
+HttpData::iterator HttpData::clearHeader(iterator header) {
+ iterator deprecated = header++;
+ headers_.erase(deprecated);
+ return header;
+}
+
+bool
+HttpData::hasHeader(const std::string& name, std::string* value) const {
+ HeaderMap::const_iterator it = headers_.find(name);
+ if (it == headers_.end()) {
+ return false;
+ } else if (value) {
+ *value = it->second;
+ }
+ return true;
+}
+
+void HttpData::setContent(const std::string& content_type,
+ StreamInterface* document) {
+ setHeader(HH_CONTENT_TYPE, content_type);
+ setDocumentAndLength(document);
+}
+
+void HttpData::setDocumentAndLength(StreamInterface* document) {
+ // TODO: Consider calling Rewind() here?
+ ASSERT(!hasHeader(HH_CONTENT_LENGTH, NULL));
+ ASSERT(!hasHeader(HH_TRANSFER_ENCODING, NULL));
+ ASSERT(document != NULL);
+ this->document.reset(document);
+ size_t content_length = 0;
+ if (this->document->GetAvailable(&content_length)) {
+ char buffer[32];
+ sprintfn(buffer, sizeof(buffer), "%d", content_length);
+ setHeader(HH_CONTENT_LENGTH, buffer);
+ } else {
+ setHeader(HH_TRANSFER_ENCODING, "chunked");
+ }
+}
+
+//
+// HttpRequestData
+//
+
+void
+HttpRequestData::clear(bool release_document) {
+ verb = HV_GET;
+ path.clear();
+ HttpData::clear(release_document);
+}
+
+void
+HttpRequestData::copy(const HttpRequestData& src) {
+ verb = src.verb;
+ path = src.path;
+ HttpData::copy(src);
+}
+
+size_t
+HttpRequestData::formatLeader(char* buffer, size_t size) const {
+ ASSERT(path.find(' ') == std::string::npos);
+ return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(),
+ path.data(), ToString(version));
+}
+
+HttpError
+HttpRequestData::parseLeader(const char* line, size_t len) {
+ UNUSED(len);
+ unsigned int vmajor, vminor;
+ int vend, dstart, dend;
+ if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u", &vend, &dstart, &dend,
+ &vmajor, &vminor) != 2)
+ || (vmajor != 1)) {
+ return HE_PROTOCOL;
+ }
+ if (vminor == 0) {
+ version = HVER_1_0;
+ } else if (vminor == 1) {
+ version = HVER_1_1;
+ } else {
+ return HE_PROTOCOL;
+ }
+ std::string sverb(line, vend);
+ if (!FromString(verb, sverb.c_str())) {
+ return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED?
+ }
+ path.assign(line + dstart, line + dend);
+ return HE_NONE;
+}
+
+bool HttpRequestData::getAbsoluteUri(std::string* uri) const {
+ if (HV_CONNECT == verb)
+ return false;
+ Url<char> url(path);
+ if (url.valid()) {
+ uri->assign(path);
+ return true;
+ }
+ std::string host;
+ if (!hasHeader(HH_HOST, &host))
+ return false;
+ url.set_address(host);
+ url.set_full_path(path);
+ uri->assign(url.url());
+ return url.valid();
+}
+
+bool HttpRequestData::getRelativeUri(std::string* host,
+ std::string* path) const
+{
+ if (HV_CONNECT == verb)
+ return false;
+ Url<char> url(this->path);
+ if (url.valid()) {
+ host->assign(url.address());
+ path->assign(url.full_path());
+ return true;
+ }
+ if (!hasHeader(HH_HOST, host))
+ return false;
+ path->assign(this->path);
+ return true;
+}
+
+//
+// HttpResponseData
+//
+
+void
+HttpResponseData::clear(bool release_document) {
+ scode = HC_INTERNAL_SERVER_ERROR;
+ message.clear();
+ HttpData::clear(release_document);
+}
+
+void
+HttpResponseData::copy(const HttpResponseData& src) {
+ scode = src.scode;
+ message = src.message;
+ HttpData::copy(src);
+}
+
+void
+HttpResponseData::set_success(uint32 scode) {
+ this->scode = scode;
+ message.clear();
+ setHeader(HH_CONTENT_LENGTH, "0", false);
+}
+
+void
+HttpResponseData::set_success(const std::string& content_type,
+ StreamInterface* document,
+ uint32 scode) {
+ this->scode = scode;
+ message.erase(message.begin(), message.end());
+ setContent(content_type, document);
+}
+
+void
+HttpResponseData::set_redirect(const std::string& location, uint32 scode) {
+ this->scode = scode;
+ message.clear();
+ setHeader(HH_LOCATION, location);
+ setHeader(HH_CONTENT_LENGTH, "0", false);
+}
+
+void
+HttpResponseData::set_error(uint32 scode) {
+ this->scode = scode;
+ message.clear();
+ setHeader(HH_CONTENT_LENGTH, "0", false);
+}
+
+size_t
+HttpResponseData::formatLeader(char* buffer, size_t size) const {
+ size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode);
+ if (!message.empty()) {
+ len += sprintfn(buffer + len, size - len, " %.*s",
+ message.size(), message.data());
+ }
+ return len;
+}
+
+HttpError
+HttpResponseData::parseLeader(const char* line, size_t len) {
+ size_t pos = 0;
+ unsigned int vmajor, vminor, temp_scode;
+ int temp_pos;
+ if (sscanf(line, "HTTP %u%n",
+ &temp_scode, &temp_pos) == 1) {
+ // This server's response has no version. :( NOTE: This happens for every
+ // response to requests made from Chrome plugins, regardless of the server's
+ // behaviour.
+ LOG(LS_VERBOSE) << "HTTP version missing from response";
+ version = HVER_UNKNOWN;
+ } else if ((sscanf(line, "HTTP/%u.%u %u%n",
+ &vmajor, &vminor, &temp_scode, &temp_pos) == 3)
+ && (vmajor == 1)) {
+ // This server's response does have a version.
+ if (vminor == 0) {
+ version = HVER_1_0;
+ } else if (vminor == 1) {
+ version = HVER_1_1;
+ } else {
+ return HE_PROTOCOL;
+ }
+ } else {
+ return HE_PROTOCOL;
+ }
+ scode = temp_scode;
+ pos = static_cast<size_t>(temp_pos);
+ while ((pos < len) && isspace(static_cast<unsigned char>(line[pos]))) ++pos;
+ message.assign(line + pos, len - pos);
+ return HE_NONE;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Http Authentication
+//////////////////////////////////////////////////////////////////////
+
+#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
+
+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 HttpAuthContext {
+ CredHandle cred;
+ CtxtHandle ctx;
+ size_t steps;
+ bool specified_credentials;
+
+ NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2)
+ : HttpAuthContext(auth), cred(c1), ctx(c2), steps(0),
+ specified_credentials(false)
+ { }
+
+ virtual ~NegotiateAuthContext() {
+ DeleteSecurityContext(&ctx);
+ FreeCredentialsHandle(&cred);
+ }
+};
+#endif // WIN32
+
+HttpAuthResult HttpAuthenticate(
+ const char * challenge, size_t len,
+ const SocketAddress& server,
+ const std::string& method, const std::string& uri,
+ const std::string& username, const CryptString& password,
+ HttpAuthContext *& context, std::string& response, std::string& auth_method)
+{
+#if TEST_DIGEST
+ challenge = DIGEST_CHALLENGE;
+ len = strlen(challenge);
+#endif
+
+ HttpAttributeList args;
+ HttpParseAttributes(challenge, len, args);
+ HttpHasNthAttribute(args, 0, &auth_method, NULL);
+
+ if (context && (context->auth_method != auth_method))
+ return HAR_IGNORE;
+
+ // BASIC
+ if (_stricmp(auth_method.c_str(), "basic") == 0) {
+ if (context)
+ return HAR_CREDENTIALS; // Bad credentials
+ if (username.empty())
+ return HAR_CREDENTIALS; // Missing credentials
+
+ context = new HttpAuthContext(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 HAR_RESPONSE;
+ }
+
+ // DIGEST
+ if (_stricmp(auth_method.c_str(), "digest") == 0) {
+ if (context)
+ return HAR_CREDENTIALS; // Bad credentials
+ if (username.empty())
+ return HAR_CREDENTIALS; // Missing credentials
+
+ context = new HttpAuthContext(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", static_cast<int>(time(0)));
+ cnonce = MD5(buffer);
+#endif
+ ncount = "00000001";
+
+ std::string realm, nonce, qop, opaque;
+ HttpHasAttribute(args, "realm", &realm);
+ HttpHasAttribute(args, "nonce", &nonce);
+ bool has_qop = HttpHasAttribute(args, "qop", &qop);
+ bool has_opaque = HttpHasAttribute(args, "opaque", &opaque);
+
+ // TODO: convert sensitive to be secure buffer
+ //std::string A1 = username + ":" + realm + ":" + password;
+ size_t len = username.size() + 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, realm.c_str());
+ pos += strcpyn(sensitive + pos, len - pos, ":");
+ password.CopyTo(sensitive + pos, true);
+
+ std::string A2 = method + ":" + uri;
+ std::string middle;
+ if (has_qop) {
+ qop = "auth";
+ middle = nonce + ":" + ncount + ":" + cnonce + ":" + qop;
+ } else {
+ middle = 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(realm);
+ ss << ", nonce=" << quote(nonce);
+ ss << ", uri=" << quote(uri);
+ if (has_qop) {
+ ss << ", qop=" << qop;
+ ss << ", nc=" << ncount;
+ ss << ", cnonce=" << quote(cnonce);
+ }
+ ss << ", response=\"" << dig_response << "\"";
+ if (has_opaque) {
+ ss << ", opaque=" << quote(opaque);
+ }
+ response = ss.str();
+ return HAR_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_F(WARNING) << "(Negotiate) - DsMakeSpn failed";
+ return HAR_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 = 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 HAR_ERROR;
+ }
+ steps = neg->steps;
+
+ std::string challenge, decoded_challenge;
+ if (HttpHasNthAttribute(args, 1, &challenge, NULL)
+ && Base64::Decode(challenge, Base64::DO_STRICT,
+ &decoded_challenge, NULL)) {
+ 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 @ " << TimeSince(now);
+ if (FAILED(ret)) {
+ LOG(LS_ERROR) << "InitializeSecurityContext returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ return HAR_ERROR;
+ }
+ } else if (neg->specified_credentials) {
+ // Try again with default credentials
+ specify_credentials = false;
+ delete context;
+ context = neg = 0;
+ } else {
+ return HAR_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 @ " << TimeSince(now);
+ if (ret != SEC_E_OK) {
+ LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ return HAR_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 @ " << TimeSince(now);
+ if (FAILED(ret)) {
+ LOG(LS_ERROR) << "InitializeSecurityContext returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ FreeCredentialsHandle(&cred);
+ return HAR_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 @ " << TimeSince(now);
+ LOG(LS_VERBOSE) << "CompleteAuthToken returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ if (FAILED(ret)) {
+ return HAR_ERROR;
+ }
+ }
+
+ //LOG(INFO) << "$$$ NEGOTIATE took " << TimeSince(now) << "ms";
+
+ std::string decoded(out_buf, out_buf + out_sec.cbBuffer);
+ response = auth_method;
+ response.append(" ");
+ response.append(Base64::Encode(decoded));
+ return HAR_RESPONSE;
+ }
+#endif
+#endif // WIN32
+
+ return HAR_IGNORE;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/httpcommon.h b/third_party/libjingle/source/talk/base/httpcommon.h
new file mode 100644
index 0000000..cd630ec
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httpcommon.h
@@ -0,0 +1,463 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_HTTPCOMMON_H__
+#define TALK_BASE_HTTPCOMMON_H__
+
+#include <map>
+#include <string>
+#include <vector>
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/stream.h"
+
+namespace talk_base {
+
+class CryptString;
+class SocketAddress;
+
+//////////////////////////////////////////////////////////////////////
+// Constants
+//////////////////////////////////////////////////////////////////////
+
+enum HttpCode {
+ HC_OK = 200,
+ HC_NON_AUTHORITATIVE = 203,
+ HC_NO_CONTENT = 204,
+ HC_PARTIAL_CONTENT = 206,
+
+ HC_MULTIPLE_CHOICES = 300,
+ HC_MOVED_PERMANENTLY = 301,
+ HC_FOUND = 302,
+ HC_SEE_OTHER = 303,
+ HC_NOT_MODIFIED = 304,
+ HC_MOVED_TEMPORARILY = 307,
+
+ HC_BAD_REQUEST = 400,
+ HC_UNAUTHORIZED = 401,
+ HC_FORBIDDEN = 403,
+ HC_NOT_FOUND = 404,
+ HC_PROXY_AUTHENTICATION_REQUIRED = 407,
+ HC_GONE = 410,
+
+ HC_INTERNAL_SERVER_ERROR = 500,
+ HC_NOT_IMPLEMENTED = 501,
+ HC_SERVICE_UNAVAILABLE = 503,
+};
+
+enum HttpVersion {
+ HVER_1_0, HVER_1_1, HVER_UNKNOWN,
+ HVER_LAST = HVER_UNKNOWN
+};
+
+enum HttpVerb {
+ HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD,
+ HV_LAST = HV_HEAD
+};
+
+enum HttpError {
+ HE_NONE,
+ HE_PROTOCOL, // Received non-valid HTTP data
+ HE_DISCONNECTED, // Connection closed unexpectedly
+ HE_OVERFLOW, // Received too much data for internal buffers
+ HE_CONNECT_FAILED, // The socket failed to connect.
+ HE_SOCKET_ERROR, // An error occurred on a connected socket
+ HE_SHUTDOWN, // Http object is being destroyed
+ HE_OPERATION_CANCELLED, // Connection aborted locally
+ HE_AUTH, // Proxy Authentication Required
+ HE_CERTIFICATE_EXPIRED, // During SSL negotiation
+ HE_STREAM, // Problem reading or writing to the document
+ HE_CACHE, // Problem reading from cache
+ HE_DEFAULT
+};
+
+enum HttpHeader {
+ HH_AGE,
+ HH_CACHE_CONTROL,
+ HH_CONNECTION,
+ HH_CONTENT_DISPOSITION,
+ HH_CONTENT_LENGTH,
+ HH_CONTENT_RANGE,
+ HH_CONTENT_TYPE,
+ HH_COOKIE,
+ HH_DATE,
+ HH_ETAG,
+ HH_EXPIRES,
+ HH_HOST,
+ HH_IF_MODIFIED_SINCE,
+ HH_IF_NONE_MATCH,
+ HH_KEEP_ALIVE,
+ HH_LAST_MODIFIED,
+ HH_LOCATION,
+ HH_PROXY_AUTHENTICATE,
+ HH_PROXY_AUTHORIZATION,
+ HH_PROXY_CONNECTION,
+ HH_RANGE,
+ HH_SET_COOKIE,
+ HH_TE,
+ HH_TRAILERS,
+ HH_TRANSFER_ENCODING,
+ HH_UPGRADE,
+ HH_USER_AGENT,
+ HH_WWW_AUTHENTICATE,
+ HH_LAST = HH_WWW_AUTHENTICATE
+};
+
+const uint16 HTTP_DEFAULT_PORT = 80;
+const uint16 HTTP_SECURE_PORT = 443;
+
+//////////////////////////////////////////////////////////////////////
+// Utility Functions
+//////////////////////////////////////////////////////////////////////
+
+inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) {
+ return (err != HE_NONE) ? err : def_err;
+}
+
+const char* ToString(HttpVersion version);
+bool FromString(HttpVersion& version, const std::string& str);
+
+const char* ToString(HttpVerb verb);
+bool FromString(HttpVerb& verb, const std::string& str);
+
+const char* ToString(HttpHeader header);
+bool FromString(HttpHeader& header, const std::string& str);
+
+inline bool HttpCodeIsInformational(uint32 code) { return ((code / 100) == 1); }
+inline bool HttpCodeIsSuccessful(uint32 code) { return ((code / 100) == 2); }
+inline bool HttpCodeIsRedirection(uint32 code) { return ((code / 100) == 3); }
+inline bool HttpCodeIsClientError(uint32 code) { return ((code / 100) == 4); }
+inline bool HttpCodeIsServerError(uint32 code) { return ((code / 100) == 5); }
+
+bool HttpCodeHasBody(uint32 code);
+bool HttpCodeIsCacheable(uint32 code);
+bool HttpHeaderIsEndToEnd(HttpHeader header);
+bool HttpHeaderIsCollapsible(HttpHeader header);
+
+struct HttpData;
+bool HttpShouldKeepAlive(const HttpData& data);
+
+typedef std::pair<std::string, std::string> HttpAttribute;
+typedef std::vector<HttpAttribute> HttpAttributeList;
+void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
+ std::string* composed);
+void HttpParseAttributes(const char * data, size_t len,
+ HttpAttributeList& attributes);
+bool HttpHasAttribute(const HttpAttributeList& attributes,
+ const std::string& name,
+ std::string* value);
+bool HttpHasNthAttribute(HttpAttributeList& attributes,
+ size_t index,
+ std::string* name,
+ std::string* value);
+
+// Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp
+bool HttpDateToSeconds(const std::string& date, unsigned long* seconds);
+
+inline const uint16 HttpDefaultPort(bool secure) {
+ return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT;
+}
+
+// Returns the http server notation for a given address
+std::string HttpAddress(const SocketAddress& address, bool secure);
+
+// functional for insensitive std::string compare
+struct iless {
+ bool operator()(const std::string& lhs, const std::string& rhs) const {
+ return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0);
+ }
+};
+
+// put quotes around a string and escape any quotes inside it
+std::string quote(const std::string& str);
+
+//////////////////////////////////////////////////////////////////////
+// Url
+//////////////////////////////////////////////////////////////////////
+
+template<class CTYPE>
+class Url {
+public:
+ typedef typename Traits<CTYPE>::string string;
+
+ // TODO(bpm): Implement Encode/Decode
+ static int Encode(const CTYPE* source, CTYPE* destination, size_t len);
+ static int Encode(const string& source, string& destination);
+ static int Decode(const CTYPE* source, CTYPE* destination, size_t len);
+ static int Decode(const string& source, string& destination);
+
+ Url(const string& url) { do_set_url(url.c_str(), url.size()); }
+ Url(const string& path, const string& host, uint16 port = HTTP_DEFAULT_PORT)
+ : host_(host), port_(port), secure_(HTTP_SECURE_PORT == port)
+ { set_full_path(path); }
+
+ bool valid() const { return !host_.empty(); }
+ void clear() {
+ host_.clear();
+ port_ = HTTP_DEFAULT_PORT;
+ secure_ = false;
+ path_.assign(1, static_cast<CTYPE>('/'));
+ query_.clear();
+ }
+
+ void set_url(const string& val) {
+ do_set_url(val.c_str(), val.size());
+ }
+ string url() const {
+ string val; do_get_url(&val); return val;
+ }
+
+ void set_address(const string& val) {
+ do_set_address(val.c_str(), val.size());
+ }
+ string address() const {
+ string val; do_get_address(&val); return val;
+ }
+
+ void set_full_path(const string& val) {
+ do_set_full_path(val.c_str(), val.size());
+ }
+ string full_path() const {
+ string val; do_get_full_path(&val); return val;
+ }
+
+ void set_host(const string& val) { host_ = val; }
+ const string& host() const { return host_; }
+
+ void set_port(uint16 val) { port_ = val; }
+ uint16 port() const { return port_; }
+
+ void set_secure(bool val) { secure_ = val; }
+ bool secure() const { return secure_; }
+
+ void set_path(const string& val) {
+ if (val.empty()) {
+ path_.assign(1, static_cast<CTYPE>('/'));
+ } else {
+ ASSERT(val[0] == static_cast<CTYPE>('/'));
+ path_ = val;
+ }
+ }
+ const string& path() const { return path_; }
+
+ void set_query(const string& val) {
+ ASSERT(val.empty() || (val[0] == static_cast<CTYPE>('?')));
+ query_ = val;
+ }
+ const string& query() const { return query_; }
+
+ bool get_attribute(const string& name, string* value) const;
+
+private:
+ void do_set_url(const CTYPE* val, size_t len);
+ void do_set_address(const CTYPE* val, size_t len);
+ void do_set_full_path(const CTYPE* val, size_t len);
+
+ void do_get_url(string* val) const;
+ void do_get_address(string* val) const;
+ void do_get_full_path(string* val) const;
+
+ string host_, path_, query_;
+ uint16 port_;
+ bool secure_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpData
+//////////////////////////////////////////////////////////////////////
+
+struct HttpData {
+ typedef std::multimap<std::string, std::string, iless> HeaderMap;
+ typedef HeaderMap::const_iterator const_iterator;
+ typedef HeaderMap::iterator iterator;
+
+ HttpVersion version;
+ scoped_ptr<StreamInterface> document;
+
+ HttpData() : version(HVER_1_1) { }
+
+ enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW };
+ void changeHeader(const std::string& name, const std::string& value,
+ HeaderCombine combine);
+ inline void addHeader(const std::string& name, const std::string& value,
+ bool append = true) {
+ changeHeader(name, value, append ? HC_AUTO : HC_NO);
+ }
+ inline void setHeader(const std::string& name, const std::string& value,
+ bool overwrite = true) {
+ changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW);
+ }
+ // Returns count of erased headers
+ size_t clearHeader(const std::string& name);
+ // Returns iterator to next header
+ iterator clearHeader(iterator header);
+
+ // keep in mind, this may not do what you want in the face of multiple headers
+ bool hasHeader(const std::string& name, std::string* value) const;
+
+ inline const_iterator begin() const {
+ return headers_.begin();
+ }
+ inline const_iterator end() const {
+ return headers_.end();
+ }
+ inline iterator begin() {
+ return headers_.begin();
+ }
+ inline iterator end() {
+ return headers_.end();
+ }
+ inline const_iterator begin(const std::string& name) const {
+ return headers_.lower_bound(name);
+ }
+ inline const_iterator end(const std::string& name) const {
+ return headers_.upper_bound(name);
+ }
+ inline iterator begin(const std::string& name) {
+ return headers_.lower_bound(name);
+ }
+ inline iterator end(const std::string& name) {
+ return headers_.upper_bound(name);
+ }
+
+ // Convenience methods using HttpHeader
+ inline void changeHeader(HttpHeader header, const std::string& value,
+ HeaderCombine combine) {
+ changeHeader(ToString(header), value, combine);
+ }
+ inline void addHeader(HttpHeader header, const std::string& value,
+ bool append = true) {
+ addHeader(ToString(header), value, append);
+ }
+ inline void setHeader(HttpHeader header, const std::string& value,
+ bool overwrite = true) {
+ setHeader(ToString(header), value, overwrite);
+ }
+ inline void clearHeader(HttpHeader header) {
+ clearHeader(ToString(header));
+ }
+ inline bool hasHeader(HttpHeader header, std::string* value) const {
+ return hasHeader(ToString(header), value);
+ }
+ inline const_iterator begin(HttpHeader header) const {
+ return headers_.lower_bound(ToString(header));
+ }
+ inline const_iterator end(HttpHeader header) const {
+ return headers_.upper_bound(ToString(header));
+ }
+ inline iterator begin(HttpHeader header) {
+ return headers_.lower_bound(ToString(header));
+ }
+ inline iterator end(HttpHeader header) {
+ return headers_.upper_bound(ToString(header));
+ }
+
+ void setContent(const std::string& content_type, StreamInterface* document);
+ void setDocumentAndLength(StreamInterface* document);
+
+ virtual size_t formatLeader(char* buffer, size_t size) const = 0;
+ virtual HttpError parseLeader(const char* line, size_t len) = 0;
+
+protected:
+ virtual ~HttpData() { }
+ void clear(bool release_document);
+ void copy(const HttpData& src);
+
+private:
+ HeaderMap headers_;
+};
+
+struct HttpRequestData : public HttpData {
+ HttpVerb verb;
+ std::string path;
+
+ HttpRequestData() : verb(HV_GET) { }
+
+ void clear(bool release_document);
+ void copy(const HttpRequestData& src);
+
+ virtual size_t formatLeader(char* buffer, size_t size) const;
+ virtual HttpError parseLeader(const char* line, size_t len);
+
+ bool getAbsoluteUri(std::string* uri) const;
+ bool getRelativeUri(std::string* host, std::string* path) const;
+};
+
+struct HttpResponseData : public HttpData {
+ uint32 scode;
+ std::string message;
+
+ HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) { }
+ void clear(bool release_document);
+ void copy(const HttpResponseData& src);
+
+ // Convenience methods
+ void set_success(uint32 scode = HC_OK);
+ void set_success(const std::string& content_type, StreamInterface* document,
+ uint32 scode = HC_OK);
+ void set_redirect(const std::string& location,
+ uint32 scode = HC_MOVED_TEMPORARILY);
+ void set_error(uint32 scode);
+
+ virtual size_t formatLeader(char* buffer, size_t size) const;
+ virtual HttpError parseLeader(const char* line, size_t len);
+};
+
+struct HttpTransaction {
+ HttpRequestData request;
+ HttpResponseData response;
+};
+
+//////////////////////////////////////////////////////////////////////
+// Http Authentication
+//////////////////////////////////////////////////////////////////////
+
+struct HttpAuthContext {
+ std::string auth_method;
+ HttpAuthContext(const std::string& auth) : auth_method(auth) { }
+ virtual ~HttpAuthContext() { }
+};
+
+enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR };
+
+// '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.
+HttpAuthResult HttpAuthenticate(
+ const char * challenge, size_t len,
+ const SocketAddress& server,
+ const std::string& method, const std::string& uri,
+ const std::string& username, const CryptString& password,
+ HttpAuthContext *& context, std::string& response, std::string& auth_method);
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_HTTPCOMMON_H__
diff --git a/third_party/libjingle/source/talk/base/httprequest.cc b/third_party/libjingle/source/talk/base/httprequest.cc
new file mode 100644
index 0000000..48c924e
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httprequest.cc
@@ -0,0 +1,127 @@
+/*
+ * libjingle
+ * Copyright 2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/httprequest.h"
+
+#include "talk/base/common.h"
+#include "talk/base/firewallsocketserver.h"
+#include "talk/base/httpclient.h"
+#include "talk/base/logging.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/socketpool.h"
+#include "talk/base/ssladapter.h"
+
+using namespace talk_base;
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpMonitor
+///////////////////////////////////////////////////////////////////////////////
+
+HttpMonitor::HttpMonitor(SocketServer *ss) {
+ ASSERT(Thread::Current() != NULL);
+ ss_ = ss;
+ reset();
+}
+
+void HttpMonitor::Connect(HttpClient *http) {
+ http->SignalHttpClientComplete.connect(this,
+ &HttpMonitor::OnHttpClientComplete);
+}
+
+void HttpMonitor::OnHttpClientComplete(HttpClient * http, HttpErrorType error) {
+ complete_ = true;
+ error_ = error;
+ ss_->WakeUp();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpRequest
+///////////////////////////////////////////////////////////////////////////////
+
+const int kDefaultHTTPTimeout = 30 * 1000; // 30 sec
+
+HttpRequest::HttpRequest(const std::string &user_agent)
+ : firewall_(0), port_(80), secure_(false),
+ timeout_(kDefaultHTTPTimeout), fail_redirect_(false),
+ client_(user_agent.c_str(), NULL), error_(HE_NONE) {
+}
+
+void HttpRequest::Send() {
+ // TODO: Rewrite this to use the thread's native socket server, and a more
+ // natural flow?
+
+ PhysicalSocketServer physical;
+ SocketServer * ss = &physical;
+ if (firewall_) {
+ ss = new FirewallSocketServer(ss, firewall_);
+ }
+
+ SslSocketFactory factory(ss, client_.agent());
+ factory.SetProxy(proxy_);
+ if (secure_)
+ factory.UseSSL(host_.c_str());
+
+ //factory.SetLogging("HttpRequest");
+
+ ReuseSocketPool pool(&factory);
+ client_.set_pool(&pool);
+
+ bool transparent_proxy = (port_ == 80) && ((proxy_.type == PROXY_HTTPS) ||
+ (proxy_.type == PROXY_UNKNOWN));
+
+ if (transparent_proxy) {
+ client_.set_proxy(proxy_);
+ }
+ client_.set_fail_redirect(fail_redirect_);
+
+ SocketAddress server(host_, port_);
+ client_.set_server(server);
+
+ LOG(LS_INFO) << "HttpRequest start: " << host_ + client_.request().path;
+
+ HttpMonitor monitor(ss);
+ monitor.Connect(&client_);
+ client_.start();
+ ss->Wait(timeout_, true);
+ if (!monitor.done()) {
+ LOG(LS_INFO) << "HttpRequest request timed out";
+ client_.reset();
+ return;
+ }
+
+ set_error(monitor.error());
+ if (error_) {
+ LOG(LS_INFO) << "HttpRequest request error: " << error_;
+ return;
+ }
+
+ std::string value;
+ if (client_.response().hasHeader(HH_LOCATION, &value)) {
+ response_redirect_ = value.c_str();
+ }
+}
diff --git a/third_party/libjingle/source/talk/base/httprequest.h b/third_party/libjingle/source/talk/base/httprequest.h
new file mode 100644
index 0000000..2e13c32
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/httprequest.h
@@ -0,0 +1,132 @@
+/*
+ * libjingle
+ * Copyright 2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _HTTPREQUEST_H_
+#define _HTTPREQUEST_H_
+
+#include "talk/base/httpclient.h"
+#include "talk/base/logging.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/thread.h"
+#include "talk/base/sslsocketfactory.h" // Deprecated include
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpRequest
+///////////////////////////////////////////////////////////////////////////////
+
+class FirewallManager;
+class MemoryStream;
+
+class HttpRequest {
+public:
+ HttpRequest(const std::string &user_agent);
+
+ void Send();
+
+ void set_proxy(const ProxyInfo& proxy) {
+ proxy_ = proxy;
+ }
+ void set_firewall(FirewallManager * firewall) {
+ firewall_ = firewall;
+ }
+
+ // The DNS name of the host to connect to.
+ const std::string& host() { return host_; }
+ void set_host(const std::string& host) { host_ = host; }
+
+ // The port to connect to on the target host.
+ int port() { return port_; }
+ void set_port(int port) { port_ = port; }
+
+ // Whether the request should use SSL.
+ bool secure() { return secure_; }
+ void set_secure(bool secure) { secure_ = secure; }
+
+ // Returns the redirect when redirection occurs
+ const std::string& response_redirect() { return response_redirect_; }
+
+ // Time to wait on the download, in ms. Default is 5000 (5s)
+ int timeout() { return timeout_; }
+ void set_timeout(int timeout) { timeout_ = timeout; }
+
+ // Fail redirects to allow analysis of redirect urls, etc.
+ bool fail_redirect() const { return fail_redirect_; }
+ void set_fail_redirect(bool fail_redirect) { fail_redirect_ = fail_redirect; }
+
+ HttpRequestData& request() { return client_.request(); }
+ HttpResponseData& response() { return client_.response(); }
+ HttpErrorType error() { return error_; }
+
+protected:
+ void set_error(HttpErrorType error) { error_ = error; }
+
+private:
+ ProxyInfo proxy_;
+ FirewallManager * firewall_;
+ std::string host_;
+ int port_;
+ bool secure_;
+ int timeout_;
+ bool fail_redirect_;
+ HttpClient client_;
+ HttpErrorType error_;
+ std::string response_redirect_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpMonitor
+///////////////////////////////////////////////////////////////////////////////
+
+class HttpMonitor : public sigslot::has_slots<> {
+public:
+ HttpMonitor(SocketServer *ss);
+
+ void reset() {
+ complete_ = false;
+ error_ = HE_DEFAULT;
+ }
+
+ bool done() const { return complete_; }
+ HttpErrorType error() const { return error_; }
+
+ void Connect(HttpClient* http);
+ void OnHttpClientComplete(HttpClient * http, HttpErrorType error);
+
+private:
+ bool complete_;
+ HttpErrorType error_;
+ SocketServer *ss_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base_
+
+#endif // _HTTPREQUEST_H_
diff --git a/third_party/libjingle/source/talk/base/linked_ptr.h b/third_party/libjingle/source/talk/base/linked_ptr.h
new file mode 100644
index 0000000..a98a367
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/linked_ptr.h
@@ -0,0 +1,142 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 TALK_BASE_LINKED_PTR_H__
+#define TALK_BASE_LINKED_PTR_H__
+
+namespace talk_base {
+
+/* 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;
+ }
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_LINKED_PTR_H__
+
diff --git a/third_party/libjingle/source/talk/base/linux.cc b/third_party/libjingle/source/talk/base/linux.cc
new file mode 100644
index 0000000..6a798cc
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/linux.cc
@@ -0,0 +1,219 @@
+// Copyright 2008 Google Inc. All Rights Reserved.
+// Author: araju@google.com (Avanish Raju)
+//
+
+#ifdef LINUX
+#include "talk/base/linux.h"
+
+#include <errno.h>
+#include <sys/utsname.h>
+
+#include <cstdio>
+
+#include "talk/base/stringencode.h"
+
+namespace talk_base {
+
+ProcCpuInfo::ProcCpuInfo() {
+}
+
+ProcCpuInfo::~ProcCpuInfo() {
+}
+
+bool ProcCpuInfo::LoadFromSystem() {
+ ConfigParser procfs;
+ if (!procfs.Open("/proc/cpuinfo"))
+ return false;
+ return procfs.Parse(&cpu_info_);
+};
+
+bool ProcCpuInfo::GetNumCpus(int *num) {
+ if (cpu_info_.size() == 0)
+ return false;
+ *num = cpu_info_.size();
+ return true;
+}
+
+bool ProcCpuInfo::GetCpuStringValue(int cpu_id, const std::string& key,
+ std::string *result) {
+ if (cpu_id >= static_cast<int>(cpu_info_.size()))
+ return false;
+ ConfigParser::SimpleMap::iterator iter = cpu_info_[cpu_id].find(key);
+ if (iter == cpu_info_[cpu_id].end())
+ return false;
+ *result = iter->second;
+ return true;
+}
+
+bool ProcCpuInfo::GetCpuIntValue(int cpu_id, const std::string& key,
+ int *result) {
+ if (cpu_id >= static_cast<int>(cpu_info_.size())) {
+ return false;
+ }
+ ConfigParser::SimpleMap::iterator iter = cpu_info_[cpu_id].find(key);
+ if (iter == cpu_info_[cpu_id].end()) {
+ return false;
+ }
+ *result = atoi((iter->second).c_str());
+ return true;
+}
+
+ConfigParser::ConfigParser() {}
+
+ConfigParser::~ConfigParser() {}
+
+bool ConfigParser::Open(const std::string& filename) {
+ FileStream *fs = new FileStream();
+ if (!fs->Open(filename, "r"))
+ return false;
+ instream_.reset(fs);
+ return true;
+}
+
+void ConfigParser::Attach(StreamInterface* stream) {
+ instream_.reset(stream);
+}
+
+bool ConfigParser::Parse(MapVector *key_val_pairs) {
+ // Parses the file and places the found key-value pairs into key_val_pairs.
+ SimpleMap section;
+ while (ParseSection(&section)) {
+ key_val_pairs->push_back(section);
+ section.clear();
+ }
+ return (!key_val_pairs->empty());
+}
+
+bool ConfigParser::ParseSection(SimpleMap *key_val_pair) {
+ // Parses the next section in the filestream and places the found key-value
+ // pairs into key_val_pair.
+ std::string key, value;
+ while (ParseLine(&key, &value)) {
+ (*key_val_pair)[key] = value;
+ }
+ return (!key_val_pair->empty());
+}
+
+bool ConfigParser::ParseLine(std::string *key, std::string *value) {
+ // Parses the next line in the filestream and places the found key-value
+ // pair into key and val.
+ std::string line;
+ if ((instream_->ReadLine(&line)) == EOF)
+ return false;
+ std::vector<std::string> tokens;
+ if (2 != split(line, ':', &tokens))
+ return false;
+ // Removes whitespace at the end of Key name
+ size_t pos = tokens[0].length() - 1;
+ while ((pos > 0) && isspace(tokens[0][pos]))
+ pos--;
+ tokens[0].erase(pos + 1);
+ // Removes whitespace at the start of value
+ pos = 0;
+ while (pos < tokens[1].length() && isspace(tokens[1][pos]))
+ pos++;
+ tokens[1].erase(0, pos);
+ *key = tokens[0];
+ *value = tokens[1];
+ return true;
+}
+
+static bool ExpectLineFromStream(FileStream *stream,
+ std::string *out) {
+ StreamResult res = stream->ReadLine(out);
+ if (res != SR_SUCCESS) {
+ if (res != SR_EOS) {
+ LOG(LS_ERROR) << "Error when reading from stream";
+ } else {
+ LOG(LS_ERROR) << "Incorrect number of lines in stream";
+ }
+ return false;
+ }
+ return true;
+}
+
+static void ExpectEofFromStream(FileStream *stream) {
+ std::string unused;
+ StreamResult res = stream->ReadLine(&unused);
+ if (res == SR_SUCCESS) {
+ LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream";
+ } else if (res != SR_EOS) {
+ LOG(LS_WARNING) << "Error when checking for extra lines from stream";
+ }
+}
+
+// For caching the lsb_release output (reading it invokes a sub-process and
+// hence is somewhat expensive).
+static std::string lsb_release_string;
+static CriticalSection lsb_release_string_critsec;
+
+std::string ReadLinuxLsbRelease() {
+ CritScope cs(&lsb_release_string_critsec);
+ if (!lsb_release_string.empty()) {
+ // Have cached result from previous call.
+ return lsb_release_string;
+ }
+ // No cached result. Run lsb_release and parse output.
+ POpenStream lsb_release_output;
+ if (!lsb_release_output.Open("lsb_release -idrcs", "r")) {
+ LOG_ERR(LS_ERROR) << "Can't run lsb_release";
+ return lsb_release_string; // empty
+ }
+ // Read in the command's output and build the string.
+ std::ostringstream sstr;
+ std::string line;
+ int wait_status;
+
+ if (!ExpectLineFromStream(&lsb_release_output, &line)) {
+ return lsb_release_string; // empty
+ }
+ sstr << "DISTRIB_ID=" << line;
+
+ if (!ExpectLineFromStream(&lsb_release_output, &line)) {
+ return lsb_release_string; // empty
+ }
+ sstr << " DISTRIB_DESCRIPTION=\"" << line << '"';
+
+ if (!ExpectLineFromStream(&lsb_release_output, &line)) {
+ return lsb_release_string; // empty
+ }
+ sstr << " DISTRIB_RELEASE=" << line;
+
+ if (!ExpectLineFromStream(&lsb_release_output, &line)) {
+ return lsb_release_string; // empty
+ }
+ sstr << " DISTRIB_CODENAME=" << line;
+
+ // Should not be anything left.
+ ExpectEofFromStream(&lsb_release_output);
+
+ lsb_release_output.Close();
+ wait_status = lsb_release_output.GetWaitStatus();
+ if (wait_status == -1 ||
+ !WIFEXITED(wait_status) ||
+ WEXITSTATUS(wait_status) != 0) {
+ LOG(LS_WARNING) << "Unexpected exit status from lsb_release";
+ }
+
+ lsb_release_string = sstr.str();
+
+ return lsb_release_string;
+}
+
+std::string ReadLinuxUname() {
+ struct utsname buf;
+ if (uname(&buf) < 0) {
+ LOG_ERR(LS_ERROR) << "Can't call uname()";
+ return std::string();
+ }
+ std::ostringstream sstr;
+ sstr << buf.sysname << " "
+ << buf.release << " "
+ << buf.version << " "
+ << buf.machine;
+ return sstr.str();
+}
+
+} // namespace talk_base
+
+#endif // LINUX
diff --git a/third_party/libjingle/source/talk/base/linux.h b/third_party/libjingle/source/talk/base/linux.h
new file mode 100644
index 0000000..ef775ed
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/linux.h
@@ -0,0 +1,97 @@
+// Copyright 2008 Google Inc. All Rights Reserved.
+// Author: araju@google.com (Avanish Raju)
+
+#ifndef TALK_BASE_LINUX_H_
+#define TALK_BASE_LINUX_H_
+
+#ifdef LINUX
+#include <string>
+#include <map>
+#include <vector>
+#include "talk/base/stream.h"
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////////////
+// ConfigParser parses a FileStream of an ".ini."-type format into a map.
+//////////////////////////////////////////////////////////////////////////////
+
+// Sample Usage:
+// ConfigParser parser;
+// ConfigParser::MapVector key_val_pairs;
+// if (parser.Open(inifile) && parser.Parse(&key_val_pairs)) {
+// for (int section_num=0; i < key_val_pairs.size(); ++section_num) {
+// std::string val1 = key_val_pairs[section_num][key1];
+// std::string val2 = key_val_pairs[section_num][key2];
+// // Do something with valn;
+// }
+// }
+
+class ConfigParser {
+ public:
+ typedef std::map<std::string, std::string> SimpleMap;
+ typedef std::vector<SimpleMap> MapVector;
+
+ ConfigParser();
+ virtual ~ConfigParser();
+
+ virtual bool Open(const std::string& filename);
+ virtual void Attach(StreamInterface* stream);
+ virtual bool Parse(MapVector *key_val_pairs);
+ virtual bool ParseSection(SimpleMap *key_val_pair);
+ virtual bool ParseLine(std::string *key, std::string *value);
+
+ private:
+ scoped_ptr<StreamInterface> instream_;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// ProcCpuInfo reads CPU info from the /proc subsystem on any *NIX platform.
+//////////////////////////////////////////////////////////////////////////////
+
+// Sample Usage:
+// ProcCpuInfo proc_info;
+// int no_of_cpu;
+// if (proc_info.LoadFromSystem()) {
+// std::string out_str;
+// proc_info.GetNumCpus(&no_of_cpu);
+// proc_info.GetCpuStringValue(0, "vendor_id", &out_str);
+// }
+// }
+
+class ProcCpuInfo {
+ public:
+ ProcCpuInfo();
+ virtual ~ProcCpuInfo();
+
+ // Reads the proc subsystem's cpu info into memory. If this fails, this
+ // returns false; if it succeeds, it returns true.
+ virtual bool LoadFromSystem();
+
+ // Obtains the number of CPUs and places the value num.
+ virtual bool GetNumCpus(int *num);
+
+ // Looks for the CPU proc item with the given name for the given CPU number
+ // and places the string value in result.
+ virtual bool GetCpuStringValue(int cpu_id, const std::string& key,
+ std::string *result);
+
+ // Looks for the CPU proc item with the given name for the given CPU number
+ // and places the int value in result.
+ virtual bool GetCpuIntValue(int cpu_id, const std::string& key,
+ int *result);
+
+ private:
+ ConfigParser::MapVector cpu_info_;
+};
+
+// Builds a string containing the info from lsb_release on a single line.
+std::string ReadLinuxLsbRelease();
+
+// Returns the output of "uname".
+std::string ReadLinuxUname();
+
+} // namespace talk_base
+
+#endif // LINUX
+#endif // TALK_BASE_LINUX_H_
diff --git a/third_party/libjingle/source/talk/base/logging.cc b/third_party/libjingle/source/talk/base/logging.cc
new file mode 100644
index 0000000..d632a2c
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/logging.cc
@@ -0,0 +1,586 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define snprintf _snprintf
+#undef ERROR // wingdi.h
+#endif
+
+#ifdef OSX
+#include <CoreServices/CoreServices.h>
+#elif defined(ANDROID)
+#include <android/log.h>
+static const char kLibjingle[] = "libjingle";
+#endif // OSX || ANDROID
+
+#include <iostream>
+#include <iomanip>
+#include <vector>
+
+#include "talk/base/logging.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/time.h"
+
+namespace talk_base {
+
+/////////////////////////////////////////////////////////////////////////////
+// Constant Labels
+/////////////////////////////////////////////////////////////////////////////
+
+const char * FindLabel(int value, const ConstantLabel entries[]) {
+ for (int i = 0; entries[i].label; ++i) {
+ if (value == entries[i].value) {
+ return entries[i].label;
+ }
+ }
+ return 0;
+}
+
+std::string ErrorName(int err, const ConstantLabel * err_table) {
+ if (err == 0)
+ return "No error";
+
+ if (err_table != 0) {
+ if (const char * value = FindLabel(err, err_table))
+ return value;
+ }
+
+ char buffer[16];
+ snprintf(buffer, sizeof(buffer), "0x%08x", err);
+ return buffer;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LogMessage
+/////////////////////////////////////////////////////////////////////////////
+
+const int LogMessage::NO_LOGGING = LS_ERROR + 1;
+
+#if _DEBUG
+static const int LOG_DEFAULT = LS_INFO;
+#else // !_DEBUG
+static const int LOG_DEFAULT = LogMessage::NO_LOGGING;
+#endif // !_DEBUG
+
+// Global lock for log subsystem, only needed to serialize access to streams_.
+CriticalSection LogMessage::crit_;
+
+// By default, release builds don't log, debug builds at info level
+int LogMessage::min_sev_ = LOG_DEFAULT;
+int LogMessage::dbg_sev_ = LOG_DEFAULT;
+
+// Don't bother printing context for the ubiquitous INFO log messages
+int LogMessage::ctx_sev_ = LS_WARNING;
+
+// The list of logging streams currently configured.
+// Note: we explicitly do not clean this up, because of the uncertain ordering
+// of destructors at program exit. Let the person who sets the stream trigger
+// cleanup by setting to NULL, or let it leak (safe at program exit).
+LogMessage::StreamList LogMessage::streams_;
+
+// Boolean options default to false (0)
+bool LogMessage::thread_, LogMessage::timestamp_;
+
+// Program start time
+uint32 LogMessage::start_ = StartTime();
+
+// If we're in diagnostic mode, we'll be explicitly set that way; default=false.
+bool LogMessage::is_diagnostic_mode_ = false;
+
+LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev,
+ LogErrorContext err_ctx, int err, const char* module)
+ : severity_(sev) {
+ // Android's logging facility keeps track of timestamp and thread.
+#ifndef ANDROID
+ if (timestamp_) {
+ uint32 time = TimeSince(start_);
+ print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000)
+ << ":" << std::setw(3) << (time % 1000) << std::setfill(' ')
+ << "] ";
+ }
+
+ if (thread_) {
+#ifdef WIN32
+ DWORD id = GetCurrentThreadId();
+ print_stream_ << "[" << std::hex << id << std::dec << "] ";
+#endif // WIN32
+ }
+#endif // !ANDROID
+
+ if (severity_ >= ctx_sev_) {
+ print_stream_ << Describe(sev) << "(" << DescribeFile(file)
+ << ":" << line << "): ";
+ }
+
+ if (err_ctx != ERRCTX_NONE) {
+ std::ostringstream tmp;
+ tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]";
+ switch (err_ctx) {
+ case ERRCTX_ERRNO:
+ tmp << " " << strerror(err);
+ break;
+#if WIN32
+ case ERRCTX_HRESULT: {
+ char msgbuf[256];
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM;
+ HMODULE hmod = GetModuleHandleA(module);
+ if (hmod)
+ flags |= FORMAT_MESSAGE_FROM_HMODULE;
+ if (DWORD len = FormatMessageA(
+ flags, hmod, err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), NULL)) {
+ while ((len > 0) &&
+ isspace(static_cast<unsigned char>(msgbuf[len-1]))) {
+ msgbuf[--len] = 0;
+ }
+ tmp << " " << msgbuf;
+ }
+ break;
+ }
+#endif // WIN32
+#if OSX
+ case ERRCTX_OSSTATUS: {
+ tmp << " " << nonnull(GetMacOSStatusErrorString(err), "Unknown error");
+ if (const char* desc = GetMacOSStatusCommentString(err)) {
+ tmp << ": " << desc;
+ }
+ break;
+ }
+#endif // OSX
+ default:
+ break;
+ }
+ extra_ = tmp.str();
+ }
+}
+
+LogMessage::~LogMessage() {
+ if (!extra_.empty())
+ print_stream_ << " : " << extra_;
+ print_stream_ << std::endl;
+
+ const std::string& str = print_stream_.str();
+ if (severity_ >= dbg_sev_) {
+ OutputToDebug(str, severity_);
+ }
+
+ // Must lock streams_ before accessing
+ CritScope cs(&crit_);
+ for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
+ if (severity_ >= it->second) {
+ OutputToStream(it->first, str);
+ }
+ }
+}
+
+void LogMessage::LogContext(int min_sev) {
+ ctx_sev_ = min_sev;
+}
+
+void LogMessage::LogThreads(bool on) {
+ thread_ = on;
+}
+
+void LogMessage::LogTimestamps(bool on) {
+ timestamp_ = on;
+}
+
+void LogMessage::ResetTimestamps() {
+ start_ = Time();
+}
+
+void LogMessage::LogToDebug(int min_sev) {
+ dbg_sev_ = min_sev;
+ UpdateMinLogSeverity();
+}
+
+void LogMessage::LogToStream(StreamInterface* stream, int min_sev) {
+ CritScope cs(&crit_);
+ // Discard and delete all previously installed streams
+ for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
+ delete it->first;
+ }
+ streams_.clear();
+ // Install the new stream, if specified
+ if (stream) {
+ AddLogToStream(stream, min_sev);
+ }
+}
+
+int LogMessage::GetLogToStream(StreamInterface* stream) {
+ CritScope cs(&crit_);
+ int sev = NO_LOGGING;
+ for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
+ if (!stream || stream == it->first) {
+ sev = _min(sev, it->second);
+ }
+ }
+ return sev;
+}
+
+void LogMessage::AddLogToStream(StreamInterface* stream, int min_sev) {
+ CritScope cs(&crit_);
+ streams_.push_back(std::make_pair(stream, min_sev));
+ UpdateMinLogSeverity();
+}
+
+void LogMessage::RemoveLogToStream(StreamInterface* stream) {
+ CritScope cs(&crit_);
+ for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
+ if (stream == it->first) {
+ streams_.erase(it);
+ break;
+ }
+ }
+ UpdateMinLogSeverity();
+}
+
+void LogMessage::ConfigureLogging(const char* params, const char* filename) {
+ int current_level = LS_VERBOSE;
+ int debug_level = GetLogToDebug();
+ int file_level = GetLogToStream();
+
+ std::vector<std::string> tokens;
+ split(params, ' ', &tokens);
+
+ for (size_t i = 0; i < tokens.size(); ++i) {
+ if (tokens[i].empty())
+ continue;
+
+ // Logging features
+ if (tokens[i] == "tstamp") {
+ LogTimestamps();
+ } else if (tokens[i] == "thread") {
+ LogThreads();
+
+ // Logging levels
+ } else if (tokens[i] == "sensitive") {
+ current_level = LS_SENSITIVE;
+ } else if (tokens[i] == "verbose") {
+ current_level = LS_VERBOSE;
+ } else if (tokens[i] == "info") {
+ current_level = LS_INFO;
+ } else if (tokens[i] == "warning") {
+ current_level = LS_WARNING;
+ } else if (tokens[i] == "error") {
+ current_level = LS_ERROR;
+ } else if (tokens[i] == "none") {
+ current_level = NO_LOGGING;
+
+ // Logging targets
+ } else if (tokens[i] == "file") {
+ file_level = current_level;
+ } else if (tokens[i] == "debug") {
+ debug_level = current_level;
+ }
+ }
+
+#ifdef WIN32
+ if ((NO_LOGGING != debug_level) && !::IsDebuggerPresent()) {
+ // First, attempt to attach to our parent's console... so if you invoke
+ // from the command line, we'll see the output there. Otherwise, create
+ // our own console window.
+ // Note: These methods fail if a console already exists, which is fine.
+ bool success = false;
+ typedef BOOL (WINAPI* PFN_AttachConsole)(DWORD);
+ if (HINSTANCE kernel32 = ::LoadLibrary(L"kernel32.dll")) {
+ // AttachConsole is defined on WinXP+.
+ if (PFN_AttachConsole attach_console = reinterpret_cast<PFN_AttachConsole>
+ (::GetProcAddress(kernel32, "AttachConsole"))) {
+ success = (FALSE != attach_console(ATTACH_PARENT_PROCESS));
+ }
+ ::FreeLibrary(kernel32);
+ }
+ if (!success) {
+ ::AllocConsole();
+ }
+ }
+#endif // WIN32
+
+ scoped_ptr<FileStream> stream;
+ if (NO_LOGGING != file_level) {
+ stream.reset(new FileStream);
+ if (!stream->Open(filename, "wb") || !stream->DisableBuffering()) {
+ stream.reset();
+ }
+ }
+
+ LogToDebug(debug_level);
+ LogToStream(stream.release(), file_level);
+}
+
+int LogMessage::ParseLogSeverity(const std::string& value) {
+ int level = NO_LOGGING;
+ if (value == "LS_SENSITIVE") {
+ level = LS_SENSITIVE;
+ } else if (value == "LS_VERBOSE") {
+ level = LS_VERBOSE;
+ } else if (value == "LS_INFO") {
+ level = LS_INFO;
+ } else if (value == "LS_WARNING") {
+ level = LS_WARNING;
+ } else if (value == "LS_ERROR") {
+ level = LS_ERROR;
+ } else if (isdigit(value[0])) {
+ level = atoi(value.c_str()); // NOLINT
+ }
+ return level;
+}
+
+void LogMessage::UpdateMinLogSeverity() {
+ int min_sev = dbg_sev_;
+ for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
+ min_sev = _min(dbg_sev_, it->second);
+ }
+ min_sev_ = min_sev;
+}
+
+const char* LogMessage::Describe(LoggingSeverity sev) {
+ switch (sev) {
+ case LS_SENSITIVE: return "Sensitive";
+ case LS_VERBOSE: return "Verbose";
+ case LS_INFO: return "Info";
+ case LS_WARNING: return "Warning";
+ case LS_ERROR: return "Error";
+ default: return "<unknown>";
+ }
+}
+
+const char* LogMessage::DescribeFile(const char* file) {
+ const char* end1 = ::strrchr(file, '/');
+ const char* end2 = ::strrchr(file, '\\');
+ if (!end1 && !end2)
+ return file;
+ else
+ return (end1 > end2) ? end1 + 1 : end2 + 1;
+}
+
+void LogMessage::OutputToDebug(const std::string& str,
+ LoggingSeverity severity) {
+ bool log_to_stderr = true;
+#if defined(OSX) && (!defined(DEBUG) || defined(NDEBUG))
+ // On the Mac, all stderr output goes to the Console log and causes clutter.
+ // So in opt builds, don't log to stderr unless the user specifically sets
+ // a preference to do so.
+ CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
+ "logToStdErr",
+ kCFStringEncodingUTF8);
+ CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle());
+ if (key != NULL && domain != NULL) {
+ Boolean exists_and_is_valid;
+ Boolean should_log =
+ CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid);
+ // If the key doesn't exist or is invalid or is false, we will not log to
+ // stderr.
+ log_to_stderr = exists_and_is_valid && should_log;
+ }
+ if (key != NULL) {
+ CFRelease(key);
+ }
+#endif
+#ifdef WIN32
+ // Always log to the debugger.
+ // Perhaps stderr should be controlled by a preference, as on Mac?
+ OutputDebugStringA(str.c_str());
+ if (log_to_stderr) {
+ // This handles dynamically allocated consoles, too.
+ if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) {
+ log_to_stderr = false;
+ unsigned long written; // NOLINT
+ ::WriteFile(error_handle, str.data(), str.size(), &written, 0);
+ }
+ }
+#endif // WIN32
+#ifdef ANDROID
+ // Android's logging facility uses severity to log messages but we
+ // need to map libjingle's severity levels to Android ones first.
+ // Also write to stderr which maybe available to executable started
+ // from the shell.
+ int prio;
+ switch (severity) {
+ case LS_SENSITIVE:
+ __android_log_write(ANDROID_LOG_INFO, kLibjingle, "SENSITIVE");
+ return;
+ case LS_VERBOSE:
+ prio = ANDROID_LOG_VERBOSE;
+ break;
+ case LS_INFO:
+ prio = ANDROID_LOG_INFO;
+ break;
+ case LS_WARNING:
+ prio = ANDROID_LOG_WARN;
+ break;
+ case LS_ERROR:
+ prio = ANDROID_LOG_ERROR;
+ break;
+ default:
+ prio = ANDROID_LOG_UNKNOWN;
+ }
+ // Use the size of the string in the format (str may have \0 in the middle).
+ __android_log_print(prio, kLibjingle, "%.*s", str.size(), str.c_str());
+#endif // ANDROID
+ if (log_to_stderr) {
+ std::cerr << str;
+ std::cerr.flush();
+ }
+}
+
+void LogMessage::OutputToStream(StreamInterface* stream,
+ const std::string& str) {
+ // If write isn't fully successful, what are we going to do, log it? :)
+ stream->WriteAll(str.data(), str.size(), NULL, NULL);
+}
+
+//////////////////////////////////////////////////////////////////////
+// Logging Helpers
+//////////////////////////////////////////////////////////////////////
+
+void LogMultiline(LoggingSeverity level, const char* label, bool input,
+ const void* data, size_t len, bool hex_mode,
+ LogMultilineState* state) {
+ if (!LOG_CHECK_LEVEL_V(level))
+ return;
+
+ const char * direction = (input ? " << " : " >> ");
+
+ // NULL data means to flush our count of unprintable characters.
+ if (!data) {
+ if (state && state->unprintable_count_[input]) {
+ LOG_V(level) << label << direction << "## "
+ << state->unprintable_count_[input]
+ << " consecutive unprintable ##";
+ state->unprintable_count_[input] = 0;
+ }
+ return;
+ }
+
+ // The ctype classification functions want unsigned chars.
+ const unsigned char* udata = static_cast<const unsigned char*>(data);
+
+ if (hex_mode) {
+ const size_t LINE_SIZE = 24;
+ char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1];
+ while (len > 0) {
+ memset(asc_line, ' ', sizeof(asc_line));
+ memset(hex_line, ' ', sizeof(hex_line));
+ size_t line_len = _min(len, LINE_SIZE);
+ for (size_t i = 0; i < line_len; ++i) {
+ unsigned char ch = udata[i];
+ asc_line[i] = isprint(ch) ? ch : '.';
+ hex_line[i*2 + i/4] = hex_encode(ch >> 4);
+ hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf);
+ }
+ asc_line[sizeof(asc_line)-1] = 0;
+ hex_line[sizeof(hex_line)-1] = 0;
+ LOG_V(level) << label << direction
+ << asc_line << " " << hex_line << " ";
+ udata += line_len;
+ len -= line_len;
+ }
+ return;
+ }
+
+ size_t consecutive_unprintable = state ? state->unprintable_count_[input] : 0;
+
+ const unsigned char* end = udata + len;
+ while (udata < end) {
+ const unsigned char* line = udata;
+ const unsigned char* end_of_line = strchrn<unsigned char>(udata,
+ end - udata,
+ '\n');
+ if (!end_of_line) {
+ udata = end_of_line = end;
+ } else {
+ udata = end_of_line + 1;
+ }
+
+ bool is_printable = true;
+
+ // If we are in unprintable mode, we need to see a line of at least
+ // kMinPrintableLine characters before we'll switch back.
+ const ptrdiff_t kMinPrintableLine = 4;
+ if (consecutive_unprintable && ((end_of_line - line) < kMinPrintableLine)) {
+ is_printable = false;
+ } else {
+ // Determine if the line contains only whitespace and printable
+ // characters.
+ bool is_entirely_whitespace = true;
+ for (const unsigned char* pos = line; pos < end_of_line; ++pos) {
+ if (isspace(*pos))
+ continue;
+ is_entirely_whitespace = false;
+ if (!isprint(*pos)) {
+ is_printable = false;
+ break;
+ }
+ }
+ // Treat an empty line following unprintable data as unprintable.
+ if (consecutive_unprintable && is_entirely_whitespace) {
+ is_printable = false;
+ }
+ }
+ if (!is_printable) {
+ consecutive_unprintable += (udata - line);
+ continue;
+ }
+ // Print out the current line, but prefix with a count of prior unprintable
+ // characters.
+ if (consecutive_unprintable) {
+ LOG_V(level) << label << direction << "## " << consecutive_unprintable
+ << " consecutive unprintable ##";
+ consecutive_unprintable = 0;
+ }
+ // Strip off trailing whitespace.
+ while ((end_of_line > line) && isspace(*(end_of_line-1))) {
+ --end_of_line;
+ }
+ // Filter out any private data
+ std::string substr(reinterpret_cast<const char*>(line), end_of_line - line);
+ 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_V(level) << label << direction << substr;
+ } else {
+ LOG_V(level) << label << direction << "## omitted for privacy ##";
+ }
+ }
+
+ if (state) {
+ state->unprintable_count_[input] = consecutive_unprintable;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/logging.h b/third_party/libjingle/source/talk/base/logging.h
new file mode 100644
index 0000000..ccfd883
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/logging.h
@@ -0,0 +1,381 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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.
+
+// LOG(sev) logs the given stream at severity "sev", which must be a
+// compile-time constant of the LoggingSeverity type, without the namespace
+// prefix.
+// LOG_V(sev) Like LOG(), but sev is a run-time variable of the LoggingSeverity
+// type (basically, it just doesn't prepend the namespace).
+// LOG_F(sev) Like LOG(), but includes the name of the current function.
+// LOG_GLE(M)(sev [, mod]) attempt to add a string description of the
+// HRESULT returned by GetLastError. The "M" variant allows searching of a
+// DLL's string table for the error description.
+// LOG_ERRNO(sev) attempts to add a string description of an errno-derived
+// error. errno and associated facilities exist on both Windows and POSIX,
+// but on Windows they only apply to the C/C++ runtime.
+// LOG_ERR(sev) is an alias for the platform's normal error system, i.e. _GLE on
+// Windows and _ERRNO on POSIX.
+// (The above three also all have _EX versions that let you specify the error
+// code, rather than using the last one.)
+// LOG_E(sev, ctx, err, ...) logs a detailed error interpreted using the
+// specified context.
+// LOG_CHECK_LEVEL(sev) (and LOG_CHECK_LEVEL_V(sev)) can be used as a test
+// before performing expensive or sensitive operations whose sole purpose is
+// to output logging data at the desired level.
+// Lastly, PLOG(sev, err) is an alias for LOG_ERR_EX.
+
+#ifndef TALK_BASE_LOGGING_H_
+#define TALK_BASE_LOGGING_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h" // NOLINT
+#endif
+
+#include <list>
+#include <sstream>
+#include <string>
+#include <utility>
+#include "talk/base/basictypes.h"
+#include "talk/base/criticalsection.h"
+
+namespace talk_base {
+
+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.
+enum LogErrorContext {
+ ERRCTX_NONE,
+ ERRCTX_ERRNO, // System-local errno
+ ERRCTX_HRESULT, // Windows HRESULT
+ ERRCTX_OSSTATUS, // MacOS OSStatus
+
+ // Abbreviations for LOG_E macro
+ ERRCTX_EN = ERRCTX_ERRNO, // LOG_E(sev, EN, x)
+ ERRCTX_HR = ERRCTX_HRESULT, // LOG_E(sev, HR, x)
+ ERRCTX_OS = ERRCTX_OSSTATUS, // LOG_E(sev, OS, x)
+};
+
+class LogMessage {
+ public:
+ static const int NO_LOGGING;
+
+ 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_; }
+
+ // 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. Multiple streams may be specified by using AddLogToStream.
+ // LogToStream is retained for backwards compatibility; when invoked, it
+ // will discard any previously set streams and install the specified stream.
+ // GetLogToStream gets the severity for the specified stream, of if none
+ // is specified, the minimum stream severity.
+ // RemoveLogToStream removes the specified stream, without destroying it.
+ static void LogToStream(StreamInterface* stream, int min_sev);
+ static int GetLogToStream(StreamInterface* stream = NULL);
+ static void AddLogToStream(StreamInterface* stream, int min_sev);
+ static void RemoveLogToStream(StreamInterface* stream);
+
+ // Testing against MinLogSeverity allows code to avoid potentially expensive
+ // logging operations by pre-checking the logging level.
+ static int GetMinLogSeverity() { return min_sev_; }
+
+ static void SetDiagnosticMode(bool f) { is_diagnostic_mode_ = f; }
+ static bool IsDiagnosticMode() { return is_diagnostic_mode_; }
+
+ // Parses the provided parameter stream to configure the options above.
+ // Useful for configuring logging from the command line. If file logging
+ // is enabled, it is output to the specified filename.
+ static void ConfigureLogging(const char* params, const char* filename);
+
+ // Convert the string to a LS_ value; also accept numeric values.
+ static int ParseLogSeverity(const std::string& value);
+
+ private:
+ typedef std::list<std::pair<StreamInterface*, int> > StreamList;
+
+ // Updates min_sev_ appropriately when debug sinks change.
+ static void UpdateMinLogSeverity();
+
+ // These assist in formatting some parts of the debug output.
+ static const char* Describe(LoggingSeverity sev);
+ static const char* DescribeFile(const char* file);
+
+ // These write out the actual log messages.
+ static void OutputToDebug(const std::string& msg, LoggingSeverity severity_);
+ static void OutputToStream(StreamInterface* stream, const std::string& msg);
+
+ // 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_;
+
+ // Global lock for the logging subsystem
+ static CriticalSection crit_;
+
+ // dbg_sev_ is 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_, ctx_sev_;
+
+ // The output streams and their associated severities
+ static StreamList streams_;
+
+ // Flags for formatting options
+ static bool thread_, timestamp_;
+
+ // The timestamp at which logging started.
+ static uint32 start_;
+
+ // are we in diagnostic mode (as defined by the app)?
+ static bool is_diagnostic_mode_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LogMessage);
+};
+
+//////////////////////////////////////////////////////////////////////
+// Logging Helpers
+//////////////////////////////////////////////////////////////////////
+
+class LogMultilineState {
+ public:
+ size_t unprintable_count_[2];
+ LogMultilineState() {
+ unprintable_count_[0] = unprintable_count_[1] = 0;
+ }
+};
+
+// When possible, pass optional state variable to track various data across
+// multiple calls to LogMultiline. Otherwise, pass NULL.
+void LogMultiline(LoggingSeverity level, const char* label, bool input,
+ const void* data, size_t len, bool hex_mode,
+ LogMultilineState* state);
+
+//////////////////////////////////////////////////////////////////////
+// Macros which automatically disable logging when LOGGING == 0
+//////////////////////////////////////////////////////////////////////
+
+// 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)
+
+#ifndef LOG
+#if LOGGING
+
+// The following non-obvious technique for implementation of a
+// conditional log stream was stolen from google3/base/logging.h.
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros. This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+
+class LogMessageVoidify {
+ public:
+ LogMessageVoidify() { }
+ // This has to be an operator with a precedence lower than << but
+ // higher than ?:
+ void operator&(std::ostream&) { }
+};
+
+#define LOG_SEVERITY_PRECONDITION(sev) \
+ !(talk_base::LogMessage::Loggable(sev)) \
+ ? (void) 0 \
+ : talk_base::LogMessageVoidify() &
+
+#define LOG(sev) \
+ LOG_SEVERITY_PRECONDITION(talk_base::sev) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev).stream()
+
+// The _V version is for when a variable is passed in. It doesn't do the
+// namespace concatination.
+#define LOG_V(sev) \
+ LOG_SEVERITY_PRECONDITION(sev) \
+ talk_base::LogMessage(__FILE__, __LINE__, sev).stream()
+
+// The _F version prefixes the message with the current function name.
+#if defined(__GNUC__) && defined(_DEBUG)
+#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": "
+#else
+#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
+#endif
+
+#define LOG_CHECK_LEVEL(sev) \
+ talk_base::LogCheckLevel(talk_base::sev)
+#define LOG_CHECK_LEVEL_V(sev) \
+ talk_base::LogCheckLevel(sev)
+inline bool LogCheckLevel(LoggingSeverity sev) {
+ return (LogMessage::GetMinLogSeverity() <= sev);
+}
+
+#define LOG_E(sev, ctx, err, ...) \
+ LOG_SEVERITY_PRECONDITION(talk_base::sev) \
+ talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \
+ talk_base::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \
+ .stream()
+
+#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)talk_base:: LogMessage(NULL, 0, talk_base::sev).stream()
+#define LOG_V(sev) \
+ while (false) talk_base::LogMessage(NULL, 0, sev).stream()
+#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
+#define LOG_CHECK_LEVEL(sev) \
+ false
+#define LOG_CHECK_LEVEL_V(sev) \
+ false
+
+#define LOG_E(sev, ctx, err, ...) \
+ while (false) talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \
+ talk_base::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \
+ .stream()
+
+#endif // !LOGGING
+
+#define LOG_ERRNO_EX(sev, err) \
+ LOG_E(sev, ERRNO, err)
+#define LOG_ERRNO(sev) \
+ LOG_ERRNO_EX(sev, errno)
+
+#ifdef WIN32
+#define LOG_GLE_EX(sev, err) \
+ LOG_E(sev, HRESULT, err)
+#define LOG_GLE(sev) \
+ LOG_GLE_EX(sev, GetLastError())
+#define LOG_GLEM(sev, mod) \
+ LOG_E(sev, HRESULT, GetLastError(), mod)
+#define LOG_ERR_EX(sev, err) \
+ LOG_GLE_EX(sev, err)
+#define LOG_ERR(sev) \
+ LOG_GLE(sev)
+#define LAST_SYSTEM_ERROR \
+ (::GetLastError())
+#elif POSIX
+#define LOG_ERR_EX(sev, err) \
+ LOG_ERRNO_EX(sev, err)
+#define LOG_ERR(sev) \
+ LOG_ERRNO(sev)
+#define LAST_SYSTEM_ERROR \
+ (errno)
+#endif // WIN32
+
+#define PLOG(sev, err) \
+ LOG_ERR_EX(sev, err)
+
+// TODO(?): Add an "assert" wrapper that logs in the same manner.
+
+#endif // LOG
+
+} // namespace talk_base
+
+#endif // TALK_BASE_LOGGING_H_
diff --git a/third_party/libjingle/source/talk/base/macconversion.cc b/third_party/libjingle/source/talk/base/macconversion.cc
new file mode 100644
index 0000000..4654e53
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/macconversion.cc
@@ -0,0 +1,176 @@
+/*
+ * libjingle
+ * Copyright 2004--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 OSX
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "talk/base/logging.h"
+#include "talk/base/macconversion.h"
+
+bool p_convertHostCFStringRefToCPPString(
+ const CFStringRef cfstr, std::string& cppstr) {
+ bool result = false;
+
+ // First this must be non-null,
+ if (NULL != cfstr) {
+ // it must actually *be* a CFString, and not something just masquerading
+ // as one,
+ if (CFGetTypeID(cfstr) == CFStringGetTypeID()) {
+ // and we must be able to get the characters out of it.
+ // (The cfstr owns this buffer; it came from somewhere else,
+ // so someone else gets to take care of getting rid of the cfstr,
+ // and then this buffer will go away automatically.)
+ unsigned length = CFStringGetLength(cfstr);
+ char* buf = new char[1 + length];
+ if (CFStringGetCString(cfstr, buf, 1 + length, kCFStringEncodingASCII)) {
+ if (strlen(buf) == length) {
+ cppstr.assign(buf);
+ result = true;
+ }
+ }
+ delete [] buf;
+ }
+ }
+
+ return result;
+}
+
+bool p_convertCFNumberToInt(CFNumberRef cfn, int* i) {
+ bool converted = false;
+
+ // It must not be null.
+ if (NULL != cfn) {
+ // It must actually *be* a CFNumber and not something just masquerading
+ // as one.
+ if (CFGetTypeID(cfn) == CFNumberGetTypeID()) {
+ CFNumberType ntype = CFNumberGetType(cfn);
+ switch (ntype) {
+ case kCFNumberSInt8Type:
+ SInt8 sint8;
+ converted = CFNumberGetValue(cfn, ntype, static_cast<void*>(&sint8));
+ if (converted) *i = static_cast<int>(sint8);
+ break;
+ case kCFNumberSInt16Type:
+ SInt16 sint16;
+ converted = CFNumberGetValue(cfn, ntype, static_cast<void*>(&sint16));
+ if (converted) *i = static_cast<int>(sint16);
+ break;
+ case kCFNumberSInt32Type:
+ SInt32 sint32;
+ converted = CFNumberGetValue(cfn, ntype, static_cast<void*>(&sint32));
+ if (converted) *i = static_cast<int>(sint32);
+ break;
+ case kCFNumberSInt64Type:
+ SInt64 sint64;
+ converted = CFNumberGetValue(cfn, ntype, static_cast<void*>(&sint64));
+ if (converted) *i = static_cast<int>(sint64);
+ break;
+ case kCFNumberFloat32Type:
+ Float32 float32;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&float32));
+ if (converted) *i = static_cast<int>(float32);
+ break;
+ case kCFNumberFloat64Type:
+ Float64 float64;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&float64));
+ if (converted) *i = static_cast<int>(float64);
+ break;
+ case kCFNumberCharType:
+ char charvalue;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&charvalue));
+ if (converted) *i = static_cast<int>(charvalue);
+ break;
+ case kCFNumberShortType:
+ short shortvalue;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&shortvalue));
+ if (converted) *i = static_cast<int>(shortvalue);
+ break;
+ case kCFNumberIntType:
+ int intvalue;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&intvalue));
+ if (converted) *i = static_cast<int>(intvalue);
+ break;
+ case kCFNumberLongType:
+ long longvalue;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&longvalue));
+ if (converted) *i = static_cast<int>(longvalue);
+ break;
+ case kCFNumberLongLongType:
+ long long llvalue;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&llvalue));
+ if (converted) *i = static_cast<int>(llvalue);
+ break;
+ case kCFNumberFloatType:
+ float floatvalue;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&floatvalue));
+ if (converted) *i = static_cast<int>(floatvalue);
+ break;
+ case kCFNumberDoubleType:
+ double doublevalue;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&doublevalue));
+ if (converted) *i = static_cast<int>(doublevalue);
+ break;
+ case kCFNumberCFIndexType:
+ CFIndex cfindex;
+ converted = CFNumberGetValue(cfn, ntype,
+ static_cast<void*>(&cfindex));
+ if (converted) *i = static_cast<int>(cfindex);
+ break;
+ default:
+ LOG(LS_ERROR) << "got unknown type.";
+ break;
+ }
+ }
+ }
+
+ return converted;
+}
+
+bool p_isCFNumberTrue(CFNumberRef cfn) {
+ // We assume it's false until proven otherwise.
+ bool result = false;
+ int asInt;
+ bool converted = p_convertCFNumberToInt(cfn, &asInt);
+
+ if (converted && (0 != asInt)) {
+ result = true;
+ }
+
+ return result;
+}
+
+#endif // OSX
diff --git a/third_party/libjingle/source/talk/base/macconversion.h b/third_party/libjingle/source/talk/base/macconversion.h
new file mode 100644
index 0000000..a401cab
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/macconversion.h
@@ -0,0 +1,56 @@
+/*
+ * libjingle
+ * Copyright 2004--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_MACCONVERSION_H_
+#define TALK_BASE_MACCONVERSION_H_
+
+#ifdef OSX
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <string>
+
+// given a CFStringRef, attempt to convert it to a C++ string.
+// returns true if it succeeds, false otherwise.
+// We can safely assume, given our context, that the string is
+// going to be in ASCII, because it will either be an IP address,
+// or a domain name, which is guaranteed to be ASCII-representable.
+bool p_convertHostCFStringRefToCPPString(const CFStringRef cfstr,
+ std::string& cppstr);
+
+// Convert the CFNumber to an integer, putting the integer in the location
+// given, and returhing true, if the conversion succeeds.
+// If given a NULL or a non-CFNumber, returns false.
+// This is pretty aggresive about trying to convert to int.
+bool p_convertCFNumberToInt(CFNumberRef cfn, int* i);
+
+// given a CFNumberRef, determine if it represents a true value.
+bool p_isCFNumberTrue(CFNumberRef cfn);
+
+#endif // OSX
+
+#endif // TALK_BASE_MACCONVERSION_H_
diff --git a/third_party/libjingle/source/talk/base/macutils.cc b/third_party/libjingle/source/talk/base/macutils.cc
new file mode 100644
index 0000000..69a2f5d
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/macutils.cc
@@ -0,0 +1,156 @@
+/*
+ * libjingle
+ * Copyright 2007--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <sstream>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/macutils.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/stringutils.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool ToUtf8(const CFStringRef str16, std::string* str8) {
+ if ((NULL == str16) || (NULL == str8))
+ return false;
+ size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16),
+ kCFStringEncodingUTF8)
+ + 1;
+ scoped_array<char> buffer(new char[maxlen]);
+ if (!buffer.get()
+ || !CFStringGetCString(str16, buffer.get(), maxlen,
+ kCFStringEncodingUTF8))
+ return false;
+ str8->assign(buffer.get());
+ return true;
+}
+
+bool ToUtf16(const std::string& str8, CFStringRef* str16) {
+ if (NULL == str16)
+ return false;
+ *str16 = CFStringCreateWithBytes(kCFAllocatorDefault,
+ reinterpret_cast<const UInt8*>(str8.data()),
+ str8.length(), kCFStringEncodingUTF8,
+ false);
+ return (NULL != *str16);
+}
+
+void DecodeFourChar(UInt32 fc, std::string* out) {
+ std::stringstream ss;
+ ss << '\'';
+ bool printable = true;
+ for (int i = 3; i >= 0; --i) {
+ char ch = (fc >> (8 * i)) & 0xFF;
+ if (isprint(static_cast<unsigned char>(ch))) {
+ ss << ch;
+ } else {
+ printable = false;
+ break;
+ }
+ }
+ if (printable) {
+ ss << '\'';
+ } else {
+ ss.str("");
+ ss << "0x" << std::hex << fc;
+ }
+ out->append(ss.str());
+}
+
+std::string DecodeEvent(EventRef event) {
+ std::string str;
+ DecodeFourChar(::GetEventClass(event), &str);
+ str.push_back(':');
+ DecodeFourChar(::GetEventKind(event), &str);
+ return str;
+}
+
+static bool GetGestalt(OSType ostype, int* value) {
+ ASSERT(NULL != value);
+ SInt32 native_value;
+ OSStatus result = Gestalt(ostype, &native_value);
+ if (noErr == result) {
+ *value = native_value;
+ return true;
+ }
+ std::string str;
+ DecodeFourChar(ostype, &str);
+ LOG_E(LS_ERROR, OS, result) << "Gestalt(" << str << ")";
+ return false;
+}
+
+bool GetOSVersion(int* major, int* minor, int* bugfix) {
+ ASSERT(major && minor && bugfix);
+ if (!GetGestalt(gestaltSystemVersion, major))
+ return false;
+ if (*major < 0x1040) {
+ *bugfix = *major & 0xF;
+ *minor = (*major >> 4) & 0xF;
+ *major = (*major >> 8);
+ return true;
+ }
+ return GetGestalt(gestaltSystemVersionMajor, major)
+ && GetGestalt(gestaltSystemVersionMinor, minor)
+ && GetGestalt(gestaltSystemVersionBugFix, bugfix);
+}
+
+MacOSVersionName GetOSVersionName() {
+ int major = 0, minor = 0, bugfix = 0;
+ if (!GetOSVersion(&major, &minor, &bugfix))
+ return kMacOSUnknown;
+ if (major > 10)
+ return kMacOSNewer;
+ if ((major < 10) || (minor < 3))
+ return kMacOSOlder;
+ switch (minor) {
+ case 3:
+ return kMacOSPanther;
+ case 4:
+ return kMacOSTiger;
+ case 5:
+ return kMacOSLeopard;
+ }
+ return kMacOSNewer;
+}
+
+bool GetQuickTimeVersion(std::string* out) {
+ int ver;
+ if (!GetGestalt(gestaltQuickTimeVersion, &ver))
+ return false;
+
+ std::stringstream ss;
+ ss << std::hex << ver;
+ *out = ss.str();
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/macutils.h b/third_party/libjingle/source/talk/base/macutils.h
new file mode 100644
index 0000000..1cd64ab
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/macutils.h
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2007--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_MACUTILS_H__
+#define TALK_BASE_MACUTILS_H__
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Carbon/Carbon.h>
+#include <string>
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool ToUtf8(const CFStringRef str16, std::string* str8);
+bool ToUtf16(const std::string& str8, CFStringRef* str16);
+
+void DecodeFourChar(UInt32 fc, std::string* out);
+std::string DecodeEvent(EventRef event);
+
+enum MacOSVersionName {
+ kMacOSUnknown, // ???
+ kMacOSOlder, // 10.2-
+ kMacOSPanther, // 10.3
+ kMacOSTiger, // 10.4
+ kMacOSLeopard, // 10.5
+ kMacOSNewer, // 10.6+
+};
+
+bool GetOSVersion(int* major, int* minor, int* bugfix);
+MacOSVersionName GetOSVersionName();
+bool GetQuickTimeVersion(std::string* version);
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_MACUTILS_H__
diff --git a/third_party/libjingle/source/talk/base/md5.h b/third_party/libjingle/source/talk/base/md5.h
new file mode 100644
index 0000000..ec458d1
--- /dev/null
+++ b/third_party/libjingle/source/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 TALK_BASE_MD5_H__
+#define TALK_BASE_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 // TALK_BASE_MD5_H__
diff --git a/third_party/libjingle/source/talk/base/md5c.c b/third_party/libjingle/source/talk/base/md5c.c
new file mode 100644
index 0000000..eb2c034
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/base/messagehandler.cc b/third_party/libjingle/source/talk/base/messagehandler.cc
new file mode 100644
index 0000000..5b3585b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/messagehandler.cc
@@ -0,0 +1,37 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/messagehandler.h"
+#include "talk/base/messagequeue.h"
+
+namespace talk_base {
+
+MessageHandler::~MessageHandler() {
+ MessageQueueManager::Instance()->Clear(this);
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/messagehandler.h b/third_party/libjingle/source/talk/base/messagehandler.h
new file mode 100644
index 0000000..eb7db9b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/messagehandler.h
@@ -0,0 +1,46 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_MESSAGEHANDLER_H__
+#define TALK_BASE_MESSAGEHANDLER_H__
+
+namespace talk_base {
+
+struct Message;
+
+// Messages get dispatched to a MessageHandler
+
+class MessageHandler {
+public:
+ virtual ~MessageHandler();
+
+ virtual void OnMessage(Message* msg) = 0;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_MESSAGEHANDLER_H__
diff --git a/third_party/libjingle/source/talk/base/messagequeue.cc b/third_party/libjingle/source/talk/base/messagequeue.cc
new file mode 100644
index 0000000..fc8cd42
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/messagequeue.cc
@@ -0,0 +1,370 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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
+
+#ifdef POSIX
+#include <sys/time.h>
+#endif
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/physicalsocketserver.h"
+
+
+namespace talk_base {
+
+const uint32 kMaxMsgLatency = 150; // 150 ms
+
+//------------------------------------------------------------------
+// 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) {
+ // MessageQueueManager methods should be non-reentrant, so we
+ // ASSERT that is the case. If any of these ASSERT, please
+ // contact bpm or jbeda.
+ ASSERT(!crit_.CurrentThreadIsOwner());
+ CritScope cs(&crit_);
+ message_queues_.push_back(message_queue);
+}
+
+void MessageQueueManager::Remove(MessageQueue *message_queue) {
+ ASSERT(!crit_.CurrentThreadIsOwner()); // See note above.
+ 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) {
+ ASSERT(!crit_.CurrentThreadIsOwner()); // See note above.
+ 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), fStop_(false), fPeekKeep_(false), active_(false),
+ dmsgq_next_num_(0) {
+ if (!ss_) {
+ // Currently, MessageQueue holds a socket server, and is the base class for
+ // Thread. It seems like it makes more sense for Thread to hold the socket
+ // server, and provide it to the MessageQueue, since the Thread controls
+ // the I/O model, and MQ is agnostic to those details. Anyway, this causes
+ // messagequeue_unittest to depend on network libraries... yuck.
+ default_ss_.reset(new PhysicalSocketServer());
+ ss_ = default_ss_.get();
+ }
+ ss_->SetMessageQueue(this);
+}
+
+MessageQueue::~MessageQueue() {
+ // The signal is done from here to ensure
+ // that it always gets called when the queue
+ // is going away.
+ SignalQueueDestroyed();
+ if (active_) {
+ MessageQueueManager::Instance()->Remove(this);
+ Clear(NULL);
+ }
+ if (ss_) {
+ ss_->SetMessageQueue(NULL);
+ }
+}
+
+void MessageQueue::set_socketserver(SocketServer* ss) {
+ ss_ = ss ? ss : default_ss_.get();
+ ss_->SetMessageQueue(this);
+}
+
+void MessageQueue::Quit() {
+ fStop_ = true;
+ ss_->WakeUp();
+}
+
+bool MessageQueue::IsQuitting() {
+ return fStop_;
+}
+
+void MessageQueue::Restart() {
+ fStop_ = false;
+}
+
+bool MessageQueue::Peek(Message *pmsg, int cmsWait) {
+ 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, bool process_io) {
+ // 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 = Time();
+ uint32 msCurrent = msStart;
+ while (true) {
+ // Check for sent messages
+
+ ReceiveSends();
+
+ // Check queues
+
+ int cmsDelayNext = kForever;
+ {
+ CritScope cs(&crit_);
+
+ // Check for delayed messages that have been triggered
+ // Calc the next trigger too
+
+ while (!dmsgq_.empty()) {
+ if (TimeIsLater(msCurrent, dmsgq_.top().msTrigger_)) {
+ cmsDelayNext = TimeDiff(dmsgq_.top().msTrigger_, msCurrent);
+ break;
+ }
+ msgq_.push_back(dmsgq_.top().msg_);
+ dmsgq_.pop();
+ }
+
+ // Check for posted events
+
+ while (!msgq_.empty()) {
+ *pmsg = msgq_.front();
+ if (pmsg->ts_sensitive) {
+ long delay = TimeDiff(msCurrent, pmsg->ts_sensitive);
+ if (delay > 0) {
+ LOG_F(LS_WARNING) << "id: " << pmsg->message_id << " delay: "
+ << (delay + kMaxMsgLatency) << "ms";
+ }
+ }
+ msgq_.pop_front();
+ if (MQID_DISPOSE == pmsg->message_id) {
+ ASSERT(NULL == pmsg->phandler);
+ delete pmsg->pdata;
+ continue;
+ }
+ return true;
+ }
+ }
+
+ if (fStop_)
+ break;
+
+ // Which is shorter, the delay wait or the asked wait?
+
+ int cmsNext;
+ if (cmsWait == kForever) {
+ cmsNext = cmsDelayNext;
+ } else {
+ cmsNext = _max(0, cmsTotal - cmsElapsed);
+ if ((cmsDelayNext != kForever) && (cmsDelayNext < cmsNext))
+ cmsNext = cmsDelayNext;
+ }
+
+ // Wait and multiplex in the meantime
+ if (!ss_->Wait(cmsNext, process_io))
+ return false;
+
+ // If the specified timeout expired, return
+
+ msCurrent = Time();
+ cmsElapsed = TimeDiff(msCurrent, msStart);
+ if (cmsWait != kForever) {
+ if (cmsElapsed >= cmsWait)
+ return false;
+ }
+ }
+ return false;
+}
+
+void MessageQueue::ReceiveSends() {
+}
+
+void MessageQueue::Post(MessageHandler *phandler, uint32 id,
+ MessageData *pdata, bool time_sensitive) {
+ if (fStop_)
+ return;
+
+ // Keep thread safe
+ // Add the message to the end of the queue
+ // Signal for the multiplexer to return
+
+ CritScope cs(&crit_);
+ EnsureActive();
+ Message msg;
+ msg.phandler = phandler;
+ msg.message_id = id;
+ msg.pdata = pdata;
+ if (time_sensitive) {
+ msg.ts_sensitive = Time() + kMaxMsgLatency;
+ }
+ msgq_.push_back(msg);
+ ss_->WakeUp();
+}
+
+void MessageQueue::DoDelayPost(int cmsDelay, uint32 tstamp,
+ MessageHandler *phandler, uint32 id, MessageData* pdata) {
+ if (fStop_)
+ return;
+
+ // Keep thread safe
+ // Add to the priority queue. Gets sorted soonest first.
+ // Signal for the multiplexer to return.
+
+ CritScope cs(&crit_);
+ EnsureActive();
+ Message msg;
+ msg.phandler = phandler;
+ msg.message_id = id;
+ msg.pdata = pdata;
+ DelayedMessage dmsg(cmsDelay, tstamp, dmsgq_next_num_, msg);
+ dmsgq_.push(dmsg);
+ // If this message queue processes 1 message every millisecond for 50 days,
+ // we will wrap this number. Even then, only messages with identical times
+ // will be misordered, and then only briefly. This is probably ok.
+ VERIFY(0 != ++dmsgq_next_num_);
+ ss_->WakeUp();
+}
+
+int MessageQueue::GetDelay() {
+ CritScope cs(&crit_);
+
+ if (!msgq_.empty())
+ return 0;
+
+ if (!dmsgq_.empty()) {
+ int delay = TimeUntil(dmsgq_.top().msTrigger_);
+ if (delay < 0)
+ delay = 0;
+ return delay;
+ }
+
+ return kForever;
+}
+
+void MessageQueue::Clear(MessageHandler *phandler, uint32 id,
+ MessageList* removed) {
+ CritScope cs(&crit_);
+
+ // Remove messages with phandler
+
+ if (fPeekKeep_ && msgPeek_.Match(phandler, id)) {
+ if (removed) {
+ removed->push_back(msgPeek_);
+ } else {
+ delete msgPeek_.pdata;
+ }
+ fPeekKeep_ = false;
+ }
+
+ // Remove from ordered message queue
+
+ for (MessageList::iterator it = msgq_.begin(); it != msgq_.end();) {
+ if (it->Match(phandler, id)) {
+ if (removed) {
+ removed->push_back(*it);
+ } else {
+ delete it->pdata;
+ }
+ it = msgq_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Remove from priority queue. Not directly iterable, so use this approach
+
+ PriorityQueue::container_type::iterator new_end = dmsgq_.container().begin();
+ for (PriorityQueue::container_type::iterator it = new_end;
+ it != dmsgq_.container().end(); ++it) {
+ if (it->msg_.Match(phandler, id)) {
+ if (removed) {
+ removed->push_back(it->msg_);
+ } else {
+ delete it->msg_.pdata;
+ }
+ } else {
+ *new_end++ = *it;
+ }
+ }
+ dmsgq_.container().erase(new_end, dmsgq_.container().end());
+ dmsgq_.reheap();
+}
+
+void MessageQueue::Dispatch(Message *pmsg) {
+ pmsg->phandler->OnMessage(pmsg);
+}
+
+void MessageQueue::EnsureActive() {
+ ASSERT(crit_.CurrentThreadIsOwner());
+ if (!active_) {
+ active_ = true;
+ MessageQueueManager::Instance()->Add(this);
+ }
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/messagequeue.h b/third_party/libjingle/source/talk/base/messagequeue.h
new file mode 100644
index 0000000..4d470df
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/messagequeue.h
@@ -0,0 +1,245 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_MESSAGEQUEUE_H_
+#define TALK_BASE_MESSAGEQUEUE_H_
+
+#include <algorithm>
+#include <cstring>
+#include <list>
+#include <queue>
+#include <vector>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/criticalsection.h"
+#include "talk/base/messagehandler.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/time.h"
+
+namespace talk_base {
+
+struct Message;
+class MessageQueue;
+
+// 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_;
+ // This list contains 'active' MessageQueues.
+ std::vector<MessageQueue *> message_queues_;
+ CriticalSection crit_;
+};
+
+// Derive from this for specialized data
+// App manages lifetime, except when messages are purged
+
+class MessageData {
+ public:
+ MessageData() {}
+ virtual ~MessageData() {}
+};
+
+template <class T>
+class TypedMessageData : public MessageData {
+ public:
+ explicit TypedMessageData(const T& data) : data_(data) { }
+ const T& data() const { return data_; }
+ T& data() { return data_; }
+ private:
+ T data_;
+};
+
+// Like TypedMessageData, but for pointers that require a delete.
+template <class T>
+class ScopedMessageData : public MessageData {
+ public:
+ explicit ScopedMessageData(T* data) : data_(data) { }
+ const scoped_ptr<T>& data() const { return data_; }
+ scoped_ptr<T>& data() { return data_; }
+ private:
+ scoped_ptr<T> data_;
+};
+
+template<class T>
+inline MessageData* WrapMessageData(const T& data) {
+ return new TypedMessageData<T>(data);
+}
+
+template<class T>
+inline const T& UseMessageData(MessageData* data) {
+ return static_cast< TypedMessageData<T>* >(data)->data();
+}
+
+template<class T>
+class DisposeData : public MessageData {
+ public:
+ explicit DisposeData(T* data) : data_(data) { }
+ virtual ~DisposeData() { delete data_; }
+ private:
+ T* data_;
+};
+
+const uint32 MQID_ANY = static_cast<uint32>(-1);
+const uint32 MQID_DISPOSE = static_cast<uint32>(-2);
+
+// No destructor
+
+struct Message {
+ Message() {
+ memset(this, 0, sizeof(*this));
+ }
+ inline bool Match(MessageHandler* handler, uint32 id) const {
+ return (handler == NULL || handler == phandler)
+ && (id == MQID_ANY || id == message_id);
+ }
+ MessageHandler *phandler;
+ uint32 message_id;
+ MessageData *pdata;
+ uint32 ts_sensitive;
+};
+
+typedef std::list<Message> MessageList;
+
+// DelayedMessage goes into a priority queue, sorted by trigger time. Messages
+// with the same trigger time are processed in num_ (FIFO) order.
+
+class DelayedMessage {
+ public:
+ DelayedMessage(int delay, uint32 trigger, uint32 num, const Message& msg)
+ : cmsDelay_(delay), msTrigger_(trigger), num_(num), msg_(msg) { }
+
+ bool operator< (const DelayedMessage& dmsg) const {
+ return (dmsg.msTrigger_ < msTrigger_)
+ || ((dmsg.msTrigger_ == msTrigger_) && (dmsg.num_ < num_));
+ }
+
+ int cmsDelay_; // for debugging
+ uint32 msTrigger_;
+ uint32 num_;
+ Message msg_;
+};
+
+class MessageQueue {
+ public:
+ explicit MessageQueue(SocketServer* ss = NULL);
+ virtual ~MessageQueue();
+
+ SocketServer* socketserver() { return ss_; }
+ void set_socketserver(SocketServer* ss);
+
+ // Note: The behavior of MessageQueue has changed. When a MQ is stopped,
+ // futher Posts and Sends will fail. However, any pending Sends and *ready*
+ // Posts (as opposed to unexpired delayed Posts) will be delivered before
+ // Get (or Peek) returns false. By guaranteeing delivery of those messages,
+ // we eliminate the race condition when an MessageHandler and MessageQueue
+ // may be destroyed independently of each other.
+ virtual void Quit();
+ virtual bool IsQuitting();
+ virtual void Restart();
+
+ // Get() will process I/O until:
+ // 1) A message is available (returns true)
+ // 2) cmsWait seconds have elapsed (returns false)
+ // 3) Stop() is called (returns false)
+ virtual bool Get(Message *pmsg, int cmsWait = kForever,
+ bool process_io = true);
+ virtual bool Peek(Message *pmsg, int cmsWait = 0);
+ virtual void Post(MessageHandler *phandler, uint32 id = 0,
+ MessageData *pdata = NULL, bool time_sensitive = false);
+ virtual void PostDelayed(int cmsDelay, MessageHandler *phandler,
+ uint32 id = 0, MessageData *pdata = NULL) {
+ return DoDelayPost(cmsDelay, TimeAfter(cmsDelay), phandler, id, pdata);
+ }
+ virtual void PostAt(uint32 tstamp, MessageHandler *phandler,
+ uint32 id = 0, MessageData *pdata = NULL) {
+ return DoDelayPost(TimeUntil(tstamp), tstamp, phandler, id, pdata);
+ }
+ virtual void Clear(MessageHandler *phandler, uint32 id = MQID_ANY,
+ MessageList* removed = NULL);
+ virtual void Dispatch(Message *pmsg);
+ virtual void ReceiveSends();
+
+ // Amount of time until the next message can be retrieved
+ virtual int GetDelay();
+
+ bool empty() const { return msgq_.empty() && dmsgq_.empty() && !fPeekKeep_; }
+ size_t size() const { return msgq_.size() + dmsgq_.size() + fPeekKeep_; }
+
+ // Internally posts a message which causes the doomed object to be deleted
+ template<class T> void Dispose(T* doomed) {
+ if (doomed) {
+ Post(NULL, MQID_DISPOSE, new DisposeData<T>(doomed));
+ }
+ }
+
+ // When this signal is sent out, any references to this queue should
+ // no longer be used.
+ sigslot::signal0<> SignalQueueDestroyed;
+
+ protected:
+ class PriorityQueue : public std::priority_queue<DelayedMessage> {
+ public:
+ container_type& container() { return c; }
+ void reheap() { make_heap(c.begin(), c.end(), comp); }
+ };
+
+ void EnsureActive();
+ void DoDelayPost(int cmsDelay, uint32 tstamp, MessageHandler *phandler,
+ uint32 id, MessageData* pdata);
+
+ // The SocketServer is not owned by MessageQueue.
+ SocketServer* ss_;
+ // If a server isn't supplied in the constructor, use this one.
+ scoped_ptr<SocketServer> default_ss_;
+ bool fStop_;
+ bool fPeekKeep_;
+ Message msgPeek_;
+ // A message queue is active if it has ever had a message posted to it.
+ // This also corresponds to being in MessageQueueManager's global list.
+ bool active_;
+ MessageList msgq_;
+ PriorityQueue dmsgq_;
+ uint32 dmsgq_next_num_;
+ CriticalSection crit_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_MESSAGEQUEUE_H_
diff --git a/third_party/libjingle/source/talk/base/nethelpers.cc b/third_party/libjingle/source/talk/base/nethelpers.cc
new file mode 100644
index 0000000..24c142c
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/nethelpers.cc
@@ -0,0 +1,138 @@
+/*
+ * libjingle
+ * Copyright 2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/nethelpers.h"
+
+#include "talk/base/byteorder.h"
+#include "talk/base/signalthread.h"
+
+namespace talk_base {
+
+// AsyncResolver
+
+AsyncResolver::AsyncResolver() : result_(NULL), error_(0) {
+}
+
+AsyncResolver::~AsyncResolver() {
+ FreeHostEnt(result_);
+}
+
+void AsyncResolver::DoWork() {
+ result_ = SafeGetHostByName(addr_.hostname().c_str(), &error_);
+}
+
+void AsyncResolver::OnWorkDone() {
+ if (result_) {
+ addr_.SetIP(NetworkToHost32(
+ *reinterpret_cast<uint32*>(result_->h_addr_list[0])));
+ }
+}
+
+// The functions below are used to do gethostbyname, but with an allocated
+// instead of a static buffer.
+hostent* SafeGetHostByName(const char* hostname, int* herrno) {
+ hostent* result = NULL;
+#if defined(WIN32) || (defined(POSIX) && !defined(OSX))
+ // On Windows we have to allocate a buffer, and manually copy the hostent,
+ // along with its embedded pointers.
+ hostent* ent = gethostbyname(hostname);
+ if (!ent) {
+#ifdef WIN32
+ *herrno = WSAGetLastError();
+#else // POSIX
+ *herrno = h_errno;
+#endif
+ return NULL;
+ }
+
+ // Get the total number of bytes we need to copy, and allocate our buffer.
+ int num_aliases = 0, num_addrs = 0;
+ int total_len = sizeof(hostent);
+ total_len += strlen(ent->h_name) + 1;
+ while (ent->h_aliases[num_aliases]) {
+ total_len += sizeof(char*) + strlen(ent->h_aliases[num_aliases]) + 1;
+ ++num_aliases;
+ }
+ total_len += sizeof(char*);
+ while (ent->h_addr_list[num_addrs]) {
+ total_len += sizeof(char*) + ent->h_length;
+ ++num_addrs;
+ }
+ total_len += sizeof(char*);
+
+ result = static_cast<hostent*>(malloc(total_len));
+ char* p = reinterpret_cast<char*>(result) + sizeof(hostent);
+
+ // Copy the hostent into it, along with its embedded pointers.
+ result->h_name = p;
+ memcpy(p, ent->h_name, strlen(ent->h_name) + 1);
+ p += strlen(ent->h_name) + 1;
+
+ result->h_aliases = reinterpret_cast<char**>(p);
+ p += (num_aliases + 1) * sizeof(char*);
+ for (int i = 0; i < num_aliases; ++i) {
+ result->h_aliases[i] = p;
+ memcpy(p, result->h_aliases[i], strlen(ent->h_aliases[i]) + 1);
+ p += strlen(ent->h_aliases[i]) + 1;
+ }
+ result->h_aliases[num_aliases] = NULL;
+
+ result->h_addrtype = ent->h_addrtype;
+ result->h_length = ent->h_length;
+
+ result->h_addr_list = reinterpret_cast<char**>(p);
+ p += (num_addrs + 1) * sizeof(char*);
+ for (int i = 0; i < num_addrs; ++i) {
+ result->h_addr_list[i] = p;
+ memcpy(p, ent->h_addr_list[i], ent->h_length);
+ p += ent->h_length;
+ }
+ result->h_addr_list[num_addrs] = NULL;
+
+ *herrno = 0;
+#elif defined(OSX)
+ // Mac OS returns an object with everything allocated.
+ result = getipnodebyname(hostname, AF_INET, AI_DEFAULT, herrno);
+#else
+#error "I don't know how to do gethostbyname safely on your system."
+#endif
+ return result;
+}
+
+// This function should mirror the above function, and free any resources
+// allocated by the above.
+void FreeHostEnt(hostent* host) {
+#if defined(WIN32) || (defined(POSIX) && !defined(OSX))
+ free(host);
+#elif defined(OSX)
+ freehostent(host);
+#else
+#error "I don't know how to free a hostent on your system."
+#endif
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/nethelpers.h b/third_party/libjingle/source/talk/base/nethelpers.h
new file mode 100644
index 0000000..4cba9a9
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/nethelpers.h
@@ -0,0 +1,76 @@
+/*
+ * libjingle
+ * Copyright 2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_NETHELPERS_H_
+#define TALK_BASE_NETHELPERS_H_
+
+#ifdef POSIX
+#include <netdb.h>
+#include <cstddef>
+#elif WIN32
+#include <winsock2.h> // NOLINT
+#endif
+
+#include <list>
+
+#include "talk/base/signalthread.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socketaddress.h"
+
+namespace talk_base {
+
+// AsyncResolver will perform async DNS resolution, signaling the result on
+// the inherited SignalWorkDone when the operation completes.
+class AsyncResolver : public SignalThread {
+ public:
+ AsyncResolver();
+
+ const SocketAddress& address() const { return addr_; }
+ void set_address(const SocketAddress& addr) { addr_ = addr; }
+ int error() const { return error_; }
+ void set_error(int error) { error_ = error; }
+
+ protected:
+ ~AsyncResolver();
+ virtual void DoWork();
+ virtual void OnWorkDone();
+
+ private:
+ SocketAddress addr_;
+ hostent* result_;
+ int error_;
+};
+
+// SafeGetHostByName functions allocate and return their result, instead of
+// using a static variable like the normal gethostbyname.
+// FreeHostEnt frees the memory allocated by SafeGetHostByName.
+hostent* SafeGetHostByName(const char* hostname, int* herrno);
+void FreeHostEnt(hostent* host);
+
+} // namespace talk_base
+
+#endif // TALK_BASE_NETHELPERS_H_
diff --git a/third_party/libjingle/source/talk/base/network.cc b/third_party/libjingle/source/talk/base/network.cc
new file mode 100644
index 0000000..4091081
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/network.cc
@@ -0,0 +1,456 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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_CONFIG_H
+#include "config.h"
+#endif
+
+#include "talk/base/network.h"
+#include "talk/base/stream.h"
+
+#ifdef POSIX
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+#endif // POSIX
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#include <Iphlpapi.h>
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <cfloat>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <sstream>
+
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/socket.h" // includes something that makes windows happy
+#include "talk/base/stringencode.h"
+#include "talk/base/time.h"
+
+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 = talk_base::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";
+}
+
+} // namespace
+
+namespace talk_base {
+
+NetworkManager::~NetworkManager() {
+ for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i)
+ delete i->second;
+}
+
+bool NetworkManager::GetNetworks(std::vector<Network*>* result) {
+ std::vector<Network*> list;
+ if (!EnumNetworks(false, &list)) {
+ return false;
+ }
+
+ 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());
+ network->set_gateway_ip(list[i]->gateway_ip());
+ delete list[i];
+ }
+
+ networks_[network->name()] = network;
+ result->push_back(network);
+ }
+ return true;
+}
+
+void NetworkManager::DumpNetworks(bool include_ignored) {
+ std::vector<Network*> list;
+ EnumNetworks(include_ignored, &list);
+ LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:";
+ for (size_t i = 0; i < list.size(); ++i) {
+ const Network* network = list[i];
+ if (!network->ignored() || include_ignored) {
+ LOG(LS_INFO) << network->ToString() << ": " << network->description()
+ << ", Gateway="
+ << SocketAddress::IPToString(network->gateway_ip())
+ << ((network->ignored()) ? ", Ignored" : "");
+ }
+ }
+}
+
+std::string NetworkManager::GetState() const {
+ StrMap map;
+ for (NetworkMap::const_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(const 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, 0);
+ network->SetState(state);
+ networks_[name] = network;
+ }
+}
+
+#ifdef POSIX
+// Gets the default gateway for the specified interface.
+uint32 GetDefaultGateway(const std::string& name) {
+#ifdef OSX
+ // TODO(juberti): /proc/net/route doesn't exist,
+ // Use ioctl to get the routing table
+ return 0xFFFFFFFF;
+#endif
+
+ uint32 gateway_ip = 0;
+
+ FileStream fs;
+ if (fs.Open("/proc/net/route", "r")) {
+ std::string line;
+ while (fs.ReadLine(&line) == SR_SUCCESS && gateway_ip == 0) {
+ char iface[16];
+ unsigned int ip, gw;
+ if (sscanf(line.c_str(), "%7s %8X %8X", iface, &ip, &gw) == 3 &&
+ name == iface && ip == 0) {
+ gateway_ip = ntohl(gw);
+ }
+ }
+ }
+
+ return gateway_ip;
+}
+
+
+bool NetworkManager::CreateNetworks(bool include_ignored,
+ std::vector<Network*>* networks) {
+ int fd;
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ LOG_ERR(LERROR) << "socket";
+ return false;
+ }
+
+ struct ifconf ifc;
+ ifc.ifc_len = 64 * sizeof(struct ifreq);
+ ifc.ifc_buf = new char[ifc.ifc_len];
+
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+ LOG_ERR(LERROR) << "ioctl";
+ return false;
+ }
+ 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);
+ scoped_ptr<Network> network(
+ new Network(ptr->ifr_name, ptr->ifr_name, ip,
+ GetDefaultGateway(ptr->ifr_name)));
+ network->set_ignored(IsIgnoredNetwork(*network));
+ if (include_ignored || !network->ignored()) {
+ networks->push_back(network.release());
+ }
+ }
+
+#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);
+ return true;
+}
+#endif // POSIX
+
+#ifdef WIN32
+bool NetworkManager::CreateNetworks(bool include_ignored,
+ std::vector<Network*>* networks) {
+ IP_ADAPTER_INFO info_temp;
+ ULONG len = 0;
+
+ if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW)
+ // This just means there's zero networks, which is not an error.
+ return true;
+
+ scoped_array<char> buf(new char[len]);
+ IP_ADAPTER_INFO *infos = reinterpret_cast<IP_ADAPTER_INFO *>(buf.get());
+ DWORD ret = GetAdaptersInfo(infos, &len);
+ if (ret != NO_ERROR) {
+ LOG_ERR_EX(LS_ERROR, ret) << "GetAdaptersInfo failed";
+ return false;
+ }
+
+ int count = 0;
+ for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) {
+ // Ignore the loopback device.
+ if (info->Type == MIB_IF_TYPE_LOOPBACK) {
+ continue;
+ }
+
+ // In non-debug builds, don't transmit the network name because of
+ // privacy concerns. Transmit a number instead.
+ std::string name;
+#ifdef _DEBUG
+ name = info->Description;
+#else // !_DEBUG
+ std::ostringstream ost;
+ ost << count;
+ name = ost.str();
+ count++;
+#endif // !_DEBUG
+
+ scoped_ptr<Network> network(new Network(name, info->Description,
+ SocketAddress::StringToIP(info->IpAddressList.IpAddress.String),
+ SocketAddress::StringToIP(info->GatewayList.IpAddress.String)));
+ network->set_ignored(IsIgnoredNetwork(*network));
+ if (include_ignored || !network->ignored()) {
+ networks->push_back(network.release());
+ }
+ }
+
+ return true;
+}
+#endif // WIN32
+
+bool NetworkManager::IsIgnoredNetwork(const Network& network) {
+#ifdef POSIX
+ // Ignore local networks (lo, lo0, etc)
+ // Also filter out VMware interfaces, typically named vmnet1 and vmnet8
+ if (strncmp(network.name().c_str(), "lo", 2) == 0 ||
+ strncmp(network.name().c_str(), "vmnet", 5) == 0) {
+ return true;
+ }
+#elif defined(WIN32)
+ // Ignore any HOST side vmware adapters with a description like:
+ // VMware Virtual Ethernet Adapter for VMnet1
+ // but don't ignore any GUEST side adapters with a description like:
+ // VMware Accelerated AMD PCNet Adapter #2
+ if (strstr(network.description().c_str(), "VMnet") != NULL) {
+ return true;
+ }
+#endif
+
+ // Ignore any networks with a 0.x.y.z IP
+ return (network.ip() < 0x01000000);
+}
+
+bool NetworkManager::EnumNetworks(bool include_ignored,
+ std::vector<Network*>* result) {
+ return CreateNetworks(include_ignored, result);
+}
+
+
+Network::Network(const std::string& name, const std::string& desc,
+ uint32 ip, uint32 gateway_ip)
+ : name_(name), description_(desc), ip_(ip), gateway_ip_(gateway_ip),
+ ignored_(false), uniform_numerator_(0), uniform_denominator_(0),
+ exponential_numerator_(0), exponential_denominator_(0),
+ quality_(kDefaultQuality) {
+ last_data_time_ = Time();
+
+ // TODO(juberti): 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;
+}
+
+std::string Network::ToString() const {
+ std::stringstream ss;
+ // Print out the first space-terminated token of the network desc, plus
+ // the IP address.
+ ss << "Net[" << description_.substr(0, description_.find(' '))
+ << ":" << SocketAddress::IPToString(ip_) << "]";
+ return ss.str();
+}
+
+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() const {
+ StrMap map;
+ map["lt"] = talk_base::ToString<uint32>(last_data_time_);
+ map["un"] = talk_base::ToString<double>(uniform_numerator_);
+ map["ud"] = talk_base::ToString<double>(uniform_denominator_);
+ map["en"] = talk_base::ToString<double>(exponential_numerator_);
+ map["ed"] = talk_base::ToString<double>(exponential_denominator_);
+
+ std::string str;
+ BuildMap(map, str);
+ return str;
+}
+
+void Network::SetState(const 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 talk_base
diff --git a/third_party/libjingle/source/talk/base/network.h b/third_party/libjingle/source/talk/base/network.h
new file mode 100644
index 0000000..8153e54
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/network.h
@@ -0,0 +1,170 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_NETWORK_H_
+#define TALK_BASE_NETWORK_H_
+
+#include <deque>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+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:
+ virtual ~NetworkManager();
+
+ // 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.
+ // Does not include ignored networks.
+ bool GetNetworks(std::vector<Network*>* networks);
+
+ // Logs the available networks.
+ void DumpNetworks(bool include_ignored);
+
+ // Reads and writes the state of the quality database in a string format.
+ std::string GetState() const;
+ void SetState(const std::string& str);
+
+ // Creates a network object for each network available on the machine.
+ static bool CreateNetworks(bool include_ignored,
+ std::vector<Network*>* networks);
+ // Determines if a network should be ignored.
+ static bool IsIgnoredNetwork(const Network& network);
+
+ protected:
+ // Fills the supplied list with all usable networks. Overrideable.
+ virtual bool EnumNetworks(bool include_ignored,
+ 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, const std::string& description,
+ uint32 ip, uint32 gateway_ip);
+
+ // Returns the index of this network. This is considered the primary key
+ // that identifies each network.
+ const std::string& name() const { return name_; }
+
+ // Returns the OS-assigned name for this network. This is useful for
+ // debugging but should not be sent over the wire (for privacy reasons).
+ const std::string& description() const { return description_; }
+
+ // Identifies the current IP address used by this network.
+ uint32 ip() const { return ip_; }
+ void set_ip(uint32 ip) { ip_ = ip; }
+
+ // Identifies the current gateway IP address used by this network.
+ uint32 gateway_ip() const { return gateway_ip_; }
+ void set_gateway_ip(uint32 ip) { gateway_ip_ = ip; }
+
+ // Indicates whether this network should be ignored, perhaps because the
+ // IP/gateway is 0, or the interface is one we know is invalid.
+ bool ignored() const { return ignored_; }
+ void set_ignored(bool ignored) { ignored_ = ignored; }
+
+ // 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_; }
+
+ // Debugging description of this network
+ std::string ToString() const;
+
+ private:
+ typedef std::vector<NetworkSession*> SessionList;
+
+ std::string name_;
+ std::string description_;
+ uint32 ip_;
+ uint32 gateway_ip_;
+ bool ignored_;
+ 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(const std::string& str);
+ std::string GetState() const;
+
+ 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:
+ virtual ~NetworkSession() { }
+
+ // 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 talk_base
+
+#endif // TALK_BASE_NETWORK_H_
diff --git a/third_party/libjingle/source/talk/base/openssladapter.cc b/third_party/libjingle/source/talk/base/openssladapter.cc
new file mode 100644
index 0000000..97a5770
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/openssladapter.cc
@@ -0,0 +1,861 @@
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#if HAVE_OPENSSL_SSL_H
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/openssladapter.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/Equifax_Secure_Global_eBusiness_CA-1.h"
+
+// TODO: Use a nicer abstraction for mutex.
+
+#if defined(WIN32)
+ #define MUTEX_TYPE HANDLE
+ #define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL)
+ #define MUTEX_CLEANUP(x) CloseHandle(x)
+ #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE)
+ #define MUTEX_UNLOCK(x) ReleaseMutex(x)
+ #define THREAD_ID GetCurrentThreadId()
+#elif defined(_POSIX_THREADS)
+ // _POSIX_THREADS is normally defined in unistd.h if pthreads are available
+ // on your platform.
+ #define MUTEX_TYPE pthread_mutex_t
+ #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
+ #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+ #define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+ #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+ #define THREAD_ID pthread_self()
+#else
+ #error You must define mutex operations appropriate for your platform!
+#endif
+
+struct CRYPTO_dynlock_value {
+ MUTEX_TYPE mutex;
+};
+
+//////////////////////////////////////////////////////////////////////
+// SocketBIO
+//////////////////////////////////////////////////////////////////////
+
+static int socket_write(BIO* h, const char* buf, int num);
+static int socket_read(BIO* h, char* buf, int size);
+static int socket_puts(BIO* h, const char* str);
+static long socket_ctrl(BIO* h, int cmd, long arg1, void* arg2);
+static int socket_new(BIO* h);
+static int socket_free(BIO* data);
+
+static BIO_METHOD methods_socket = {
+ BIO_TYPE_BIO,
+ "socket",
+ socket_write,
+ socket_read,
+ socket_puts,
+ 0,
+ socket_ctrl,
+ socket_new,
+ socket_free,
+ NULL,
+};
+
+BIO_METHOD* BIO_s_socket2() { return(&methods_socket); }
+
+BIO* BIO_new_socket(talk_base::AsyncSocket* socket) {
+ BIO* ret = BIO_new(BIO_s_socket2());
+ if (ret == NULL) {
+ return NULL;
+ }
+ ret->ptr = socket;
+ return ret;
+}
+
+static int socket_new(BIO* b) {
+ b->shutdown = 0;
+ b->init = 1;
+ b->num = 0; // 1 means socket closed
+ b->ptr = 0;
+ return 1;
+}
+
+static int socket_free(BIO* b) {
+ if (b == NULL)
+ return 0;
+ return 1;
+}
+
+static int socket_read(BIO* b, char* out, int outl) {
+ if (!out)
+ return -1;
+ talk_base::AsyncSocket* socket = static_cast<talk_base::AsyncSocket*>(b->ptr);
+ BIO_clear_retry_flags(b);
+ int result = socket->Recv(out, outl);
+ if (result > 0) {
+ return result;
+ } else if (result == 0) {
+ b->num = 1;
+ } else if (socket->IsBlocking()) {
+ BIO_set_retry_read(b);
+ }
+ return -1;
+}
+
+static int socket_write(BIO* b, const char* in, int inl) {
+ if (!in)
+ return -1;
+ talk_base::AsyncSocket* socket = static_cast<talk_base::AsyncSocket*>(b->ptr);
+ BIO_clear_retry_flags(b);
+ int result = socket->Send(in, inl);
+ if (result > 0) {
+ return result;
+ } else if (socket->IsBlocking()) {
+ BIO_set_retry_write(b);
+ }
+ return -1;
+}
+
+static int socket_puts(BIO* b, const char* str) {
+ return socket_write(b, str, strlen(str));
+}
+
+static long socket_ctrl(BIO* b, int cmd, long num, void* ptr) {
+ UNUSED(num);
+ UNUSED(ptr);
+
+ switch (cmd) {
+ case BIO_CTRL_RESET:
+ return 0;
+ case BIO_CTRL_EOF:
+ return b->num;
+ case BIO_CTRL_WPENDING:
+ case BIO_CTRL_PENDING:
+ return 0;
+ case BIO_CTRL_FLUSH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// OpenSSLAdapter
+/////////////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+// This array will store all of the mutexes available to OpenSSL.
+static MUTEX_TYPE* mutex_buf = NULL;
+
+static void locking_function(int mode, int n, const char * file, int line) {
+ if (mode & CRYPTO_LOCK) {
+ MUTEX_LOCK(mutex_buf[n]);
+ } else {
+ MUTEX_UNLOCK(mutex_buf[n]);
+ }
+}
+
+static pthread_t id_function() {
+ return THREAD_ID;
+}
+
+static CRYPTO_dynlock_value* dyn_create_function(const char* file, int line) {
+ CRYPTO_dynlock_value* value = new CRYPTO_dynlock_value;
+ if (!value)
+ return NULL;
+ MUTEX_SETUP(value->mutex);
+ return value;
+}
+
+static void dyn_lock_function(int mode, CRYPTO_dynlock_value* l,
+ const char* file, int line) {
+ if (mode & CRYPTO_LOCK) {
+ MUTEX_LOCK(l->mutex);
+ } else {
+ MUTEX_UNLOCK(l->mutex);
+ }
+}
+
+static void dyn_destroy_function(CRYPTO_dynlock_value* l,
+ const char* file, int line) {
+ MUTEX_CLEANUP(l->mutex);
+ delete l;
+}
+
+VerificationCallback OpenSSLAdapter::custom_verify_callback_ = NULL;
+
+bool OpenSSLAdapter::InitializeSSL(VerificationCallback callback) {
+ if (!InitializeSSLThread() || !SSL_library_init())
+ return false;
+ SSL_load_error_strings();
+ ERR_load_BIO_strings();
+ OpenSSL_add_all_algorithms();
+ RAND_poll();
+ custom_verify_callback_ = callback;
+ return true;
+}
+
+bool OpenSSLAdapter::InitializeSSLThread() {
+ mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()];
+ if (!mutex_buf)
+ return false;
+ for (int i = 0; i < CRYPTO_num_locks(); ++i)
+ MUTEX_SETUP(mutex_buf[i]);
+
+ // we need to cast our id_function to return an unsigned long -- pthread_t is a pointer
+ CRYPTO_set_id_callback((unsigned long (*)())id_function);
+ CRYPTO_set_locking_callback(locking_function);
+ CRYPTO_set_dynlock_create_callback(dyn_create_function);
+ CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
+ CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
+ return true;
+}
+
+bool OpenSSLAdapter::CleanupSSL() {
+ if (!mutex_buf)
+ return false;
+ CRYPTO_set_id_callback(NULL);
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_dynlock_create_callback(NULL);
+ CRYPTO_set_dynlock_lock_callback(NULL);
+ CRYPTO_set_dynlock_destroy_callback(NULL);
+ for (int i = 0; i < CRYPTO_num_locks(); ++i)
+ MUTEX_CLEANUP(mutex_buf[i]);
+ delete [] mutex_buf;
+ mutex_buf = NULL;
+ return true;
+}
+
+OpenSSLAdapter::OpenSSLAdapter(AsyncSocket* socket)
+ : SSLAdapter(socket),
+ state_(SSL_NONE),
+ ssl_read_needs_write_(false),
+ ssl_write_needs_read_(false),
+ restartable_(false),
+ ssl_(NULL), ssl_ctx_(NULL),
+ custom_verification_succeeded_(false) {
+}
+
+OpenSSLAdapter::~OpenSSLAdapter() {
+ Cleanup();
+}
+
+int
+OpenSSLAdapter::StartSSL(const char* hostname, bool restartable) {
+ if (state_ != SSL_NONE)
+ return -1;
+
+ ssl_host_name_ = hostname;
+ restartable_ = restartable;
+
+ if (socket_->GetState() != Socket::CS_CONNECTED) {
+ state_ = SSL_WAIT;
+ return 0;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ Error("BeginSSL", err, false);
+ return err;
+ }
+
+ return 0;
+}
+
+int
+OpenSSLAdapter::BeginSSL() {
+ LOG(LS_INFO) << "BeginSSL: " << ssl_host_name_;
+ ASSERT(state_ == SSL_CONNECTING);
+
+ int err = 0;
+ BIO* bio = NULL;
+
+ // First set up the context
+ if (!ssl_ctx_)
+ ssl_ctx_ = SetupSSLContext();
+
+ if (!ssl_ctx_) {
+ err = -1;
+ goto ssl_error;
+ }
+
+ bio = BIO_new_socket(static_cast<AsyncSocketAdapter*>(socket_));
+ if (!bio) {
+ err = -1;
+ goto ssl_error;
+ }
+
+ ssl_ = SSL_new(ssl_ctx_);
+ if (!ssl_) {
+ err = -1;
+ goto ssl_error;
+ }
+
+ SSL_set_app_data(ssl_, this);
+
+ SSL_set_bio(ssl_, bio, bio);
+ SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE |
+ SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ // the SSL object owns the bio now
+ bio = NULL;
+
+ // Do the connect
+ err = ContinueSSL();
+ if (err != 0)
+ goto ssl_error;
+
+ return err;
+
+ssl_error:
+ Cleanup();
+ if (bio)
+ BIO_free(bio);
+
+ return err;
+}
+
+int
+OpenSSLAdapter::ContinueSSL() {
+ LOG(LS_INFO) << "ContinueSSL";
+ ASSERT(state_ == SSL_CONNECTING);
+
+ int code = SSL_connect(ssl_);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ LOG(LS_INFO) << " -- success";
+
+ if (!SSLPostConnectionCheck(ssl_, ssl_host_name_.c_str())) {
+ LOG(LS_ERROR) << "TLS post connection check failed";
+ // make sure we close the socket
+ Cleanup();
+ // The connect failed so return -1 to shut down the socket
+ return -1;
+ }
+
+ state_ = SSL_CONNECTED;
+ AsyncSocketAdapter::OnConnectEvent(this);
+#if 0 // TODO: worry about this
+ // Don't let ourselves go away during the callbacks
+ PRefPtr<OpenSSLAdapter> lock(this);
+ LOG(LS_INFO) << " -- onStreamReadable";
+ AsyncSocketAdapter::OnReadEvent(this);
+ LOG(LS_INFO) << " -- onStreamWriteable";
+ AsyncSocketAdapter::OnWriteEvent(this);
+#endif
+ break;
+
+ case SSL_ERROR_WANT_READ:
+ LOG(LS_INFO) << " -- error want read";
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ LOG(LS_INFO) << " -- error want write";
+ break;
+
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ LOG(LS_INFO) << " -- error " << code;
+ return (code != 0) ? code : -1;
+ }
+
+ return 0;
+}
+
+void
+OpenSSLAdapter::Error(const char* context, int err, bool signal) {
+ LOG(LS_WARNING) << "SChannelAdapter::Error("
+ << context << ", " << err << ")";
+ state_ = SSL_ERROR;
+ SetError(err);
+ if (signal)
+ AsyncSocketAdapter::OnCloseEvent(this, err);
+}
+
+void
+OpenSSLAdapter::Cleanup() {
+ LOG(LS_INFO) << "Cleanup";
+
+ state_ = SSL_NONE;
+ ssl_read_needs_write_ = false;
+ ssl_write_needs_read_ = false;
+ custom_verification_succeeded_ = false;
+
+ if (ssl_) {
+ SSL_free(ssl_);
+ ssl_ = NULL;
+ }
+
+ if (ssl_ctx_) {
+ SSL_CTX_free(ssl_ctx_);
+ ssl_ctx_ = NULL;
+ }
+}
+
+//
+// AsyncSocket Implementation
+//
+
+int
+OpenSSLAdapter::Send(const void* pv, size_t cb) {
+ //LOG(LS_INFO) << "OpenSSLAdapter::Send(" << cb << ")";
+
+ switch (state_) {
+ case SSL_NONE:
+ return AsyncSocketAdapter::Send(pv, cb);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ default:
+ return SOCKET_ERROR;
+ }
+
+ // OpenSSL will return an error if we try to write zero bytes
+ if (cb == 0)
+ return 0;
+
+ ssl_write_needs_read_ = false;
+
+ int code = SSL_write(ssl_, pv, cb);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ //LOG(LS_INFO) << " -- success";
+ return code;
+ case SSL_ERROR_WANT_READ:
+ //LOG(LS_INFO) << " -- error want read";
+ ssl_write_needs_read_ = true;
+ SetError(EWOULDBLOCK);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ //LOG(LS_INFO) << " -- error want write";
+ SetError(EWOULDBLOCK);
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ //LOG(LS_INFO) << " -- remote side closed";
+ SetError(EWOULDBLOCK);
+ // do we need to signal closure?
+ break;
+ default:
+ //LOG(LS_INFO) << " -- error " << code;
+ Error("SSL_write", (code ? code : -1), false);
+ break;
+ }
+
+ return SOCKET_ERROR;
+}
+
+int
+OpenSSLAdapter::Recv(void* pv, size_t cb) {
+ //LOG(LS_INFO) << "OpenSSLAdapter::Recv(" << cb << ")";
+ switch (state_) {
+
+ case SSL_NONE:
+ return AsyncSocketAdapter::Recv(pv, cb);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ default:
+ return SOCKET_ERROR;
+ }
+
+ // Don't trust OpenSSL with zero byte reads
+ if (cb == 0)
+ return 0;
+
+ ssl_read_needs_write_ = false;
+
+ int code = SSL_read(ssl_, pv, cb);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ //LOG(LS_INFO) << " -- success";
+ return code;
+ case SSL_ERROR_WANT_READ:
+ //LOG(LS_INFO) << " -- error want read";
+ SetError(EWOULDBLOCK);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ //LOG(LS_INFO) << " -- error want write";
+ ssl_read_needs_write_ = true;
+ SetError(EWOULDBLOCK);
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ //LOG(LS_INFO) << " -- remote side closed";
+ SetError(EWOULDBLOCK);
+ // do we need to signal closure?
+ break;
+ default:
+ //LOG(LS_INFO) << " -- error " << code;
+ Error("SSL_read", (code ? code : -1), false);
+ break;
+ }
+
+ return SOCKET_ERROR;
+}
+
+int
+OpenSSLAdapter::Close() {
+ Cleanup();
+ state_ = restartable_ ? SSL_WAIT : SSL_NONE;
+ return AsyncSocketAdapter::Close();
+}
+
+Socket::ConnState
+OpenSSLAdapter::GetState() const {
+ //if (signal_close_)
+ // return CS_CONNECTED;
+ ConnState state = socket_->GetState();
+ if ((state == CS_CONNECTED)
+ && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING)))
+ state = CS_CONNECTING;
+ return state;
+}
+
+void
+OpenSSLAdapter::OnConnectEvent(AsyncSocket* socket) {
+ LOG(LS_INFO) << "OpenSSLAdapter::OnConnectEvent";
+ if (state_ != SSL_WAIT) {
+ ASSERT(state_ == SSL_NONE);
+ AsyncSocketAdapter::OnConnectEvent(socket);
+ return;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+ }
+}
+
+void
+OpenSSLAdapter::OnReadEvent(AsyncSocket* socket) {
+ //LOG(LS_INFO) << "OpenSSLAdapter::OnReadEvent";
+
+ if (state_ == SSL_NONE) {
+ AsyncSocketAdapter::OnReadEvent(socket);
+ return;
+ }
+
+ if (state_ == SSL_CONNECTING) {
+ if (int err = ContinueSSL()) {
+ Error("ContinueSSL", err);
+ }
+ return;
+ }
+
+ if (state_ != SSL_CONNECTED)
+ return;
+
+ // Don't let ourselves go away during the callbacks
+ //PRefPtr<OpenSSLAdapter> lock(this); // TODO: fix this
+ if (ssl_write_needs_read_) {
+ //LOG(LS_INFO) << " -- onStreamWriteable";
+ AsyncSocketAdapter::OnWriteEvent(socket);
+ }
+
+ //LOG(LS_INFO) << " -- onStreamReadable";
+ AsyncSocketAdapter::OnReadEvent(socket);
+}
+
+void
+OpenSSLAdapter::OnWriteEvent(AsyncSocket* socket) {
+ //LOG(LS_INFO) << "OpenSSLAdapter::OnWriteEvent";
+
+ if (state_ == SSL_NONE) {
+ AsyncSocketAdapter::OnWriteEvent(socket);
+ return;
+ }
+
+ if (state_ == SSL_CONNECTING) {
+ if (int err = ContinueSSL()) {
+ Error("ContinueSSL", err);
+ }
+ return;
+ }
+
+ if (state_ != SSL_CONNECTED)
+ return;
+
+ // Don't let ourselves go away during the callbacks
+ //PRefPtr<OpenSSLAdapter> lock(this); // TODO: fix this
+
+ if (ssl_read_needs_write_) {
+ //LOG(LS_INFO) << " -- onStreamReadable";
+ AsyncSocketAdapter::OnReadEvent(socket);
+ }
+
+ //LOG(LS_INFO) << " -- onStreamWriteable";
+ AsyncSocketAdapter::OnWriteEvent(socket);
+}
+
+void
+OpenSSLAdapter::OnCloseEvent(AsyncSocket* socket, int err) {
+ LOG(LS_INFO) << "OpenSSLAdapter::OnCloseEvent(" << err << ")";
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+}
+
+// This code is taken from the "Network Security with OpenSSL"
+// sample in chapter 5
+
+bool OpenSSLAdapter::VerifyServerName(SSL* ssl, const char* host,
+ bool ignore_bad_cert) {
+ if (!host)
+ return false;
+
+ // Checking the return from SSL_get_peer_certificate here is not strictly
+ // necessary. With our setup, it is not possible for it to return
+ // NULL. However, it is good form to check the return.
+ X509* certificate = SSL_get_peer_certificate(ssl);
+ if (!certificate)
+ return false;
+
+#ifdef _DEBUG
+ {
+ LOG(LS_INFO) << "Certificate from server:";
+ BIO* mem = BIO_new(BIO_s_mem());
+ X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER);
+ BIO_write(mem, "\0", 1);
+ char* buffer;
+ BIO_get_mem_data(mem, &buffer);
+ LOG(LS_INFO) << buffer;
+ BIO_free(mem);
+
+ char* cipher_description =
+ SSL_CIPHER_description(SSL_get_current_cipher(ssl), NULL, 128);
+ LOG(LS_INFO) << "Cipher: " << cipher_description;
+ OPENSSL_free(cipher_description);
+ }
+#endif
+
+ bool ok = false;
+ int extension_count = X509_get_ext_count(certificate);
+ for (int i = 0; i < extension_count; ++i) {
+ X509_EXTENSION* extension = X509_get_ext(certificate, i);
+ int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
+
+ if (extension_nid == NID_subject_alt_name) {
+ X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension);
+ if (!meth)
+ break;
+
+ void* ext_str = NULL;
+
+ // We assign this to a local variable, instead of passing the address
+ // directly to ASN1_item_d2i.
+ // See http://readlist.com/lists/openssl.org/openssl-users/0/4761.html.
+ unsigned char* ext_value_data = extension->value->data;
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+ const unsigned char **ext_value_data_ptr =
+ (const_cast<const unsigned char **>(&ext_value_data));
+#else
+ unsigned char **ext_value_data_ptr = &ext_value_data;
+#endif
+
+ if (meth->it) {
+ ext_str = ASN1_item_d2i(NULL, ext_value_data_ptr,
+ extension->value->length,
+ ASN1_ITEM_ptr(meth->it));
+ } else {
+ ext_str = meth->d2i(NULL, ext_value_data_ptr, extension->value->length);
+ }
+
+ STACK_OF(CONF_VALUE)* value = meth->i2v(meth, ext_str, NULL);
+ for (int j = 0; j < sk_CONF_VALUE_num(value); ++j) {
+ CONF_VALUE* nval = sk_CONF_VALUE_value(value, j);
+ // The value for nval can contain wildcards
+ if (!strcmp(nval->name, "DNS") && string_match(host, nval->value)) {
+ ok = true;
+ break;
+ }
+ }
+ sk_CONF_VALUE_pop_free(value, X509V3_conf_free);
+ value = NULL;
+
+ if (meth->it) {
+ ASN1_item_free(reinterpret_cast<ASN1_VALUE*>(ext_str), meth->it);
+ } else {
+ meth->ext_free(ext_str);
+ }
+ ext_str = NULL;
+ }
+ if (ok)
+ break;
+ }
+
+ char data[256];
+ X509_name_st* subject;
+ if (!ok
+ && (subject = X509_get_subject_name(certificate))
+ && (X509_NAME_get_text_by_NID(subject, NID_commonName,
+ data, sizeof(data)) > 0)) {
+ data[sizeof(data)-1] = 0;
+ if (_stricmp(data, host) == 0)
+ ok = true;
+ }
+
+ X509_free(certificate);
+
+ if (!ok && ignore_bad_cert) {
+ LOG(LS_WARNING) << "TLS certificate check FAILED. "
+ << "Allowing connection anyway.";
+ ok = true;
+ }
+
+ return ok;
+}
+
+bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) {
+ bool ok = VerifyServerName(ssl, host, ignore_bad_cert());
+
+ if (ok) {
+ ok = (SSL_get_verify_result(ssl) == X509_V_OK ||
+ custom_verification_succeeded_);
+ }
+
+ if (!ok && ignore_bad_cert()) {
+ LOG(LS_INFO) << "Other TLS post connection checks failed.";
+ ok = true;
+ }
+
+ return ok;
+}
+
+#if _DEBUG
+
+// We only use this for tracing and so it is only needed in debug mode
+
+void
+OpenSSLAdapter::SSLInfoCallback(const SSL* s, int where, int ret) {
+ const char* str = "undefined";
+ int w = where & ~SSL_ST_MASK;
+ if (w & SSL_ST_CONNECT) {
+ str = "SSL_connect";
+ } else if (w & SSL_ST_ACCEPT) {
+ str = "SSL_accept";
+ }
+ if (where & SSL_CB_LOOP) {
+ LOG(LS_INFO) << str << ":" << SSL_state_string_long(s);
+ } else if (where & SSL_CB_ALERT) {
+ str = (where & SSL_CB_READ) ? "read" : "write";
+ LOG(LS_INFO) << "SSL3 alert " << str
+ << ":" << SSL_alert_type_string_long(ret)
+ << ":" << SSL_alert_desc_string_long(ret);
+ } else if (where & SSL_CB_EXIT) {
+ if (ret == 0) {
+ LOG(LS_INFO) << str << ":failed in " << SSL_state_string_long(s);
+ } else if (ret < 0) {
+ LOG(LS_INFO) << str << ":error in " << SSL_state_string_long(s);
+ }
+ }
+}
+
+#endif // _DEBUG
+
+int
+OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
+#if _DEBUG
+ if (!ok) {
+ char data[256];
+ X509* cert = X509_STORE_CTX_get_current_cert(store);
+ int depth = X509_STORE_CTX_get_error_depth(store);
+ int err = X509_STORE_CTX_get_error(store);
+
+ LOG(LS_INFO) << "Error with certificate at depth: " << depth;
+ X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
+ LOG(LS_INFO) << " issuer = " << data;
+ X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
+ LOG(LS_INFO) << " subject = " << data;
+ LOG(LS_INFO) << " err = " << err
+ << ":" << X509_verify_cert_error_string(err);
+ }
+#endif
+
+ // Get our stream pointer from the store
+ SSL* ssl = reinterpret_cast<SSL*>(
+ X509_STORE_CTX_get_ex_data(store,
+ SSL_get_ex_data_X509_STORE_CTX_idx()));
+
+ OpenSSLAdapter* stream =
+ reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
+
+ if (!ok && custom_verify_callback_) {
+ void* cert =
+ reinterpret_cast<void*>(X509_STORE_CTX_get_current_cert(store));
+ if (custom_verify_callback_(cert)) {
+ stream->custom_verification_succeeded_ = true;
+ LOG(LS_INFO) << "validated certificate using custom callback";
+ ok = true;
+ }
+ }
+
+ if (!ok && stream->ignore_bad_cert()) {
+ LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
+ ok = 1;
+ }
+
+ return ok;
+}
+
+bool OpenSSLAdapter::ConfigureTrustedRootCertificates(SSL_CTX* ctx) {
+ // Add the root cert to the SSL context
+ // TODO(sdoyon): this cert appears to be the wrong one.
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+ const unsigned char* cert_buffer
+#else
+ unsigned char* cert_buffer
+#endif
+ = EquifaxSecureGlobalEBusinessCA1_certificate;
+ size_t cert_buffer_len = sizeof(EquifaxSecureGlobalEBusinessCA1_certificate);
+ X509* cert = d2i_X509(NULL, &cert_buffer, cert_buffer_len);
+ if (cert == NULL)
+ return false;
+ bool success = X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert);
+ X509_free(cert);
+ return success;
+}
+
+SSL_CTX*
+OpenSSLAdapter::SetupSSLContext() {
+ SSL_CTX* ctx = SSL_CTX_new(TLSv1_client_method());
+ if (ctx == NULL)
+ return NULL;
+
+ if (!ConfigureTrustedRootCertificates(ctx)) {
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+
+#ifdef _DEBUG
+ SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
+#endif
+
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
+ SSL_CTX_set_verify_depth(ctx, 4);
+ SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+
+ return ctx;
+}
+
+} // namespace talk_base
+
+#endif // HAVE_OPENSSL_SSL_H
diff --git a/third_party/libjingle/source/talk/base/openssladapter.h b/third_party/libjingle/source/talk/base/openssladapter.h
new file mode 100644
index 0000000..c89c292
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/openssladapter.h
@@ -0,0 +1,105 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_OPENSSLADAPTER_H__
+#define TALK_BASE_OPENSSLADAPTER_H__
+
+#include <string>
+#include "talk/base/ssladapter.h"
+
+typedef struct ssl_st SSL;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class OpenSSLAdapter : public SSLAdapter {
+public:
+ static bool InitializeSSL(VerificationCallback callback);
+ static bool InitializeSSLThread();
+ static bool CleanupSSL();
+
+ OpenSSLAdapter(AsyncSocket* socket);
+ virtual ~OpenSSLAdapter();
+
+ virtual int StartSSL(const char* hostname, bool restartable);
+ virtual int Send(const void* pv, size_t cb);
+ virtual int Recv(void* pv, size_t cb);
+ virtual int Close();
+
+ // Note that the socket returns ST_CONNECTING while SSL is being negotiated.
+ virtual ConnState GetState() const;
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket* socket);
+ virtual void OnReadEvent(AsyncSocket* socket);
+ virtual void OnWriteEvent(AsyncSocket* socket);
+ virtual void OnCloseEvent(AsyncSocket* socket, int err);
+
+private:
+ enum SSLState {
+ SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR
+ };
+
+ int BeginSSL();
+ int ContinueSSL();
+ void Error(const char* context, int err, bool signal = true);
+ void Cleanup();
+
+ static bool VerifyServerName(SSL* ssl, const char* host,
+ bool ignore_bad_cert);
+ bool SSLPostConnectionCheck(SSL* ssl, const char* host);
+#if _DEBUG
+ static void SSLInfoCallback(const SSL* s, int where, int ret);
+#endif // !_DEBUG
+ static int SSLVerifyCallback(int ok, X509_STORE_CTX* store);
+ static VerificationCallback custom_verify_callback_;
+ friend class OpenSSLStreamAdapter; // for custom_verify_callback_;
+
+ static bool ConfigureTrustedRootCertificates(SSL_CTX* ctx);
+ static SSL_CTX* SetupSSLContext();
+
+ SSLState state_;
+ bool ssl_read_needs_write_;
+ bool ssl_write_needs_read_;
+ // If true, socket will retain SSL configuration after Close.
+ bool restartable_;
+
+ SSL* ssl_;
+ SSL_CTX* ssl_ctx_;
+ std::string ssl_host_name_;
+
+ bool custom_verification_succeeded_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_OPENSSLADAPTER_H__
diff --git a/third_party/libjingle/source/talk/base/opensslidentity.cc b/third_party/libjingle/source/talk/base/opensslidentity.cc
new file mode 100644
index 0000000..a9d94b2
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/opensslidentity.cc
@@ -0,0 +1,274 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/opensslidentity.h"
+
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/crypto.h>
+
+#include "talk/base/logging.h"
+#include "talk/base/helpers.h"
+
+namespace talk_base {
+
+// We could have exposed a myriad of parameters for the crypto stuff,
+// but keeping it simple seems best.
+
+// Strength of generated keys. Those are RSA.
+static const int KEY_LENGTH = 1024;
+
+// Random bits for certificate serial number
+static const int SERIAL_RAND_BITS = 64;
+
+// Certificate validity lifetime
+static const int CERTIFICATE_LIFETIME = 60*60*24*365; // one year, arbitrarily
+
+// Generate a key pair. Caller is responsible for freeing the returned object.
+static EVP_PKEY* MakeKey() {
+ LOG(LS_INFO) << "Making key pair";
+ EVP_PKEY* pkey = EVP_PKEY_new();
+#if OPENSSL_VERSION_NUMBER < 0x00908000l
+ // Only RSA_generate_key is available. Use that.
+ RSA* rsa = RSA_generate_key(KEY_LENGTH, 0x10001, NULL, NULL);
+ if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
+ EVP_PKEY_free(pkey);
+ RSA_free(rsa);
+ return NULL;
+ }
+#else
+ // RSA_generate_key is deprecated. Use _ex version.
+ BIGNUM* exponent = BN_new();
+ RSA* rsa = RSA_new();
+ if (!pkey || !exponent || !rsa ||
+ !BN_set_word(exponent, 0x10001) || // 65537 RSA exponent
+ !RSA_generate_key_ex(rsa, KEY_LENGTH, exponent, NULL) ||
+ !EVP_PKEY_assign_RSA(pkey, rsa)) {
+ EVP_PKEY_free(pkey);
+ BN_free(exponent);
+ RSA_free(rsa);
+ return NULL;
+ }
+ // ownership of rsa struct was assigned, don't free it.
+ BN_free(exponent);
+#endif
+ LOG(LS_INFO) << "Returning key pair";
+ return pkey;
+}
+
+// Generate a self-signed certificate, with the public key from the
+// given key pair. Caller is responsible for freeing the returned object.
+static X509* MakeCertificate(EVP_PKEY* pkey, const char* common_name) {
+ LOG(LS_INFO) << "Making certificate for " << common_name;
+ X509* x509 = NULL;
+ BIGNUM* serial_number = NULL;
+ X509_NAME* name = NULL;
+
+ if ((x509=X509_new()) == NULL)
+ goto error;
+
+ if (!X509_set_pubkey(x509, pkey))
+ goto error;
+
+ // serial number
+ // temporary reference to serial number inside x509 struct
+ ASN1_INTEGER* asn1_serial_number;
+ if (!(serial_number = BN_new()) ||
+ !BN_pseudo_rand(serial_number, SERIAL_RAND_BITS, 0, 0) ||
+ !(asn1_serial_number = X509_get_serialNumber(x509)) ||
+ !BN_to_ASN1_INTEGER(serial_number, asn1_serial_number))
+ goto error;
+
+ if (!X509_set_version(x509, 0L)) // version 1
+ goto error;
+
+ // There are a lot of possible components for the name entries. In
+ // our P2P SSL mode however, the certificates are pre-exchanged
+ // (through the secure XMPP channel), and so the certificate
+ // identification is arbitrary. It can't be empty, so we set some
+ // arbitrary common_name. Note that this certificate goes out in
+ // clear during SSL negotiation, so there may be a privacy issue in
+ // putting anything recognizable here.
+ if (!(name = X509_NAME_new()) ||
+ !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_UTF8,
+ (unsigned char*)common_name, -1, -1, 0) ||
+ !X509_set_subject_name(x509, name) ||
+ !X509_set_issuer_name(x509, name))
+ goto error;
+
+ if (!X509_gmtime_adj(X509_get_notBefore(x509), 0) ||
+ !X509_gmtime_adj(X509_get_notAfter(x509), CERTIFICATE_LIFETIME))
+ goto error;
+
+ if (!X509_sign(x509, pkey, EVP_sha1()))
+ goto error;
+
+ BN_free(serial_number);
+ X509_NAME_free(name);
+ LOG(LS_INFO) << "Returning certificate";
+ return x509;
+
+ error:
+ BN_free(serial_number);
+ X509_NAME_free(name);
+ X509_free(x509);
+ return NULL;
+}
+
+// This dumps the SSL error stack to the log.
+static void LogSSLErrors(const std::string& prefix) {
+ char error_buf[200];
+ unsigned long err;
+ while ((err = ERR_get_error())) {
+ ERR_error_string_n(err, error_buf, sizeof(error_buf));
+ LOG(LS_ERROR) << prefix << ": " << error_buf << "\n";
+ }
+}
+
+OpenSSLKeyPair* OpenSSLKeyPair::Generate() {
+ EVP_PKEY* pkey = MakeKey();
+ if (!pkey) {
+ LogSSLErrors("Generating key pair");
+ return NULL;
+ }
+ return new OpenSSLKeyPair(pkey);
+}
+
+OpenSSLKeyPair::~OpenSSLKeyPair() {
+ EVP_PKEY_free(pkey_);
+}
+
+void OpenSSLKeyPair::AddReference() {
+ CRYPTO_add(&pkey_->references, 1, CRYPTO_LOCK_EVP_PKEY);
+}
+
+#ifdef _DEBUG
+// Print a certificate to the log, for debugging.
+static void PrintCert(X509* x509) {
+ BIO* temp_memory_bio = BIO_new(BIO_s_mem());
+ if (!temp_memory_bio) {
+ LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
+ return;
+ }
+ X509_print_ex(temp_memory_bio, x509, XN_FLAG_SEP_CPLUS_SPC, 0);
+ BIO_write(temp_memory_bio, "\0", 1);
+ char* buffer;
+ BIO_get_mem_data(temp_memory_bio, &buffer);
+ LOG(LS_VERBOSE) << buffer;
+ BIO_free(temp_memory_bio);
+}
+#endif
+
+OpenSSLCertificate* OpenSSLCertificate::Generate(
+ OpenSSLKeyPair* key_pair, const std::string& common_name) {
+ std::string actual_common_name = common_name;
+ if (actual_common_name.empty())
+ // Use a random string, arbitrarily 8chars long.
+ actual_common_name = CreateRandomString(8);
+ X509* x509 = MakeCertificate(key_pair->pkey(), actual_common_name.c_str());
+ if (!x509) {
+ LogSSLErrors("Generating certificate");
+ return NULL;
+ }
+#ifdef _DEBUG
+ PrintCert(x509);
+#endif
+ return new OpenSSLCertificate(x509);
+}
+
+OpenSSLCertificate* OpenSSLCertificate::FromPEMString(
+ const std::string& pem_string, int* pem_length) {
+ BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
+ if (!bio)
+ return NULL;
+ (void)BIO_set_close(bio, BIO_NOCLOSE);
+ BIO_set_mem_eof_return(bio, 0);
+ X509 *x509 = PEM_read_bio_X509(bio, NULL, NULL,
+ const_cast<char*>("\0"));
+ char *ptr;
+ int remaining_length = BIO_get_mem_data(bio, &ptr);
+ BIO_free(bio);
+ if (pem_length)
+ *pem_length = pem_string.length() - remaining_length;
+ if (x509)
+ return new OpenSSLCertificate(x509);
+ else
+ return NULL;
+}
+
+OpenSSLCertificate::~OpenSSLCertificate() {
+ X509_free(x509_);
+}
+
+std::string OpenSSLCertificate::ToPEMString() const {
+ BIO* bio = BIO_new(BIO_s_mem());
+ if (!bio)
+ return NULL;
+ if (!PEM_write_bio_X509(bio, x509_)) {
+ BIO_free(bio);
+ return NULL;
+ }
+ BIO_write(bio, "\0", 1);
+ char* buffer;
+ BIO_get_mem_data(bio, &buffer);
+ std::string ret(buffer);
+ BIO_free(bio);
+ return ret;
+}
+
+void OpenSSLCertificate::AddReference() {
+ CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509);
+}
+
+OpenSSLIdentity* OpenSSLIdentity::Generate(const std::string& common_name) {
+ OpenSSLKeyPair *key_pair = OpenSSLKeyPair::Generate();
+ if (key_pair) {
+ OpenSSLCertificate *certificate =
+ OpenSSLCertificate::Generate(key_pair, common_name);
+ if (certificate)
+ return new OpenSSLIdentity(key_pair, certificate);
+ delete key_pair;
+ }
+ LOG(LS_INFO) << "Identity generation failed";
+ return NULL;
+}
+
+bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
+ // 1 is the documented success return code.
+ if (SSL_CTX_use_certificate(ctx, certificate_->x509()) != 1 ||
+ SSL_CTX_use_PrivateKey(ctx, key_pair_->pkey()) != 1) {
+ LogSSLErrors("Configuring key and certificate");
+ return false;
+ }
+ return true;
+}
+
+} // talk_base namespace
diff --git a/third_party/libjingle/source/talk/base/opensslidentity.h b/third_party/libjingle/source/talk/base/opensslidentity.h
new file mode 100644
index 0000000..7cac419
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/opensslidentity.h
@@ -0,0 +1,137 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_OPENSSLIDENTITY_H__
+#define TALK_BASE_OPENSSLIDENTITY_H__
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <string>
+
+#include "talk/base/common.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sslidentity.h"
+
+typedef struct ssl_ctx_st SSL_CTX;
+
+namespace talk_base {
+
+// OpenSSLKeyPair encapsulates an OpenSSL EVP_PKEY* keypair object,
+// which is reference counted inside the OpenSSL library.
+class OpenSSLKeyPair {
+ public:
+ static OpenSSLKeyPair* Generate();
+
+ virtual ~OpenSSLKeyPair();
+
+ virtual OpenSSLKeyPair* GetReference() {
+ AddReference();
+ return new OpenSSLKeyPair(pkey_);
+ }
+
+ EVP_PKEY* pkey() const { return pkey_; }
+
+ private:
+ explicit OpenSSLKeyPair(EVP_PKEY* pkey) : pkey_(pkey) {
+ ASSERT(pkey_ != NULL);
+ }
+ void AddReference();
+
+ EVP_PKEY* pkey_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OpenSSLKeyPair);
+};
+
+// OpenSSLCertificate encapsulates an OpenSSL X509* certificate object,
+// which is also reference counted inside the OpenSSL library.
+class OpenSSLCertificate : public SSLCertificate {
+ public:
+ static OpenSSLCertificate* Generate(OpenSSLKeyPair* key_pair,
+ const std::string& common_name);
+ static OpenSSLCertificate* FromPEMString(const std::string& pem_string,
+ int* pem_length);
+
+ virtual ~OpenSSLCertificate();
+
+ virtual OpenSSLCertificate* GetReference() {
+ AddReference();
+ return new OpenSSLCertificate(x509_);
+ }
+
+ X509* x509() const { return x509_; }
+
+ virtual std::string ToPEMString() const;
+
+ private:
+ explicit OpenSSLCertificate(X509* x509) : x509_(x509) {
+ ASSERT(x509_ != NULL);
+ }
+ void AddReference();
+
+ X509* x509_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OpenSSLCertificate);
+};
+
+// Holds a keypair and certificate together, and a method to generate
+// them consistently.
+class OpenSSLIdentity : public SSLIdentity {
+ public:
+ static OpenSSLIdentity* Generate(const std::string& common_name);
+
+ virtual ~OpenSSLIdentity() { }
+
+ virtual OpenSSLCertificate& certificate() const {
+ return *certificate_;
+ }
+
+ virtual OpenSSLIdentity* GetReference() {
+ return new OpenSSLIdentity(key_pair_->GetReference(),
+ certificate_->GetReference());
+ }
+
+ // Configure an SSL context object to use our key and certificate.
+ bool ConfigureIdentity(SSL_CTX* ctx);
+
+ private:
+ OpenSSLIdentity(OpenSSLKeyPair* key_pair,
+ OpenSSLCertificate* certificate)
+ : key_pair_(key_pair), certificate_(certificate) {
+ ASSERT(key_pair != NULL);
+ ASSERT(certificate != NULL);
+ }
+
+ scoped_ptr<OpenSSLKeyPair> key_pair_;
+ scoped_ptr<OpenSSLCertificate> certificate_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OpenSSLIdentity);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_OPENSSLIDENTITY_H__
diff --git a/third_party/libjingle/source/talk/base/opensslstreamadapter.cc b/third_party/libjingle/source/talk/base/opensslstreamadapter.cc
new file mode 100644
index 0000000..6a34e13
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/opensslstreamadapter.cc
@@ -0,0 +1,650 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#if HAVE_OPENSSL_SSL_H
+
+#include "talk/base/opensslstreamadapter.h"
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/stream.h"
+#include "talk/base/openssladapter.h"
+#include "talk/base/opensslidentity.h"
+#include "talk/base/stringutils.h"
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// StreamBIO
+//////////////////////////////////////////////////////////////////////
+
+static int stream_write(BIO* h, const char* buf, int num);
+static int stream_read(BIO* h, char* buf, int size);
+static int stream_puts(BIO* h, const char* str);
+static long stream_ctrl(BIO* h, int cmd, long arg1, void* arg2);
+static int stream_new(BIO* h);
+static int stream_free(BIO* data);
+
+static BIO_METHOD methods_stream = {
+ BIO_TYPE_BIO,
+ "stream",
+ stream_write,
+ stream_read,
+ stream_puts,
+ 0,
+ stream_ctrl,
+ stream_new,
+ stream_free,
+ NULL,
+};
+
+static BIO_METHOD* BIO_s_stream() { return(&methods_stream); }
+
+static BIO* BIO_new_stream(StreamInterface* stream) {
+ BIO* ret = BIO_new(BIO_s_stream());
+ if (ret == NULL)
+ return NULL;
+ ret->ptr = stream;
+ return ret;
+}
+
+// bio methods return 1 (or at least non-zero) on success and 0 on failure.
+
+static int stream_new(BIO* b) {
+ b->shutdown = 0;
+ b->init = 1;
+ b->num = 0; // 1 means end-of-stream
+ b->ptr = 0;
+ return 1;
+}
+
+static int stream_free(BIO* b) {
+ if (b == NULL)
+ return 0;
+ return 1;
+}
+
+static int stream_read(BIO* b, char* out, int outl) {
+ if (!out)
+ return -1;
+ StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
+ BIO_clear_retry_flags(b);
+ size_t read;
+ int error;
+ StreamResult result = stream->Read(out, outl, &read, &error);
+ if (result == SR_SUCCESS) {
+ return read;
+ } else if (result == SR_EOS) {
+ b->num = 1;
+ } else if (result == SR_BLOCK) {
+ BIO_set_retry_read(b);
+ }
+ return -1;
+}
+
+static int stream_write(BIO* b, const char* in, int inl) {
+ if (!in)
+ return -1;
+ StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
+ BIO_clear_retry_flags(b);
+ size_t written;
+ int error;
+ StreamResult result = stream->Write(in, inl, &written, &error);
+ if (result == SR_SUCCESS) {
+ return written;
+ } else if (result == SR_BLOCK) {
+ BIO_set_retry_write(b);
+ }
+ return -1;
+}
+
+static int stream_puts(BIO* b, const char* str) {
+ return stream_write(b, str, strlen(str));
+}
+
+static long stream_ctrl(BIO* b, int cmd, long num, void* ptr) {
+ UNUSED(num);
+ UNUSED(ptr);
+
+ switch (cmd) {
+ case BIO_CTRL_RESET:
+ return 0;
+ case BIO_CTRL_EOF:
+ return b->num;
+ case BIO_CTRL_WPENDING:
+ case BIO_CTRL_PENDING:
+ return 0;
+ case BIO_CTRL_FLUSH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// OpenSSLStreamAdapter
+/////////////////////////////////////////////////////////////////////////////
+
+OpenSSLStreamAdapter::OpenSSLStreamAdapter(StreamInterface* stream)
+ : SSLStreamAdapter(stream),
+ state_(SSL_NONE),
+ role_(SSL_CLIENT),
+ ssl_read_needs_write_(false), ssl_write_needs_read_(false),
+ ssl_(NULL), ssl_ctx_(NULL),
+ custom_verification_succeeded_(false) {
+}
+
+OpenSSLStreamAdapter::~OpenSSLStreamAdapter() {
+ Cleanup();
+}
+
+void OpenSSLStreamAdapter::SetIdentity(SSLIdentity* identity) {
+ ASSERT(identity_.get() == NULL);
+ identity_.reset(static_cast<OpenSSLIdentity*>(identity));
+}
+
+void OpenSSLStreamAdapter::SetServerRole() {
+ role_ = SSL_SERVER;
+}
+
+void OpenSSLStreamAdapter::SetPeerCertificate(SSLCertificate* cert) {
+ ASSERT(peer_certificate_.get() == NULL);
+ ASSERT(ssl_server_name_.empty());
+ peer_certificate_.reset(static_cast<OpenSSLCertificate*>(cert));
+}
+
+int OpenSSLStreamAdapter::StartSSLWithServer(const char* server_name) {
+ ASSERT(server_name != NULL && server_name[0] != '\0');
+ ssl_server_name_ = server_name;
+ return StartSSL();
+}
+
+int OpenSSLStreamAdapter::StartSSLWithPeer() {
+ ASSERT(ssl_server_name_.empty());
+ // It is permitted to specify peer_certificate_ only later.
+ return StartSSL();
+}
+
+//
+// StreamInterface Implementation
+//
+
+StreamResult OpenSSLStreamAdapter::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ LOG(LS_INFO) << "OpenSSLStreamAdapter::Write(" << data_len << ")";
+
+ switch (state_) {
+ case SSL_NONE:
+ // pass-through in clear text
+ return StreamAdapterInterface::Write(data, data_len, written, error);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ return SR_BLOCK;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ case SSL_CLOSED:
+ default:
+ if (error)
+ *error = ssl_error_code_;
+ return SR_ERROR;
+ }
+
+ // OpenSSL will return an error if we try to write zero bytes
+ if (data_len == 0) {
+ if (written)
+ *written = 0;
+ return SR_SUCCESS;
+ }
+
+ ssl_write_needs_read_ = false;
+
+ int code = SSL_write(ssl_, data, data_len);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ LOG(LS_INFO) << " -- success";
+ ASSERT(0 < code && static_cast<unsigned>(code) <= data_len);
+ if (written)
+ *written = code;
+ return SR_SUCCESS;
+ case SSL_ERROR_WANT_READ:
+ LOG(LS_INFO) << " -- error want read";
+ ssl_write_needs_read_ = true;
+ return SR_BLOCK;
+ case SSL_ERROR_WANT_WRITE:
+ LOG(LS_INFO) << " -- error want write";
+ return SR_BLOCK;
+
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ Error("SSL_write", (code ? code : -1), false);
+ if (error)
+ *error = ssl_error_code_;
+ return SR_ERROR;
+ }
+ // not reached
+}
+
+StreamResult OpenSSLStreamAdapter::Read(void* data, size_t data_len,
+ size_t* read, int* error) {
+ LOG(LS_INFO) << "OpenSSLStreamAdapter::Read(" << data_len << ")";
+ switch (state_) {
+ case SSL_NONE:
+ // pass-through in clear text
+ return StreamAdapterInterface::Read(data, data_len, read, error);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ return SR_BLOCK;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_CLOSED:
+ return SR_EOS;
+
+ case SSL_ERROR:
+ default:
+ if (error)
+ *error = ssl_error_code_;
+ return SR_ERROR;
+ }
+
+ // Don't trust OpenSSL with zero byte reads
+ if (data_len == 0) {
+ if (read)
+ *read = 0;
+ return SR_SUCCESS;
+ }
+
+ ssl_read_needs_write_ = false;
+
+ int code = SSL_read(ssl_, data, data_len);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ LOG(LS_INFO) << " -- success";
+ ASSERT(0 < code && static_cast<unsigned>(code) <= data_len);
+ if (read)
+ *read = code;
+ return SR_SUCCESS;
+ case SSL_ERROR_WANT_READ:
+ LOG(LS_INFO) << " -- error want read";
+ return SR_BLOCK;
+ case SSL_ERROR_WANT_WRITE:
+ LOG(LS_INFO) << " -- error want write";
+ ssl_read_needs_write_ = true;
+ return SR_BLOCK;
+ case SSL_ERROR_ZERO_RETURN:
+ LOG(LS_INFO) << " -- remote side closed";
+ return SR_EOS;
+ break;
+ default:
+ LOG(LS_INFO) << " -- error " << code;
+ Error("SSL_read", (code ? code : -1), false);
+ if (error)
+ *error = ssl_error_code_;
+ return SR_ERROR;
+ }
+ // not reached
+}
+
+void OpenSSLStreamAdapter::Close() {
+ Cleanup();
+ ASSERT(state_ == SSL_CLOSED || state_ == SSL_ERROR);
+ StreamAdapterInterface::Close();
+}
+
+StreamState OpenSSLStreamAdapter::GetState() const {
+ switch(state_) {
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ return SS_OPENING;
+ case SSL_CONNECTED:
+ return SS_OPEN;
+ default:
+ return SS_CLOSED;
+ };
+ // not reached
+}
+
+void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events,
+ int err) {
+ int events_to_signal = 0;
+ int signal_error = 0;
+ ASSERT(stream == this->stream());
+ if ((events & SE_OPEN)) {
+ LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent SE_OPEN";
+ if (state_ != SSL_WAIT) {
+ ASSERT(state_ == SSL_NONE);
+ events_to_signal |= SE_OPEN;
+ } else {
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ Error("BeginSSL", err, true);
+ return;
+ }
+ }
+ }
+ if ((events & (SE_READ|SE_WRITE))) {
+ LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent"
+ << ((events & SE_READ) ? " SE_READ" : "")
+ << ((events & SE_WRITE) ? " SE_WRITE" : "");
+ if (state_ == SSL_NONE) {
+ events_to_signal |= events & (SE_READ|SE_WRITE);
+ } else if (state_ == SSL_CONNECTING) {
+ if (int err = ContinueSSL()) {
+ Error("ContinueSSL", err, true);
+ return;
+ }
+ } else if (state_ == SSL_CONNECTED) {
+ if (((events & SE_READ) && ssl_write_needs_read_) ||
+ (events & SE_WRITE)) {
+ LOG(LS_INFO) << " -- onStreamWriteable";
+ events_to_signal |= SE_WRITE;
+ }
+ if (((events & SE_WRITE) && ssl_read_needs_write_) ||
+ (events & SE_READ)) {
+ LOG(LS_INFO) << " -- onStreamReadable";
+ events_to_signal |= SE_READ;
+ }
+ }
+ }
+ if ((events & SE_CLOSE)) {
+ LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent(SE_CLOSE, " << err << ")";
+ Cleanup();
+ events_to_signal |= SE_CLOSE;
+ // SE_CLOSE is the only event that uses the final parameter to OnEvent().
+ ASSERT(signal_error == 0);
+ signal_error = err;
+ }
+ if(events_to_signal)
+ StreamAdapterInterface::OnEvent(stream, events_to_signal, signal_error);
+}
+
+int OpenSSLStreamAdapter::StartSSL() {
+ ASSERT(state_ == SSL_NONE);
+
+ if (StreamAdapterInterface::GetState() != SS_OPEN) {
+ state_ = SSL_WAIT;
+ return 0;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ Error("BeginSSL", err, false);
+ return err;
+ }
+
+ return 0;
+}
+
+int OpenSSLStreamAdapter::BeginSSL() {
+ ASSERT(state_ == SSL_CONNECTING);
+ // The underlying stream has open. If we are in peer-to-peer mode
+ // then a peer certificate must have been specified by now.
+ ASSERT(!ssl_server_name_.empty() || peer_certificate_.get() != NULL);
+ LOG(LS_INFO) << "BeginSSL: "
+ << (!ssl_server_name_.empty() ? ssl_server_name_ :
+ "with peer");
+
+ BIO* bio = NULL;
+
+ // First set up the context
+ ASSERT(ssl_ctx_ == NULL);
+ ssl_ctx_ = SetupSSLContext();
+ if (!ssl_ctx_)
+ return -1;
+
+ bio = BIO_new_stream(static_cast<StreamInterface*>(stream()));
+ if (!bio)
+ return -1;
+
+ ssl_ = SSL_new(ssl_ctx_);
+ if (!ssl_) {
+ BIO_free(bio);
+ return -1;
+ }
+
+ SSL_set_app_data(ssl_, this);
+
+ SSL_set_bio(ssl_, bio, bio); // the SSL object owns the bio now.
+
+ SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE |
+ SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ // Do the connect
+ return ContinueSSL();
+}
+
+int OpenSSLStreamAdapter::ContinueSSL() {
+ LOG(LS_INFO) << "ContinueSSL";
+ ASSERT(state_ == SSL_CONNECTING);
+
+ int code = (role_ == SSL_CLIENT) ? SSL_connect(ssl_) : SSL_accept(ssl_);
+ switch (SSL_get_error(ssl_, code)) {
+ case SSL_ERROR_NONE:
+ LOG(LS_INFO) << " -- success";
+
+ if (!SSLPostConnectionCheck(ssl_, ssl_server_name_.c_str(),
+ peer_certificate_.get() != NULL
+ ? peer_certificate_->x509() : NULL)) {
+ LOG(LS_ERROR) << "TLS post connection check failed";
+ return -1;
+ }
+
+ state_ = SSL_CONNECTED;
+ StreamAdapterInterface::OnEvent(stream(), SE_OPEN|SE_READ|SE_WRITE, 0);
+ break;
+
+ case SSL_ERROR_WANT_READ:
+ LOG(LS_INFO) << " -- error want read";
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ LOG(LS_INFO) << " -- error want write";
+ break;
+
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ LOG(LS_INFO) << " -- error " << code;
+ return (code != 0) ? code : -1;
+ }
+
+ return 0;
+}
+
+void OpenSSLStreamAdapter::Error(const char* context, int err, bool signal) {
+ LOG(LS_WARNING) << "OpenSSLStreamAdapter::Error("
+ << context << ", " << err << ")";
+ state_ = SSL_ERROR;
+ ssl_error_code_ = err;
+ Cleanup();
+ if (signal)
+ StreamAdapterInterface::OnEvent(stream(), SE_CLOSE, err);
+}
+
+void OpenSSLStreamAdapter::Cleanup() {
+ LOG(LS_INFO) << "Cleanup";
+
+ if (state_ != SSL_ERROR) {
+ state_ = SSL_CLOSED;
+ ssl_error_code_ = 0;
+ }
+
+ if (ssl_) {
+ SSL_free(ssl_);
+ ssl_ = NULL;
+ }
+ if (ssl_ctx_) {
+ SSL_CTX_free(ssl_ctx_);
+ ssl_ctx_ = NULL;
+ }
+ identity_.reset();
+ peer_certificate_.reset();
+}
+
+SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
+ SSL_CTX* ctx = SSL_CTX_new(role_ == SSL_CLIENT ? TLSv1_client_method()
+ : TLSv1_server_method());
+ if (ctx == NULL)
+ return NULL;
+
+ if (identity_.get() && !identity_->ConfigureIdentity(ctx)) {
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+
+ if (peer_certificate_.get() == NULL) { // traditional mode
+ // Add the root cert to the SSL context
+ if(!OpenSSLAdapter::ConfigureTrustedRootCertificates(ctx)) {
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+ }
+
+ if (peer_certificate_.get() != NULL && role_ == SSL_SERVER)
+ // we must specify which client cert to ask for
+ SSL_CTX_add_client_CA(ctx, peer_certificate_->x509());
+
+#ifdef _DEBUG
+ SSL_CTX_set_info_callback(ctx, OpenSSLAdapter::SSLInfoCallback);
+#endif
+
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ SSLVerifyCallback);
+ SSL_CTX_set_verify_depth(ctx, 4);
+ SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+
+ return ctx;
+}
+
+int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
+#if _DEBUG
+ if (!ok) {
+ char data[256];
+ X509* cert = X509_STORE_CTX_get_current_cert(store);
+ int depth = X509_STORE_CTX_get_error_depth(store);
+ int err = X509_STORE_CTX_get_error(store);
+
+ LOG(LS_INFO) << "Error with certificate at depth: " << depth;
+ X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
+ LOG(LS_INFO) << " issuer = " << data;
+ X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
+ LOG(LS_INFO) << " subject = " << data;
+ LOG(LS_INFO) << " err = " << err
+ << ":" << X509_verify_cert_error_string(err);
+ }
+#endif
+
+ // Get our SSL structure from the store
+ SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
+ store,
+ SSL_get_ex_data_X509_STORE_CTX_idx()));
+
+ OpenSSLStreamAdapter* stream =
+ reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
+
+ // In peer-to-peer mode, no root cert / certificate authority was
+ // specified, so the libraries knows of no certificate to accept,
+ // and therefore it will necessarily call here on the first cert it
+ // tries to verify.
+ if (!ok && stream->peer_certificate_.get() != NULL) {
+ X509* cert = X509_STORE_CTX_get_current_cert(store);
+ int err = X509_STORE_CTX_get_error(store);
+ // peer-to-peer mode: allow the certificate to be self-signed,
+ // assuming it matches the cert that was specified.
+ if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT &&
+ X509_cmp(cert, stream->peer_certificate_->x509()) == 0) {
+ LOG(LS_INFO) << "Accepted self-signed peer certificate authority";
+ ok = 1;
+ }
+ } else if (!ok && OpenSSLAdapter::custom_verify_callback_) {
+ // this applies only in traditional mode
+ void* cert =
+ reinterpret_cast<void*>(X509_STORE_CTX_get_current_cert(store));
+ if (OpenSSLAdapter::custom_verify_callback_(cert)) {
+ stream->custom_verification_succeeded_ = true;
+ LOG(LS_INFO) << "validated certificate using custom callback";
+ ok = 1;
+ }
+ }
+
+ if (!ok && stream->ignore_bad_cert()) {
+ LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
+ ok = 1;
+ }
+
+ return ok;
+}
+
+// This code is taken from the "Network Security with OpenSSL"
+// sample in chapter 5
+bool OpenSSLStreamAdapter::SSLPostConnectionCheck(SSL* ssl,
+ const char* server_name,
+ const X509* peer_cert) {
+ ASSERT(server_name != NULL);
+ bool ok;
+ if(server_name[0] != '\0') { // traditional mode
+ ok = OpenSSLAdapter::VerifyServerName(ssl, server_name, ignore_bad_cert());
+
+ if (ok) {
+ ok = (SSL_get_verify_result(ssl) == X509_V_OK ||
+ custom_verification_succeeded_);
+ }
+ } else { // peer-to-peer mode
+ ASSERT(peer_cert != NULL);
+ // no server name validation
+ ok = true;
+ }
+
+ if (!ok && ignore_bad_cert()) {
+ LOG(LS_ERROR) << "SSL_get_verify_result(ssl) = "
+ << SSL_get_verify_result(ssl);
+ LOG(LS_INFO) << "Other TLS post connection checks failed.";
+ ok = true;
+ }
+
+ return ok;
+}
+
+} // namespace talk_base
+
+#endif // HAVE_OPENSSL_SSL_H
diff --git a/third_party/libjingle/source/talk/base/opensslstreamadapter.h b/third_party/libjingle/source/talk/base/opensslstreamadapter.h
new file mode 100644
index 0000000..16ec751
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/opensslstreamadapter.h
@@ -0,0 +1,171 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_OPENSSLSTREAMADAPTER_H__
+#define TALK_BASE_OPENSSLSTREAMADAPTER_H__
+
+#include <string>
+#include "talk/base/sslstreamadapter.h"
+#include "talk/base/opensslidentity.h"
+
+typedef struct ssl_st SSL;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+
+namespace talk_base {
+
+// This class was written with OpenSSLAdapter (a socket adapter) as a
+// starting point. It has similar structure and functionality, with
+// the peer-to-peer mode added.
+//
+// Static methods to initialize and deinit the SSL library are in
+// OpenSSLAdapter. This class also uses
+// OpenSSLAdapter::custom_verify_callback_ (a static field). These
+// should probably be moved out to a neutral class.
+//
+// In a few cases I have factored out some OpenSSLAdapter code into
+// static methods so it can be reused from this class. Eventually that
+// code should probably be moved to a common support
+// class. Unfortunately there remain a few duplicated sections of
+// code. I have not done more restructuring because I did not want to
+// affect existing code that uses OpenSSLAdapter.
+//
+// This class does not support the SSL connection restart feature
+// present in OpenSSLAdapter. I am not entirely sure how the feature
+// is useful and I am not convinced that it works properly.
+//
+// This implementation is careful to disallow data exchange after an
+// SSL error, and it has an explicit SSL_CLOSED state. It should not
+// be possible to send any data in clear after one of the StartSSL
+// methods has been called.
+
+// Look in sslstreamadapter.h for documentation of the methods.
+
+class OpenSSLIdentity;
+
+///////////////////////////////////////////////////////////////////////////////
+
+class OpenSSLStreamAdapter : public SSLStreamAdapter {
+ public:
+ explicit OpenSSLStreamAdapter(StreamInterface* stream);
+ virtual ~OpenSSLStreamAdapter();
+
+ virtual void SetIdentity(SSLIdentity* identity);
+ virtual void SetServerRole();
+ virtual void SetPeerCertificate(SSLCertificate* cert);
+
+ virtual int StartSSLWithServer(const char* server_name);
+ virtual int StartSSLWithPeer();
+
+ virtual StreamResult Read(void* data, size_t data_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+ virtual StreamState GetState() const;
+
+ protected:
+ virtual void OnEvent(StreamInterface* stream, int events, int err);
+
+ private:
+ enum SSLState {
+ // Before calling one of the StartSSL methods, data flows
+ // in clear text.
+ SSL_NONE,
+ SSL_WAIT, // waiting for the stream to open to start SSL negotiation
+ SSL_CONNECTING, // SSL negotiation in progress
+ SSL_CONNECTED, // SSL stream successfully established
+ SSL_ERROR, // some SSL error occurred, stream is closed
+ SSL_CLOSED // Clean close
+ };
+ enum SSLRole {
+ SSL_CLIENT, SSL_SERVER
+ };
+
+ // The following three methods return 0 on success and a negative
+ // error code on failure. The error code may be from OpenSSL or -1
+ // on some other error cases, so it can't really be interpreted
+ // unfortunately.
+
+ // Go from state SSL_NONE to either SSL_CONNECTING or SSL_WAIT,
+ // depending on whether the underlying stream is already open or
+ // not.
+ int StartSSL();
+ // Prepare SSL library, state is SSL_CONNECTING.
+ int BeginSSL();
+ // Perform SSL negotiation steps.
+ int ContinueSSL();
+
+ // Error handler helper. signal is given as true for errors in
+ // asynchronous contexts (when an error method was not returned
+ // through some other method), and in that case an SE_CLOSE event is
+ // raised on the stream with the specified error.
+ // A 0 error means a graceful close, otherwise there is not really enough
+ // context to interpret the error code.
+ void Error(const char* context, int err, bool signal);
+ void Cleanup();
+
+ // SSL library configuration
+ SSL_CTX* SetupSSLContext();
+ // SSL verification check
+ bool SSLPostConnectionCheck(SSL* ssl, const char* server_name,
+ const X509* peer_cert);
+ // SSL certification verification error handler, called back from
+ // the openssl library. Returns an int interpreted as a boolean in
+ // the C style: zero means verification failure, non-zero means
+ // passed.
+ static int SSLVerifyCallback(int ok, X509_STORE_CTX* store);
+
+
+ SSLState state_;
+ SSLRole role_;
+ int ssl_error_code_; // valid when state_ == SSL_ERROR or SSL_CLOSED
+ // Whether the SSL negotiation is blocked on needing to read or
+ // write to the wrapped stream.
+ bool ssl_read_needs_write_;
+ bool ssl_write_needs_read_;
+
+ SSL* ssl_;
+ SSL_CTX* ssl_ctx_;
+ // in traditional mode, the server name that the server's certificate
+ // must specify. Empty in peer-to-peer mode.
+ // Our key and certificate, mostly useful in peer-to-peer mode.
+ scoped_ptr<OpenSSLIdentity> identity_;
+ std::string ssl_server_name_;
+ // In peer-to-peer mode, the certificate that the peer must
+ // present. Empty in traditional mode.
+ scoped_ptr<OpenSSLCertificate> peer_certificate_;
+
+ // OpenSSLAdapter::custom_verify_callback_ result
+ bool custom_verification_succeeded_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_OPENSSLSTREAMADAPTER_H__
diff --git a/third_party/libjingle/source/talk/base/pathutils.cc b/third_party/libjingle/source/talk/base/pathutils.cc
new file mode 100644
index 0000000..d56373b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/pathutils.cc
@@ -0,0 +1,268 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 WIN32
+#include "talk/base/win32.h"
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+#endif // WIN32
+
+#include "talk/base/common.h"
+#include "talk/base/fileutils.h"
+#include "talk/base/logging.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/urlencode.h"
+
+namespace talk_base {
+
+std::string const EMPTY_STR = "";
+
+// EXT_DELIM separates a file basename from extension
+const char EXT_DELIM = '.';
+
+// FOLDER_DELIMS separate folder segments and the filename
+const char* const FOLDER_DELIMS = "/\\";
+
+// DEFAULT_FOLDER_DELIM is the preferred delimiter for this platform
+#if WIN32
+const char DEFAULT_FOLDER_DELIM = '\\';
+#else // !WIN32
+const char DEFAULT_FOLDER_DELIM = '/';
+#endif // !WIN32
+
+///////////////////////////////////////////////////////////////////////////////
+// Pathname - parsing of pathnames into components, and vice versa
+///////////////////////////////////////////////////////////////////////////////
+
+bool Pathname::IsFolderDelimiter(char ch) {
+ return (NULL != ::strchr(FOLDER_DELIMS, ch));
+}
+
+char Pathname::DefaultFolderDelimiter() {
+ return DEFAULT_FOLDER_DELIM;
+}
+
+Pathname::Pathname()
+ : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
+}
+
+Pathname::Pathname(const std::string& pathname)
+ : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
+ SetPathname(pathname);
+}
+
+Pathname::Pathname(const std::string& folder, const std::string& filename)
+ : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
+ SetPathname(folder, filename);
+}
+
+void Pathname::SetFolderDelimiter(char delimiter) {
+ ASSERT(IsFolderDelimiter(delimiter));
+ folder_delimiter_ = delimiter;
+}
+
+void Pathname::Normalize() {
+ for (size_t i=0; i<folder_.length(); ++i) {
+ if (IsFolderDelimiter(folder_[i])) {
+ folder_[i] = folder_delimiter_;
+ }
+ }
+}
+
+void Pathname::clear() {
+ folder_.clear();
+ basename_.clear();
+ extension_.clear();
+}
+
+bool Pathname::empty() const {
+ return folder_.empty() && basename_.empty() && extension_.empty();
+}
+
+std::string Pathname::pathname() const {
+ std::string pathname(folder_);
+ pathname.append(basename_);
+ pathname.append(extension_);
+ if (pathname.empty()) {
+ // Instead of the empty pathname, return the current working directory.
+ pathname.push_back('.');
+ pathname.push_back(folder_delimiter_);
+ }
+ return pathname;
+}
+
+std::string Pathname::url() const {
+ std::string s = "file:///";
+ for (size_t i=0; i<folder_.length(); ++i) {
+ if (IsFolderDelimiter(folder_[i]))
+ s += '/';
+ else
+ s += folder_[i];
+ }
+ s += basename_;
+ s += extension_;
+ return UrlEncodeStringForOnlyUnsafeChars(s);
+}
+
+void Pathname::SetPathname(const std::string& pathname) {
+ std::string::size_type pos = pathname.find_last_of(FOLDER_DELIMS);
+ if (pos != std::string::npos) {
+ SetFolder(pathname.substr(0, pos + 1));
+ SetFilename(pathname.substr(pos + 1));
+ } else {
+ SetFolder(EMPTY_STR);
+ SetFilename(pathname);
+ }
+}
+
+void Pathname::SetPathname(const std::string& folder,
+ const std::string& filename) {
+ SetFolder(folder);
+ SetFilename(filename);
+}
+
+void Pathname::AppendPathname(const std::string& pathname) {
+ std::string full_pathname(folder_);
+ full_pathname.append(pathname);
+ SetPathname(full_pathname);
+}
+
+std::string Pathname::folder() const {
+ return folder_;
+}
+
+std::string Pathname::folder_name() const {
+ std::string::size_type pos = std::string::npos;
+ if (folder_.size() >= 2) {
+ pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2);
+ }
+ if (pos != std::string::npos) {
+ return folder_.substr(pos + 1);
+ } else {
+ return folder_;
+ }
+}
+
+std::string Pathname::parent_folder() const {
+ std::string::size_type pos = std::string::npos;
+ if (folder_.size() >= 2) {
+ pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2);
+ }
+ if (pos != std::string::npos) {
+ return folder_.substr(0, pos + 1);
+ } else {
+ return EMPTY_STR;
+ }
+}
+
+void Pathname::SetFolder(const std::string& folder) {
+ folder_.assign(folder);
+ // Ensure folder ends in a path delimiter
+ if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) {
+ folder_.push_back(folder_delimiter_);
+ }
+}
+
+void Pathname::AppendFolder(const std::string& folder) {
+ folder_.append(folder);
+ // Ensure folder ends in a path delimiter
+ if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) {
+ folder_.push_back(folder_delimiter_);
+ }
+}
+
+std::string Pathname::basename() const {
+ return basename_;
+}
+
+bool Pathname::SetBasename(const std::string& basename) {
+ if(basename.find_first_of(FOLDER_DELIMS) != std::string::npos) {
+ return false;
+ }
+ basename_.assign(basename);
+ return true;
+}
+
+std::string Pathname::extension() const {
+ return extension_;
+}
+
+bool Pathname::SetExtension(const std::string& extension) {
+ if (extension.find_first_of(FOLDER_DELIMS) != std::string::npos ||
+ extension.find_first_of(EXT_DELIM, 1) != std::string::npos) {
+ return false;
+ }
+ extension_.assign(extension);
+ // Ensure extension begins with the extension delimiter
+ if (!extension_.empty() && (extension_[0] != EXT_DELIM)) {
+ extension_.insert(extension_.begin(), EXT_DELIM);
+ }
+ return true;
+}
+
+std::string Pathname::filename() const {
+ std::string filename(basename_);
+ filename.append(extension_);
+ return filename;
+}
+
+bool Pathname::SetFilename(const std::string& filename) {
+ std::string::size_type pos = filename.rfind(EXT_DELIM);
+ if ((pos == std::string::npos) || (pos == 0)) {
+ return SetExtension(EMPTY_STR) && SetBasename(filename);
+ } else {
+ return SetExtension(filename.substr(pos)) && SetBasename(filename.substr(0, pos));
+ }
+}
+
+#ifdef WIN32
+bool Pathname::GetDrive(char *drive, uint32 bytes) const {
+ return GetDrive(drive, bytes, folder_);
+}
+
+// static
+bool Pathname::GetDrive(char *drive, uint32 bytes,
+ const std::string& pathname) {
+ // need at lease 4 bytes to save c:
+ if (bytes < 4 || pathname.size() < 3) {
+ return false;
+ }
+
+ memcpy(drive, pathname.c_str(), 3);
+ drive[3] = 0;
+ // sanity checking
+ return (isalpha(drive[0]) &&
+ drive[1] == ':' &&
+ drive[2] == '\\');
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/pathutils.h b/third_party/libjingle/source/talk/base/pathutils.h
new file mode 100644
index 0000000..ab2aacd
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/pathutils.h
@@ -0,0 +1,180 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_PATHUTILS_H__
+#define TALK_BASE_PATHUTILS_H__
+
+#include <string>
+// Temporary, until deprecated helpers are removed.
+#include "talk/base/fileutils.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Pathname - parsing of pathnames into components, and vice versa.
+//
+// To establish consistent terminology, a filename never contains a folder
+// component. A folder never contains a filename. A pathname may include
+// a folder and/or filename component. Here are some examples:
+//
+// pathname() /home/john/example.txt
+// folder() /home/john/
+// filename() example.txt
+// parent_folder() /home/
+// folder_name() john/
+// basename() example
+// extension() .txt
+//
+// Basename may begin, end, and/or include periods, but no folder delimiters.
+// If extension exists, it consists of a period followed by zero or more
+// non-period/non-delimiter characters, and basename is non-empty.
+///////////////////////////////////////////////////////////////////////////////
+
+class Pathname {
+public:
+ // Folder delimiters are slash and backslash
+ static bool IsFolderDelimiter(char ch);
+ static char DefaultFolderDelimiter();
+
+ Pathname();
+ Pathname(const std::string& pathname);
+ Pathname(const std::string& folder, const std::string& filename);
+
+ // Set's the default folder delimiter for this Pathname
+ char folder_delimiter() const { return folder_delimiter_; }
+ void SetFolderDelimiter(char delimiter);
+
+ // Normalize changes all folder delimiters to folder_delimiter()
+ void Normalize();
+
+ // Reset to the empty pathname
+ void clear();
+
+ // Returns true if the pathname is empty. Note: this->pathname().empty()
+ // is always false.
+ bool empty() const;
+
+ std::string url() const;
+
+ // Returns the folder and filename components. If the pathname is empty,
+ // returns a string representing the current directory (as a relative path,
+ // i.e., ".").
+ std::string pathname() const;
+ void SetPathname(const std::string& pathname);
+ void SetPathname(const std::string& folder, const std::string& filename);
+
+ // Append pathname to the current folder (if any). Any existing filename
+ // will be discarded.
+ void AppendPathname(const std::string& pathname);
+
+ std::string folder() const;
+ std::string folder_name() const;
+ std::string parent_folder() const;
+ // SetFolder and AppendFolder will append a folder delimiter, if needed.
+ void SetFolder(const std::string& folder);
+ void AppendFolder(const std::string& folder);
+
+ std::string basename() const;
+ bool SetBasename(const std::string& basename);
+
+ std::string extension() const;
+ // SetExtension will prefix a period, if needed.
+ bool SetExtension(const std::string& extension);
+
+ std::string filename() const;
+ bool SetFilename(const std::string& filename);
+
+#ifdef WIN32
+ bool GetDrive(char *drive, uint32 bytes) const;
+ static bool GetDrive(char *drive, uint32 bytes,const std::string& pathname);
+#endif
+
+private:
+ std::string folder_, basename_, extension_;
+ char folder_delimiter_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Global Helpers (deprecated)
+///////////////////////////////////////////////////////////////////////////////
+
+inline void SetOrganizationName(const std::string& organization) {
+ Filesystem::SetOrganizationName(organization);
+}
+inline void SetApplicationName(const std::string& application) {
+ Filesystem::SetApplicationName(application);
+}
+inline void GetOrganizationName(std::string* organization) {
+ Filesystem::GetOrganizationName(organization);
+}
+inline void GetApplicationName(std::string* application) {
+ Filesystem::GetApplicationName(application);
+}
+inline bool CreateFolder(const Pathname& path) {
+ return Filesystem::CreateFolder(path);
+}
+inline bool FinishPath(Pathname& path, bool create, const std::string& append) {
+ if (!append.empty())
+ path.AppendFolder(append);
+ return !create || CreateFolder(path);
+}
+// Note: this method uses the convention of <temp>/<appname> for the temporary
+// folder. Filesystem uses <temp>/<exename>. We will be migrating exclusively
+// to <temp>/<orgname>/<appname> eventually. Since these are temp folders,
+// it's probably ok to orphan them during the transition.
+inline bool GetTemporaryFolder(Pathname& path, bool create,
+ const std::string& append) {
+ std::string application_name;
+ Filesystem::GetApplicationName(&application_name);
+ ASSERT(!application_name.empty());
+ return Filesystem::GetTemporaryFolder(path, create, &application_name)
+ && FinishPath(path, create, append);
+}
+inline bool GetAppDataFolder(Pathname& path, bool create,
+ const std::string& append) {
+ ASSERT(!create); // TODO: Support create flag on Filesystem::GetAppDataFolder.
+ return Filesystem::GetAppDataFolder(&path, true)
+ && FinishPath(path, create, append);
+}
+inline bool CleanupTemporaryFolder() {
+ Pathname path;
+ if (!GetTemporaryFolder(path, false, ""))
+ return false;
+ if (Filesystem::IsAbsent(path))
+ return true;
+ if (!Filesystem::IsTemporaryPath(path)) {
+ ASSERT(false);
+ return false;
+ }
+ return Filesystem::DeleteFolderContents(path);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_PATHUTILS_H__
diff --git a/third_party/libjingle/source/talk/base/physicalsocketserver.cc b/third_party/libjingle/source/talk/base/physicalsocketserver.cc
new file mode 100644
index 0000000..f5724f2
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/physicalsocketserver.cc
@@ -0,0 +1,1606 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <signal.h>
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#undef SetPort
+#endif
+
+#include <algorithm>
+#include <map>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/nethelpers.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/time.h"
+#include "talk/base/winping.h"
+#include "talk/base/win32socketinit.h"
+
+// stm: this will tell us if we are on OSX
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef POSIX
+#include <netinet/tcp.h> // for TCP_NODELAY
+#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h
+typedef void* SockOptArg;
+#endif // POSIX
+
+#ifdef WIN32
+typedef char* SockOptArg;
+#endif
+
+namespace talk_base {
+
+const int kfRead = 0x0001;
+const int kfWrite = 0x0002;
+const int kfConnect = 0x0004;
+const int kfClose = 0x0008;
+const int kfAccept = 0x0010;
+
+// Standard MTUs, from RFC 1191
+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 sigslot::has_slots<> {
+ 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),
+ resolver_(NULL) {
+#ifdef WIN32
+ // EnsureWinsockInit() ensures that winsock is initialized. The default
+ // version of this function doesn't do anything because winsock is
+ // initialized by constructor of a static object. If neccessary libjingle
+ // users can link it with a different version of this function by replacing
+ // win32socketinit.cc. See win32socketinit.cc for more details.
+ EnsureWinsockInit();
+#endif
+ if (s_ != INVALID_SOCKET) {
+ enabled_events_ = kfRead | kfWrite;
+
+ int type = SOCK_STREAM;
+ socklen_t len = sizeof(type);
+ VERIFY(0 == getsockopt(s_, SOL_SOCKET, SO_TYPE, (SockOptArg)&type, &len));
+ udp_ = (SOCK_DGRAM == type);
+ }
+ }
+
+ 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);
+ udp_ = (SOCK_DGRAM == type);
+ UpdateLastError();
+ if (udp_)
+ enabled_events_ = kfRead | kfWrite;
+ return s_ != INVALID_SOCKET;
+ }
+
+ SocketAddress GetLocalAddress() const {
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getsockname(s_, (sockaddr*)&addr, &addrlen);
+ SocketAddress address;
+ if (result >= 0) {
+ ASSERT(addrlen == sizeof(addr));
+ address.FromSockAddr(addr);
+ } else {
+ LOG(LS_WARNING) << "GetLocalAddress: unable to get local addr, socket="
+ << s_;
+ }
+ return address;
+ }
+
+ SocketAddress GetRemoteAddress() const {
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getpeername(s_, (sockaddr*)&addr, &addrlen);
+ SocketAddress address;
+ if (result >= 0) {
+ ASSERT(addrlen == sizeof(addr));
+ address.FromSockAddr(addr);
+ } else {
+ LOG(LS_WARNING) << "GetRemoteAddress: unable to get remote addr, socket="
+ << s_;
+ }
+ return address;
+ }
+
+ int Bind(const SocketAddress& addr) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int err = ::bind(s_, (sockaddr*)&saddr, sizeof(saddr));
+ UpdateLastError();
+#ifdef _DEBUG
+ if (0 == err) {
+ dbg_addr_ = "Bound @ ";
+ dbg_addr_.append(GetLocalAddress().ToString());
+ }
+#endif // _DEBUG
+ 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;
+ if (addr.IsUnresolved()) {
+ if (state_ != CS_CLOSED) {
+ SetError(EALREADY);
+ return SOCKET_ERROR;
+ }
+
+ LOG(LS_VERBOSE) << "Resolving addr in PhysicalSocket::Connect";
+ resolver_ = new AsyncResolver();
+ resolver_->set_address(addr);
+ resolver_->SignalWorkDone.connect(this, &PhysicalSocket::OnResolveResult);
+ resolver_->Start();
+ state_ = CS_CONNECTING;
+ return 0;
+ }
+
+ return DoConnect(addr);
+ }
+
+ int DoConnect(const SocketAddress& addr) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int err = ::connect(s_, (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;
+ } else {
+ return SOCKET_ERROR;
+ }
+
+ enabled_events_ |= kfRead | kfWrite;
+ return 0;
+ }
+
+ int GetError() const {
+ return error_;
+ }
+
+ void SetError(int error) {
+ error_ = error;
+ }
+
+ ConnState GetState() const {
+ return state_;
+ }
+
+ int GetOption(Option opt, int* value) {
+ int slevel;
+ int sopt;
+ if (TranslateOption(opt, &slevel, &sopt) == -1)
+ return -1;
+ socklen_t optlen = sizeof(*value);
+ int ret = ::getsockopt(s_, slevel, sopt, (SockOptArg)value, &optlen);
+ if (ret != -1 && opt == OPT_DONTFRAGMENT) {
+#ifdef LINUX
+ *value = (*value != IP_PMTUDISC_DONT) ? 1 : 0;
+#endif
+ }
+ return ret;
+ }
+
+ int SetOption(Option opt, int value) {
+ int slevel;
+ int sopt;
+ if (TranslateOption(opt, &slevel, &sopt) == -1)
+ return -1;
+ if (opt == OPT_DONTFRAGMENT) {
+#ifdef LINUX
+ value = (value) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
+#endif
+ }
+ return ::setsockopt(s_, slevel, sopt, (SockOptArg)&value, sizeof(value));
+ }
+
+ int Send(const void *pv, size_t cb) {
+ int sent = ::send(s_, reinterpret_cast<const char *>(pv), (int)cb,
+#ifdef LINUX
+ // Suppress SIGPIPE. Without this, attempting to send on a socket whose
+ // other end is closed will result in a SIGPIPE signal being raised to
+ // our process, which by default will terminate the process, which we
+ // don't want. By specifying this flag, we'll just get the error EPIPE
+ // instead and can handle the error gracefully.
+ MSG_NOSIGNAL
+#else
+ 0
+#endif
+ );
+ 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) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int sent = ::sendto(
+ s_, (const char *)pv, (int)cb,
+#ifdef LINUX
+ // Suppress SIGPIPE. See above for explanation.
+ MSG_NOSIGNAL,
+#else
+ 0,
+#endif
+ (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;
+ }
+ //LOG_F(LS_INFO) << cb << ":" << addr.ToString() << ":" << sent << ":" << error_;
+ return sent;
+ }
+
+ int Recv(void *pv, size_t cb) {
+ int received = ::recv(s_, (char *)pv, (int)cb, 0);
+ if ((received == 0) && (cb != 0)) {
+ // Note: on graceful shutdown, recv can return 0. In this case, we
+ // pretend it is blocking, and then signal close, so that simplifying
+ // assumptions can be made about Recv.
+ LOG(LS_WARNING) << "EOF from socket; deferring close event";
+ // Must turn this back on so that the select() loop will notice the close
+ // event.
+ enabled_events_ |= kfRead;
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ UpdateLastError();
+ bool success = (received >= 0) || IsBlockingError(error_);
+ if (udp_ || success) {
+ enabled_events_ |= kfRead;
+ }
+ if (!success) {
+ LOG_F(LS_VERBOSE) << "Error = " << error_;
+ }
+ return received;
+ }
+
+ int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) {
+ sockaddr_in saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ int received = ::recvfrom(s_, (char *)pv, (int)cb, 0, (sockaddr*)&saddr,
+ &cbAddr);
+ UpdateLastError();
+ if ((received >= 0) && (paddr != NULL))
+ paddr->FromSockAddr(saddr);
+ bool success = (received >= 0) || IsBlockingError(error_);
+ if (udp_ || success) {
+ enabled_events_ |= kfRead;
+ }
+ if (!success) {
+ LOG_F(LS_VERBOSE) << "Error = " << error_;
+ }
+ return received;
+ }
+
+ int Listen(int backlog) {
+ int err = ::listen(s_, backlog);
+ UpdateLastError();
+ if (err == 0) {
+ state_ = CS_CONNECTING;
+ enabled_events_ |= kfAccept;
+#ifdef _DEBUG
+ dbg_addr_ = "Listening @ ";
+ dbg_addr_.append(GetLocalAddress().ToString());
+#endif // _DEBUG
+ }
+ return err;
+ }
+
+ AsyncSocket* Accept(SocketAddress *paddr) {
+ sockaddr_in saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ SOCKET s = ::accept(s_, (sockaddr*)&saddr, &cbAddr);
+ UpdateLastError();
+ if (s == INVALID_SOCKET)
+ return NULL;
+ enabled_events_ |= kfAccept;
+ if (paddr != NULL)
+ paddr->FromSockAddr(saddr);
+ 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;
+ if (resolver_) {
+ resolver_->Destroy(false);
+ resolver_ = NULL;
+ }
+ return err;
+ }
+
+ int EstimateMTU(uint16* mtu) {
+ SocketAddress addr = GetRemoteAddress();
+ if (addr.IsAny()) {
+ error_ = ENOTCONN;
+ return -1;
+ }
+
+#if defined(WIN32)
+ // Gets the interface MTU (TTL=1) for the interface used to reach |addr|.
+ 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;
+ WinPing::PingResult result = ping.Ping(addr.ip(), size, 0, 1, false);
+ if (result == WinPing::PING_FAIL) {
+ error_ = EINVAL; // can't think of a better error ID
+ return -1;
+ } else if (result != WinPing::PING_TOO_LARGE) {
+ *mtu = PACKET_MAXIMUMS[level];
+ return 0;
+ }
+ }
+
+ ASSERT(false);
+ return -1;
+#elif defined(OSX)
+ // No simple way to do this on Mac OS X.
+ // SIOCGIFMTU would work if we knew which interface would be used, but
+ // figuring that out is pretty complicated. For now we'll return an error
+ // and let the caller pick a default MTU.
+ error_ = EINVAL;
+ return -1;
+#elif defined(LINUX)
+ // Gets the path MTU.
+ 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 = value;
+ return 0;
+#endif
+ }
+
+ SocketServer* socketserver() { return ss_; }
+
+ protected:
+ void OnResolveResult(SignalThread* thread) {
+ if (thread != resolver_) {
+ return;
+ }
+
+ int error = resolver_->error();
+ if (error == 0) {
+ error = DoConnect(resolver_->address());
+ } else {
+ Close();
+ }
+
+ if (error) {
+ error_ = error;
+ SignalCloseEvent(this, error_);
+ }
+ }
+
+ void UpdateLastError() {
+ error_ = LAST_SYSTEM_ERROR;
+ }
+
+ static int TranslateOption(Option opt, int* slevel, int* sopt) {
+ switch (opt) {
+ case OPT_DONTFRAGMENT:
+#ifdef WIN32
+ *slevel = IPPROTO_IP;
+ *sopt = IP_DONTFRAGMENT;
+ break;
+#elif defined(OSX)
+ LOG(LS_WARNING) << "Socket::OPT_DONTFRAGMENT not supported.";
+ return -1;
+#elif defined(POSIX)
+ *slevel = IPPROTO_IP;
+ *sopt = IP_MTU_DISCOVER;
+ break;
+#endif
+ case OPT_RCVBUF:
+ *slevel = SOL_SOCKET;
+ *sopt = SO_RCVBUF;
+ break;
+ case OPT_SNDBUF:
+ *slevel = SOL_SOCKET;
+ *sopt = SO_SNDBUF;
+ break;
+ case OPT_NODELAY:
+ *slevel = IPPROTO_TCP;
+ *sopt = TCP_NODELAY;
+ break;
+ default:
+ ASSERT(false);
+ return -1;
+ }
+ return 0;
+ }
+
+ PhysicalSocketServer* ss_;
+ SOCKET s_;
+ uint8 enabled_events_;
+ bool udp_;
+ int error_;
+ ConnState state_;
+ AsyncResolver* resolver_;
+
+#ifdef _DEBUG
+ std::string dbg_addr_;
+#endif // _DEBUG;
+};
+
+#ifdef POSIX
+class Dispatcher {
+ public:
+ virtual ~Dispatcher() { }
+ virtual uint32 GetRequestedEvents() = 0;
+ virtual void OnPreEvent(uint32 ff) = 0;
+ virtual void OnEvent(uint32 ff, int err) = 0;
+ virtual int GetDescriptor() = 0;
+ virtual bool IsDescriptorClosed() = 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_) {
+ const uint8 b[1] = { 0 };
+ if (VERIFY(1 == write(afd_[1], b, sizeof(b)))) {
+ 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[4]; // Allow for reading more than 1 byte, but expect 1.
+ VERIFY(1 == read(afd_[0], b, sizeof(b)));
+ fSignaled_ = false;
+ }
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ ASSERT(false);
+ }
+
+ virtual int GetDescriptor() {
+ return afd_[0];
+ }
+
+ virtual bool IsDescriptorClosed() {
+ return false;
+ }
+
+ private:
+ PhysicalSocketServer *ss_;
+ int afd_[2];
+ bool fSignaled_;
+ CriticalSection crit_;
+};
+
+// This is a class customized to use the self-pipe trick to deliver POSIX
+// signals. This is the only safe, reliable, cross-platform way to do
+// non-trivial things with a POSIX signal (until proper pselect()
+// implementations become ubiquitous).
+class PosixSignalDeliveryDispatcher : public Dispatcher {
+ public:
+ virtual ~PosixSignalDeliveryDispatcher() {
+ close(afd_[0]);
+ close(afd_[1]);
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return kfRead;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ // Events might get grouped if signals come very fast, so we read out up to
+ // 16 bytes to make sure we keep the pipe empty.
+ uint8 b[16];
+ ssize_t ret = read(afd_[0], b, sizeof(b));
+ if (ret < 0) {
+ LOG_ERR(LS_WARNING) << "Error in read()";
+ } else if (ret == 0) {
+ LOG(LS_WARNING) << "Should have read at least one byte";
+ }
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ for (int signum = 0; signum < ARRAY_SIZE(received_signal_); ++signum) {
+ if (received_signal_[signum]) {
+ received_signal_[signum] = false;
+ HandlerMap::iterator i = handlers_.find(signum);
+ if (i == handlers_.end()) {
+ // This can happen if a signal is delivered to our process at around
+ // the same time as we unset our handler for it. It is not an error
+ // condidion, but it's unusual enough to be worth logging.
+ LOG(LS_INFO) << "Received signal with no handler: " << signum;
+ } else {
+ // Otherwise, execute the handler.
+ (*i->second)(signum);
+ }
+ }
+ }
+ }
+
+ virtual int GetDescriptor() {
+ return afd_[0];
+ }
+
+ virtual bool IsDescriptorClosed() {
+ return false;
+ }
+
+ void SetHandler(int signum, void (*handler)(int)) {
+ handlers_[signum] = handler;
+ }
+
+ void ClearHandler(int signum) {
+ handlers_.erase(signum);
+ }
+
+ bool HasHandlers() {
+ return !handlers_.empty();
+ }
+
+ // This is called directly from our real signal handler, so it must be
+ // signal-handler-safe. That means it cannot assume anything about the
+ // user-level state of the process, since the handler could be executed at any
+ // time on any thread.
+ void OnPosixSignalReceived(int signum) {
+ if (signum >= ARRAY_SIZE(received_signal_)) {
+ // We don't have space in our array for this.
+ return;
+ }
+ // Set a flag saying we've seen this signal.
+ received_signal_[signum] = true;
+ // Tell the thread running our PhysicalSocketServer that we got a signal.
+ const uint8 b[1] = { 0 };
+ if (-1 == write(afd_[1], b, sizeof(b))) {
+ // Nothing we can do here. If there's an error somehow then there's
+ // nothing we can safely do from a signal handler.
+ // No, we can't even safely log it.
+ // But, we still have to check the return value here. Otherwise,
+ // GCC 4.4.1 complains ignoring return value. Even (void) doesn't help.
+ return;
+ }
+ }
+
+ // Sets a PhysicalSocketServer to own signal delivery, or fails if already
+ // owned.
+ bool SetOwner(PhysicalSocketServer *owner) {
+ CritScope cs(&owner_critsec_);
+ if (owner == owner_) {
+ return true;
+ } else if (owner_) {
+ return false;
+ } else {
+ owner_ = owner;
+ owner_->Add(this);
+ return true;
+ }
+ }
+
+ bool IsOwner(PhysicalSocketServer *ss) {
+ CritScope cs(&owner_critsec_);
+ return owner_ == ss;
+ }
+
+ void ClearOwner(PhysicalSocketServer *ss) {
+ CritScope cs(&owner_critsec_);
+ if (owner_ != ss) {
+ return;
+ }
+ owner_->Remove(this);
+ owner_ = NULL;
+ }
+
+ // There is just a single global instance. (Signal handlers do not get any
+ // sort of user-defined void * parameter, so they can't access anything that
+ // isn't global.)
+ static PosixSignalDeliveryDispatcher instance_;
+
+ private:
+ // POSIX only specifies 32 signals, but in principle the system might have
+ // more and the programmer might choose to use them, so we size our array
+ // for 128.
+ static const int kNumPosixSignals = 128;
+
+ typedef std::map<int, void (*)(int)> HandlerMap;
+
+ PosixSignalDeliveryDispatcher() : owner_(NULL) {
+ if (pipe(afd_) < 0) {
+ LOG_ERR(LS_ERROR) << "pipe failed";
+ return;
+ }
+ if (fcntl(afd_[0], F_SETFL, O_NONBLOCK) < 0) {
+ LOG_ERR(LS_WARNING) << "fcntl #1 failed";
+ }
+ if (fcntl(afd_[1], F_SETFL, O_NONBLOCK) < 0) {
+ LOG_ERR(LS_WARNING) << "fcntl #2 failed";
+ }
+ memset(const_cast<void *>(static_cast<volatile void *>(received_signal_)),
+ 0,
+ sizeof(received_signal_));
+ }
+
+ int afd_[2];
+ HandlerMap handlers_;
+ // These are boolean flags that will be set in our signal handler and read
+ // and cleared from Wait(). There is a race involved in this, but it is
+ // benign. The signal handler sets the flag before signaling the pipe, so
+ // we'll never end up blocking in select() while a flag is still true.
+ // However, if two of the same signal arrive close to each other then it's
+ // possible that the second time the handler may set the flag while it's still
+ // true, meaning that signal will be missed. But the first occurrence of it
+ // will still be handled, so this isn't a problem.
+ // Volatile is not necessary here for correctness, but this data _is_ volatile
+ // so I've marked it as such.
+ volatile uint8 received_signal_[kNumPosixSignals];
+ // Our owner.
+ PhysicalSocketServer *owner_;
+ // To synchronize ownership changes.
+ CriticalSection owner_critsec_;
+};
+
+PosixSignalDeliveryDispatcher PosixSignalDeliveryDispatcher::instance_;
+
+class SocketDispatcher : public Dispatcher, public PhysicalSocket {
+ public:
+ explicit SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) {
+ }
+ SocketDispatcher(SOCKET s, PhysicalSocketServer *ss) : PhysicalSocket(ss, s) {
+ }
+
+ virtual ~SocketDispatcher() {
+ Close();
+ }
+
+ bool Initialize() {
+ ss_->Add(this);
+ 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 bool IsDescriptorClosed() {
+ // We don't have a reliable way of distinguishing end-of-stream
+ // from readability. So test on each readable call. Is this
+ // inefficient? Probably.
+ char ch;
+ ssize_t res = ::recv(s_, &ch, 1, MSG_PEEK);
+ if (res > 0) {
+ // Data available, so not closed.
+ return false;
+ } else if (res == 0) {
+ // EOF, so closed.
+ return true;
+ } else { // error
+ switch (errno) {
+ // Returned if we've already closed s_.
+ case EBADF:
+ // Returned during ungraceful peer shutdown.
+ case ECONNRESET:
+ return true;
+ default:
+ // Assume that all other errors are just blocking errors, meaning the
+ // connection is still good but we just can't read from it right now.
+ // This should only happen when connecting (and at most once), because
+ // in all other cases this function is only called if the file
+ // descriptor is already known to be in the readable state. However,
+ // it's not necessary a problem if we spuriously interpret a
+ // "connection lost"-type error as a blocking error, because typically
+ // the next recv() will get EOF, so we'll still eventually notice that
+ // the socket is closed.
+ LOG_ERR(LS_WARNING) << "Assuming benign blocking error";
+ return false;
+ }
+ }
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return enabled_events_;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ if ((ff & kfConnect) != 0)
+ state_ = CS_CONNECTED;
+ if ((ff & kfClose) != 0)
+ state_ = CS_CLOSED;
+ }
+
+ 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 & kfAccept) != 0) {
+ enabled_events_ &= ~kfAccept;
+ SignalReadEvent(this);
+ }
+ if ((ff & kfClose) != 0) {
+ // The socket is now dead to us, so stop checking it.
+ enabled_events_ = 0;
+ SignalCloseEvent(this, err);
+ }
+ }
+
+ virtual int Close() {
+ if (s_ == INVALID_SOCKET)
+ return 0;
+
+ ss_->Remove(this);
+ return PhysicalSocket::Close();
+ }
+};
+
+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 bool IsDescriptorClosed() {
+ return false;
+ }
+
+ 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 ~Dispatcher() {}
+ 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;
+};
+
+static uint32 FlagsToEvents(uint32 events) {
+ uint32 ffFD = FD_CLOSE;
+ if (events & kfRead)
+ ffFD |= FD_READ;
+ if (events & kfWrite)
+ ffFD |= FD_WRITE;
+ if (events & kfConnect)
+ ffFD |= FD_CONNECT;
+ if (events & kfAccept)
+ ffFD |= FD_ACCEPT;
+ return ffFD;
+}
+
+class EventDispatcher : public Dispatcher {
+ public:
+ EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) {
+ hev_ = WSACreateEvent();
+ if (hev_) {
+ 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;
+ // We set CS_CLOSED from CheckSignalClose.
+ }
+
+ 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)) {
+ if (ff != kfConnect)
+ LOG(LS_VERBOSE) << "Signalled with kfConnect: " << ff;
+ enabled_events_ &= ~kfConnect;
+#ifdef _DEBUG
+ dbg_addr_ = "Connected @ ";
+ dbg_addr_.append(GetRemoteAddress().ToString());
+#endif // _DEBUG
+ SignalConnectEvent(this);
+ }
+ if (((ff & kfAccept) != 0) && (id_ == cache_id)) {
+ enabled_events_ &= ~kfAccept;
+ SignalReadEvent(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;
+
+ state_ = CS_CLOSED;
+ 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_);
+#ifdef WIN32
+ socket_ev_ = WSACreateEvent();
+#endif
+}
+
+PhysicalSocketServer::~PhysicalSocketServer() {
+#ifdef WIN32
+ WSACloseEvent(socket_ev_);
+#endif
+#ifdef POSIX
+ PosixSignalDeliveryDispatcher::instance_.ClearOwner(this);
+#endif
+ delete signal_wakeup_;
+ ASSERT(dispatchers_.empty());
+}
+
+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_);
+ // Prevent duplicates. This can cause dead dispatchers to stick around.
+ DispatcherList::iterator pos = std::find(dispatchers_.begin(),
+ dispatchers_.end(),
+ pdispatcher);
+ if (pos != dispatchers_.end())
+ return;
+ dispatchers_.push_back(pdispatcher);
+}
+
+void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) {
+ CritScope cs(&crit_);
+ DispatcherList::iterator pos = std::find(dispatchers_.begin(),
+ dispatchers_.end(),
+ pdispatcher);
+ ASSERT(pos != dispatchers_.end());
+ size_t index = pos - dispatchers_.begin();
+ dispatchers_.erase(pos);
+ for (IteratorList::iterator it = iterators_.begin(); it != iterators_.end();
+ ++it) {
+ if (index < **it) {
+ --**it;
+ }
+ }
+}
+
+#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 != kForever) {
+ // 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 (size_t 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 | kfAccept))
+ 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.
+ if (n < 0) {
+ if (errno != EINTR) {
+ LOG_E(LS_ERROR, EN, errno) << "select";
+ return false;
+ }
+ // Else ignore the error and keep going. If this EINTR was for one of the
+ // signals managed by this PhysicalSocketServer, the
+ // PosixSignalDeliveryDispatcher will be in the signaled state in the next
+ // iteration.
+ } else if (n == 0) {
+ // If timeout, return success
+ return true;
+ } else {
+ // We have signaled descriptors
+ CritScope cr(&crit_);
+ for (size_t i = 0; i < dispatchers_.size(); ++i) {
+ Dispatcher *pdispatcher = dispatchers_[i];
+ int fd = pdispatcher->GetDescriptor();
+ uint32 ff = 0;
+ int errcode = 0;
+
+ // Reap any error code, which can be signaled through reads or writes.
+ // TODO(juberti): Should we set errcode if getsockopt fails?
+ if (FD_ISSET(fd, &fdsRead) || FD_ISSET(fd, &fdsWrite)) {
+ socklen_t len = sizeof(errcode);
+ ::getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &len);
+ }
+
+ // Check readable descriptors. If we're waiting on an accept, signal
+ // that. Otherwise we're waiting for data, check to see if we're
+ // readable or really closed.
+ // TODO(juberti): Only peek at TCP descriptors.
+ if (FD_ISSET(fd, &fdsRead)) {
+ FD_CLR(fd, &fdsRead);
+ if (pdispatcher->GetRequestedEvents() & kfAccept) {
+ ff |= kfAccept;
+ } else if (errcode || pdispatcher->IsDescriptorClosed()) {
+ ff |= kfClose;
+ } else {
+ ff |= kfRead;
+ }
+ }
+
+ // Check writable descriptors. If we're waiting on a connect, detect
+ // success versus failure by the reaped error code.
+ if (FD_ISSET(fd, &fdsWrite)) {
+ FD_CLR(fd, &fdsWrite);
+ if (pdispatcher->GetRequestedEvents() & kfConnect) {
+ if (!errcode) {
+ ff |= kfConnect;
+ } else {
+ ff |= kfClose;
+ }
+ } else {
+ ff |= kfWrite;
+ }
+ }
+
+ // Tell the descriptor about the event.
+ if (ff != 0) {
+ pdispatcher->OnPreEvent(ff);
+ pdispatcher->OnEvent(ff, errcode);
+ }
+ }
+ }
+
+ // Recalc the time remaining to wait. Doing it here means it doesn't get
+ // calced twice the first time through the loop
+
+ if (cmsWait != kForever) {
+ ptvWait->tv_sec = 0;
+ ptvWait->tv_usec = 0;
+ struct timeval tvT;
+ gettimeofday(&tvT, NULL);
+ if ((tvStop.tv_sec > tvT.tv_sec)
+ || ((tvStop.tv_sec == tvT.tv_sec)
+ && (tvStop.tv_usec > tvT.tv_usec))) {
+ ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec;
+ ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec;
+ if (ptvWait->tv_usec < 0) {
+ ASSERT(ptvWait->tv_sec > 0);
+ ptvWait->tv_usec += 1000000;
+ ptvWait->tv_sec -= 1;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static void GlobalSignalHandler(int signum) {
+ PosixSignalDeliveryDispatcher::instance_.OnPosixSignalReceived(signum);
+}
+
+bool PhysicalSocketServer::SetPosixSignalHandler(int signum,
+ void (*handler)(int)) {
+ // If handler is SIG_IGN or SIG_DFL then clear our user-level handler,
+ // otherwise set one.
+ if (handler == SIG_IGN || handler == SIG_DFL) {
+ if (!InstallSignal(signum, handler)) {
+ return false;
+ }
+ if (PosixSignalDeliveryDispatcher::instance_.IsOwner(this)) {
+ PosixSignalDeliveryDispatcher::instance_.ClearHandler(signum);
+ if (!PosixSignalDeliveryDispatcher::instance_.HasHandlers()) {
+ PosixSignalDeliveryDispatcher::instance_.ClearOwner(this);
+ }
+ }
+ } else {
+ if (!PosixSignalDeliveryDispatcher::instance_.SetOwner(this)) {
+ LOG(LS_ERROR) <<
+ "Cannot do POSIX signal delivery on more than one PhysicalSocketServer";
+ return false;
+ }
+ PosixSignalDeliveryDispatcher::instance_.SetHandler(signum, handler);
+ if (!InstallSignal(signum, &GlobalSignalHandler)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool PhysicalSocketServer::InstallSignal(int signum, void (*handler)(int)) {
+ struct sigaction act;
+ // It doesn't really matter what we set this mask to.
+ if (sigemptyset(&act.sa_mask) != 0) {
+ LOG_ERR(LS_ERROR) << "Couldn't set mask";
+ return false;
+ }
+ act.sa_handler = handler;
+ // Use SA_RESTART so that our syscalls don't get EINTR, since we don't need it
+ // and it's a nuisance. Though some syscalls still return EINTR and there's no
+ // real standard for which ones. :(
+ act.sa_flags = SA_RESTART;
+ if (sigaction(signum, &act, NULL) != 0) {
+ LOG_ERR(LS_ERROR) << "Couldn't set sigaction";
+ return false;
+ }
+ return true;
+}
+#endif // POSIX
+
+#ifdef WIN32
+bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) {
+ int cmsTotal = cmsWait;
+ int cmsElapsed = 0;
+ uint32 msStart = Time();
+
+#if LOGGING
+ if (last_tick_dispatch_count_ == 0) {
+ last_tick_tracked_ = msStart;
+ }
+#endif
+
+ fWait_ = true;
+ while (fWait_) {
+ std::vector<WSAEVENT> events;
+ std::vector<Dispatcher *> event_owners;
+
+ events.push_back(socket_ev_);
+
+ {
+ CritScope cr(&crit_);
+ size_t i = 0;
+ iterators_.push_back(&i);
+ // Don't track dispatchers_.size(), because we want to pick up any new
+ // dispatchers that were added while processing the loop.
+ while (i < dispatchers_.size()) {
+ 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);
+ }
+ }
+ ASSERT(iterators_.back() == &i);
+ iterators_.pop_back();
+ }
+
+ // Which is shorter, the delay wait or the asked wait?
+
+ int cmsNext;
+ if (cmsWait == kForever) {
+ cmsNext = cmsWait;
+ } else {
+ cmsNext = _max(0, cmsTotal - cmsElapsed);
+ }
+
+ // 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) {
+ int32 elapsed = TimeSince(last_tick_tracked_);
+ LOG(INFO) << "PhysicalSocketServer took " << elapsed << "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(elapsed > 1000);
+
+ last_tick_tracked_ = Time();
+ last_tick_dispatch_count_ = 0;
+ }
+#endif
+
+ if (dw == WSA_WAIT_FAILED) {
+ // Failed?
+ // TODO(juberti): need a better strategy than this!
+ int error = WSAGetLastError();
+ ASSERT(false);
+ return false;
+ } else if (dw == WSA_WAIT_TIMEOUT) {
+ // Timeout?
+ return true;
+ } else {
+ // 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) {
+ size_t i = 0, end = dispatchers_.size();
+ iterators_.push_back(&i);
+ iterators_.push_back(&end); // Don't iterate over new dispatchers.
+ while (i < end) {
+ 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 {
+ ff |= kfClose;
+ errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT];
+ }
+ }
+ if (wsaEvents.lNetworkEvents & FD_ACCEPT)
+ ff |= kfAccept;
+ if (wsaEvents.lNetworkEvents & FD_CLOSE) {
+ ff |= kfClose;
+ errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT];
+ }
+ if (ff != 0) {
+ disp->OnPreEvent(ff);
+ disp->OnEvent(ff, errcode);
+ }
+ }
+ }
+ ASSERT(iterators_.back() == &end);
+ iterators_.pop_back();
+ ASSERT(iterators_.back() == &i);
+ iterators_.pop_back();
+ }
+
+ // Reset the network event until new activity occurs
+ WSAResetEvent(socket_ev_);
+ }
+
+ // Break?
+ if (!fWait_)
+ break;
+ cmsElapsed = TimeSince(msStart);
+ if ((cmsWait != kForever) && (cmsElapsed >= cmsWait)) {
+ break;
+ }
+ }
+
+ // Done
+ return true;
+}
+#endif // WIN32
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/physicalsocketserver.h b/third_party/libjingle/source/talk/base/physicalsocketserver.h
new file mode 100644
index 0000000..282e14c
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/physicalsocketserver.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 TALK_BASE_PHYSICALSOCKETSERVER_H__
+#define TALK_BASE_PHYSICALSOCKETSERVER_H__
+
+#include <vector>
+
+#include "talk/base/asyncfile.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/criticalsection.h"
+
+#ifdef POSIX
+typedef int SOCKET;
+#endif // POSIX
+
+namespace talk_base {
+
+class Dispatcher;
+class Signaler;
+#ifdef POSIX
+class PosixSignalDeliveryDispatcher;
+#endif
+
+// 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);
+
+ // Sets the function to be executed in response to the specified POSIX signal.
+ // The function is executed from inside Wait() using the "self-pipe trick"--
+ // regardless of which thread receives the signal--and hence can safely
+ // manipulate user-level data structures.
+ // "handler" may be SIG_IGN, SIG_DFL, or a user-specified function, just like
+ // with signal(2).
+ // Only one PhysicalSocketServer may have user-level signal handlers.
+ // Attempting to install a signal handler for a PhysicalSocketServer when
+ // another already owns some will fail.
+ // The signal mask is not modified. It is the caller's responsibily to
+ // maintain it as desired.
+ bool SetPosixSignalHandler(int signum, void (*handler)(int));
+#endif
+
+private:
+ typedef std::vector<Dispatcher*> DispatcherList;
+ typedef std::vector<size_t*> IteratorList;
+
+#ifdef POSIX
+ static bool InstallSignal(int signum, void (*handler)(int));
+#endif
+
+ DispatcherList dispatchers_;
+ IteratorList iterators_;
+ Signaler* signal_wakeup_;
+ CriticalSection crit_;
+ bool fWait_;
+ uint32 last_tick_tracked_;
+ int last_tick_dispatch_count_;
+#ifdef WIN32
+ WSAEVENT socket_ev_;
+#endif
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_PHYSICALSOCKETSERVER_H__
diff --git a/third_party/libjingle/source/talk/base/proxydetect.cc b/third_party/libjingle/source/talk/base/proxydetect.cc
new file mode 100644
index 0000000..5c498f5
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/proxydetect.cc
@@ -0,0 +1,1253 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/proxydetect.h"
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#include <shlobj.h>
+#endif // WIN32
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef OSX
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#include <Security/Security.h>
+#include "macconversion.h"
+#endif
+
+#include <map>
+
+#include "talk/base/fileutils.h"
+#include "talk/base/httpcommon.h"
+#include "talk/base/httpcommon-inl.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stringutils.h"
+
+#ifdef WIN32
+#define _TRY_WINHTTP 1
+#define _TRY_JSPROXY 0
+#define _TRY_WM_FINDPROXY 0
+#define _TRY_IE_LAN_SETTINGS 1
+#endif // WIN32
+
+// For all platforms try Firefox.
+#define _TRY_FIREFOX 1
+
+// Use profiles.ini to find the correct profile for this user.
+// If not set, we'll just look for the default one.
+#define USE_FIREFOX_PROFILES_INI 1
+
+static const size_t kMaxLineLength = 1024;
+static const char kFirefoxPattern[] = "Firefox";
+static const char kInternetExplorerPattern[] = "MSIE";
+
+struct StringMap {
+ public:
+ void Add(const char * name, const char * value) { map_[name] = value; }
+ const std::string& Get(const char * name, const char * def = "") const {
+ std::map<std::string, std::string>::const_iterator it =
+ map_.find(name);
+ if (it != map_.end())
+ return it->second;
+ def_ = def;
+ return def_;
+ }
+ bool IsSet(const char * name) const {
+ return (map_.find(name) != map_.end());
+ }
+ private:
+ std::map<std::string, std::string> map_;
+ mutable std::string def_;
+};
+
+enum UserAgent {
+ UA_FIREFOX,
+ UA_INTERNETEXPLORER,
+ UA_OTHER,
+ UA_UNKNOWN
+};
+
+#if _TRY_WINHTTP
+//#include <winhttp.h>
+// Note: From winhttp.h
+
+const char WINHTTP[] = "winhttp";
+
+typedef LPVOID HINTERNET;
+
+typedef struct {
+ DWORD dwAccessType; // see WINHTTP_ACCESS_* types below
+ LPWSTR lpszProxy; // proxy server list
+ LPWSTR lpszProxyBypass; // proxy bypass list
+} WINHTTP_PROXY_INFO, * LPWINHTTP_PROXY_INFO;
+
+typedef struct {
+ DWORD dwFlags;
+ DWORD dwAutoDetectFlags;
+ LPCWSTR lpszAutoConfigUrl;
+ LPVOID lpvReserved;
+ DWORD dwReserved;
+ BOOL fAutoLogonIfChallenged;
+} WINHTTP_AUTOPROXY_OPTIONS;
+
+typedef struct {
+ BOOL fAutoDetect;
+ LPWSTR lpszAutoConfigUrl;
+ LPWSTR lpszProxy;
+ LPWSTR lpszProxyBypass;
+} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
+
+extern "C" {
+ typedef HINTERNET (WINAPI * pfnWinHttpOpen)
+ (
+ IN LPCWSTR pwszUserAgent,
+ IN DWORD dwAccessType,
+ IN LPCWSTR pwszProxyName OPTIONAL,
+ IN LPCWSTR pwszProxyBypass OPTIONAL,
+ IN DWORD dwFlags
+ );
+ typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle)
+ (
+ IN HINTERNET hInternet
+ );
+ typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl)
+ (
+ IN HINTERNET hSession,
+ IN LPCWSTR lpcwszUrl,
+ IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions,
+ OUT WINHTTP_PROXY_INFO * pProxyInfo
+ );
+ typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig)
+ (
+ IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig
+ );
+
+} // extern "C"
+
+#define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001
+#define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002
+#define WINHTTP_AUTOPROXY_RUN_INPROCESS 0x00010000
+#define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY 0x00020000
+#define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001
+#define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002
+#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
+#define WINHTTP_ACCESS_TYPE_NO_PROXY 1
+#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
+#define WINHTTP_NO_PROXY_NAME NULL
+#define WINHTTP_NO_PROXY_BYPASS NULL
+
+#endif // _TRY_WINHTTP
+
+#if _TRY_JSPROXY
+extern "C" {
+ typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo)
+ (
+ LPCSTR lpszUrl,
+ DWORD dwUrlLength,
+ LPSTR lpszUrlHostName,
+ DWORD dwUrlHostNameLength,
+ LPSTR * lplpszProxyHostName,
+ LPDWORD lpdwProxyHostNameLength
+ );
+} // extern "C"
+#endif // _TRY_JSPROXY
+
+#if _TRY_WM_FINDPROXY
+#include <comutil.h>
+#include <wmnetsourcecreator.h>
+#include <wmsinternaladminnetsource.h>
+#endif // _TRY_WM_FINDPROXY
+
+#if _TRY_IE_LAN_SETTINGS
+#include <wininet.h>
+#include <string>
+#endif // _TRY_IE_LAN_SETTINGS
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Utility Functions
+//////////////////////////////////////////////////////////////////////
+
+#ifdef WIN32
+#ifdef _UNICODE
+
+typedef std::wstring tstring;
+std::string Utf8String(const tstring& str) { return ToUtf8(str); }
+
+#else // !_UNICODE
+
+typedef std::string tstring;
+std::string Utf8String(const tstring& str) { return str; }
+
+#endif // !_UNICODE
+#endif // WIN32
+
+bool ProxyItemMatch(const Url<char>& url, char * item, size_t len) {
+ // hostname:443
+ if (char * port = ::strchr(item, ':')) {
+ *port++ = '\0';
+ if (url.port() != atol(port)) {
+ return false;
+ }
+ }
+
+ // A.B.C.D or A.B.C.D/24
+ int a, b, c, d, m;
+ int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m);
+ if (match >= 4) {
+ uint32 ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) |
+ (d & 0xFF);
+ if ((match < 5) || (m > 32))
+ m = 32;
+ else if (m < 0)
+ m = 0;
+ uint32 mask = (m == 0) ? 0 : (~0UL) << (32 - m);
+ SocketAddress addr(url.host(), 0);
+ return !addr.IsUnresolved() && ((addr.ip() & mask) == (ip & mask));
+ }
+
+ // .foo.com
+ if (*item == '.') {
+ size_t hostlen = url.host().length();
+ return (hostlen > len)
+ && (stricmp(url.host().c_str() + (hostlen - len), item) == 0);
+ }
+
+ // localhost or www.*.com
+ if (!string_match(url.host().c_str(), item))
+ return false;
+
+ return true;
+}
+
+bool ProxyListMatch(const Url<char>& url, const std::string& proxy_list,
+ char sep) {
+ const size_t BUFSIZE = 256;
+ char buffer[BUFSIZE];
+ const char* list = proxy_list.c_str();
+ while (*list) {
+ // Remove leading space
+ if (isspace(*list)) {
+ ++list;
+ continue;
+ }
+ // Break on separator
+ size_t len;
+ const char * start = list;
+ if (const char * end = ::strchr(list, sep)) {
+ len = (end - list);
+ list += len + 1;
+ } else {
+ len = strlen(list);
+ list += len;
+ }
+ // Remove trailing space
+ while ((len > 0) && isspace(start[len-1]))
+ --len;
+ // Check for oversized entry
+ if (len >= BUFSIZE)
+ continue;
+ memcpy(buffer, start, len);
+ buffer[len] = 0;
+ if (!ProxyItemMatch(url, buffer, len))
+ continue;
+ return true;
+ }
+ return false;
+}
+
+bool Better(ProxyType lhs, const ProxyType rhs) {
+ // PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN
+ const int PROXY_VALUE[5] = { 0, 2, 3, 1 };
+ return (PROXY_VALUE[lhs] > PROXY_VALUE[rhs]);
+}
+
+bool ParseProxy(const std::string& saddress, ProxyInfo* proxy) {
+ const size_t kMaxAddressLength = 1024;
+ // Allow semicolon, space, or tab as an address separator
+ const char* const kAddressSeparator = " ;\t";
+
+ ProxyType ptype;
+ std::string host;
+ uint16 port;
+
+ const char* address = saddress.c_str();
+ while (*address) {
+ size_t len;
+ const char * start = address;
+ if (const char * sep = strchr(address, kAddressSeparator)) {
+ len = (sep - address);
+ address += len + 1;
+ while (::strchr(kAddressSeparator, *address)) {
+ address += 1;
+ }
+ } else {
+ len = strlen(address);
+ address += len;
+ }
+
+ if (len > kMaxAddressLength - 1) {
+ LOG(LS_WARNING) << "Proxy address too long [" << start << "]";
+ continue;
+ }
+
+ char buffer[kMaxAddressLength];
+ memcpy(buffer, start, len);
+ buffer[len] = 0;
+
+ char * colon = ::strchr(buffer, ':');
+ if (!colon) {
+ LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]";
+ continue;
+ }
+
+ *colon = 0;
+ char * endptr;
+ port = static_cast<uint16>(strtol(colon + 1, &endptr, 0));
+ if (*endptr != 0) {
+ LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]";
+ continue;
+ }
+
+ if (char * equals = ::strchr(buffer, '=')) {
+ *equals = 0;
+ host = equals + 1;
+ if (_stricmp(buffer, "socks") == 0) {
+ ptype = PROXY_SOCKS5;
+ } else if (_stricmp(buffer, "https") == 0) {
+ ptype = PROXY_HTTPS;
+ } else {
+ LOG(LS_WARNING) << "Proxy address with unknown protocol ["
+ << buffer << "]";
+ ptype = PROXY_UNKNOWN;
+ }
+ } else {
+ host = buffer;
+ ptype = PROXY_UNKNOWN;
+ }
+
+ if (Better(ptype, proxy->type)) {
+ proxy->type = ptype;
+ proxy->address.SetIP(host);
+ proxy->address.SetPort(port);
+ }
+ }
+
+ return proxy->type != PROXY_NONE;
+}
+
+UserAgent GetAgent(const char* agent) {
+ if (agent) {
+ std::string agent_str(agent);
+ if (agent_str.find(kFirefoxPattern) != std::string::npos) {
+ return UA_FIREFOX;
+ } else if (agent_str.find(kInternetExplorerPattern) != std::string::npos) {
+ return UA_INTERNETEXPLORER;
+ } else if (agent_str.empty()) {
+ return UA_UNKNOWN;
+ }
+ }
+ return UA_OTHER;
+}
+
+bool EndsWith(const std::string& a, const std::string& b) {
+ if (b.size() > a.size()) {
+ return false;
+ }
+ int result = a.compare(a.size() - b.size(), b.size(), b);
+ return result == 0;
+}
+
+bool GetFirefoxProfilePath(Pathname* path) {
+#ifdef WIN32
+ wchar_t w_path[MAX_PATH];
+ if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, w_path) !=
+ S_OK) {
+ LOG(LS_ERROR) << "SHGetFolderPath failed";
+ return false;
+ }
+ path->SetFolder(ToUtf8(w_path, wcslen(w_path)));
+ path->AppendFolder("Mozilla");
+ path->AppendFolder("Firefox");
+#elif OSX
+ FSRef fr;
+ if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType,
+ kCreateFolder, &fr)) {
+ LOG(LS_ERROR) << "FSFindFolder failed";
+ return false;
+ }
+ char buffer[NAME_MAX + 1];
+ if (0 != FSRefMakePath(&fr, reinterpret_cast<uint8*>(buffer),
+ ARRAY_SIZE(buffer))) {
+ LOG(LS_ERROR) << "FSRefMakePath failed";
+ return false;
+ }
+ path->SetFolder(std::string(buffer));
+ path->AppendFolder("Firefox");
+#else
+ char* user_home = getenv("HOME");
+ if (user_home == NULL) {
+ return false;
+ }
+ path->SetFolder(std::string(user_home));
+ path->AppendFolder(".mozilla");
+ path->AppendFolder("firefox");
+#endif // WIN32
+ return true;
+}
+
+bool GetDefaultFirefoxProfile(Pathname* profile_path) {
+ ASSERT(NULL != profile_path);
+ Pathname path;
+ if (!GetFirefoxProfilePath(&path)) {
+ return false;
+ }
+
+#if USE_FIREFOX_PROFILES_INI
+ // [Profile0]
+ // Name=default
+ // IsRelative=1
+ // Path=Profiles/2de53ejb.default
+ // Default=1
+
+ // Note: we are looking for the first entry with "Default=1", or the last
+ // entry in the file
+ path.SetFilename("profiles.ini");
+ FileStream* fs = Filesystem::OpenFile(path, "r");
+ if (!fs) {
+ return false;
+ }
+ Pathname candidate;
+ bool relative = true;
+ std::string line;
+ while (fs->ReadLine(&line) == SR_SUCCESS) {
+ if (line.length() == 0) {
+ continue;
+ }
+ if (line.at(0) == '[') {
+ relative = true;
+ candidate.clear();
+ } else if (line.find("IsRelative=") == 0) {
+ relative = (line.at(11) != '0');
+ } else if (line.find("Path=") == 0) {
+ if (relative) {
+ candidate = path;
+ } else {
+ candidate.clear();
+ }
+ candidate.AppendFolder(line.substr(5));
+ } else if (line.find("Default=") == 0) {
+ if ((line.at(8) != '0') && !candidate.empty()) {
+ break;
+ }
+ }
+ }
+ fs->Close();
+ if (candidate.empty()) {
+ return false;
+ }
+ profile_path->SetPathname(candidate.pathname());
+
+#else // !USE_FIREFOX_PROFILES_INI
+ path.AppendFolder("Profiles");
+ DirectoryIterator* it = Filesystem::IterateDirectory();
+ it->Iterate(path);
+ std::string extension(".default");
+ while (!EndsWith(it->Name(), extension)) {
+ if (!it->Next()) {
+ return false;
+ }
+ }
+
+ profile_path->SetPathname(path);
+ profile->AppendFolder("Profiles");
+ profile->AppendFolder(it->Name());
+ delete it;
+
+#endif // !USE_FIREFOX_PROFILES_INI
+
+ return true;
+}
+
+bool ReadFirefoxPrefs(const Pathname& filename,
+ const char * prefix,
+ StringMap* settings) {
+ FileStream* fs = Filesystem::OpenFile(filename, "r");
+ if (!fs) {
+ LOG(LS_ERROR) << "Failed to open file: " << filename.pathname();
+ return false;
+ }
+
+ std::string line;
+ while (fs->ReadLine(&line) == SR_SUCCESS) {
+ size_t prefix_len = strlen(prefix);
+
+ // Skip blank lines and too long lines.
+ if ((line.length() == 0) || (line.length() > kMaxLineLength)
+ || (line.at(0) == '#') || line.compare(0, 2, "/*") == 0
+ || line.compare(0, 2, " *") == 0) {
+ continue;
+ }
+
+ char buffer[kMaxLineLength];
+ strcpyn(buffer, sizeof(buffer), line.c_str());
+ int nstart = 0, nend = 0, vstart = 0, vend = 0;
+ sscanf(buffer, "user_pref(\"%n%*[^\"]%n\", %n%*[^)]%n);",
+ &nstart, &nend, &vstart, &vend);
+ if (vend > 0) {
+ char* name = buffer + nstart;
+ name[nend - nstart] = 0;
+ if ((vend - vstart >= 2) && (buffer[vstart] == '"')) {
+ vstart += 1;
+ vend -= 1;
+ }
+ char* value = buffer + vstart;
+ value[vend - vstart] = 0;
+ if ((strncmp(name, prefix, prefix_len) == 0) && *value) {
+ settings->Add(name + prefix_len, value);
+ }
+ } else {
+ LOG_F(LS_WARNING) << "Unparsed pref [" << buffer << "]";
+ }
+ }
+ fs->Close();
+ return true;
+}
+
+bool GetFirefoxProxySettings(const char* url, ProxyInfo* proxy) {
+ Url<char> purl(url);
+ Pathname path;
+ bool success = false;
+ if (GetDefaultFirefoxProfile(&path)) {
+ StringMap settings;
+ path.SetFilename("prefs.js");
+ if (ReadFirefoxPrefs(path, "network.proxy.", &settings)) {
+ success = true;
+ proxy->bypass_list =
+ settings.Get("no_proxies_on", "localhost, 127.0.0.1");
+ if (settings.Get("type") == "1") {
+ // User has manually specified a proxy, try to figure out what
+ // type it is.
+ if (ProxyListMatch(purl, proxy->bypass_list.c_str(), ',')) {
+ // Our url is in the list of url's to bypass proxy.
+ } else if (settings.Get("share_proxy_settings") == "true") {
+ proxy->type = PROXY_UNKNOWN;
+ proxy->address.SetIP(settings.Get("http"));
+ proxy->address.SetPort(atoi(settings.Get("http_port").c_str()));
+ } else if (settings.IsSet("socks")) {
+ proxy->type = PROXY_SOCKS5;
+ proxy->address.SetIP(settings.Get("socks"));
+ proxy->address.SetPort(atoi(settings.Get("socks_port").c_str()));
+ } else if (settings.IsSet("ssl")) {
+ proxy->type = PROXY_HTTPS;
+ proxy->address.SetIP(settings.Get("ssl"));
+ proxy->address.SetPort(atoi(settings.Get("ssl_port").c_str()));
+ } else if (settings.IsSet("http")) {
+ proxy->type = PROXY_HTTPS;
+ proxy->address.SetIP(settings.Get("http"));
+ proxy->address.SetPort(atoi(settings.Get("http_port").c_str()));
+ }
+ } else if (settings.Get("type") == "2") {
+ // Browser is configured to get proxy settings from a given url.
+ proxy->autoconfig_url = settings.Get("autoconfig_url").c_str();
+ } else if (settings.Get("type") == "4") {
+ // Browser is configured to auto detect proxy config.
+ proxy->autodetect = true;
+ } else {
+ // No proxy set.
+ }
+ }
+ }
+ return success;
+}
+
+#ifdef WIN32 // Windows specific implementation for reading Internet
+ // Explorer proxy settings.
+
+void LogGetProxyFault() {
+ LOG_GLEM(LERROR, WINHTTP) << "WinHttpGetProxyForUrl faulted!!";
+}
+
+BOOL MyWinHttpGetProxyForUrl(pfnWinHttpGetProxyForUrl pWHGPFU,
+ HINTERNET hWinHttp, LPCWSTR url,
+ WINHTTP_AUTOPROXY_OPTIONS *options,
+ WINHTTP_PROXY_INFO *info) {
+ // WinHttpGetProxyForUrl() can call plugins which can crash.
+ // In the case of McAfee scriptproxy.dll, it does crash in
+ // older versions. Try to catch crashes here and treat as an
+ // error.
+ BOOL success = FALSE;
+
+#if (_HAS_EXCEPTIONS == 0)
+ __try {
+ success = pWHGPFU(hWinHttp, url, options, info);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ // This is a separate function to avoid
+ // Visual C++ error 2712 when compiling with C++ EH
+ LogGetProxyFault();
+ }
+#else
+ success = pWHGPFU(hWinHttp, url, options, info);
+#endif // (_HAS_EXCEPTIONS == 0)
+
+ return success;
+}
+
+bool IsDefaultBrowserFirefox() {
+ HKEY key;
+ LONG result = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command",
+ 0, KEY_READ, &key);
+ if (ERROR_SUCCESS != result)
+ return false;
+
+ wchar_t* value = NULL;
+ DWORD size, type;
+ result = RegQueryValueEx(key, L"", 0, &type, NULL, &size);
+ if (REG_SZ != type) {
+ result = ERROR_ACCESS_DENIED; // Any error is fine
+ } else if (ERROR_SUCCESS == result) {
+ value = new wchar_t[size+1];
+ BYTE* buffer = reinterpret_cast<BYTE*>(value);
+ result = RegQueryValueEx(key, L"", 0, &type, buffer, &size);
+ }
+ RegCloseKey(key);
+
+ bool success = false;
+ if (ERROR_SUCCESS == result) {
+ value[size] = L'\0';
+ for (size_t i = 0; i < size; ++i) {
+ value[i] = tolowercase(value[i]);
+ }
+ success = (NULL != strstr(value, L"firefox.exe"));
+ }
+ delete [] value;
+ return success;
+}
+
+bool GetWinHttpProxySettings(const char* url, ProxyInfo* proxy) {
+ HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll");
+ if (winhttp_handle == NULL) {
+ LOG(LS_ERROR) << "Failed to load winhttp.dll.";
+ return false;
+ }
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg;
+ memset(&iecfg, 0, sizeof(iecfg));
+ Url<char> purl(url);
+ pfnWinHttpGetIEProxyConfig pWHGIEPC =
+ reinterpret_cast<pfnWinHttpGetIEProxyConfig>(
+ GetProcAddress(winhttp_handle,
+ "WinHttpGetIEProxyConfigForCurrentUser"));
+ bool success = false;
+ if (pWHGIEPC && pWHGIEPC(&iecfg)) {
+ // We were read proxy config successfully.
+ success = true;
+ if (iecfg.fAutoDetect) {
+ proxy->autodetect = true;
+ }
+ if (iecfg.lpszAutoConfigUrl) {
+ proxy->autoconfig_url = ToUtf8(iecfg.lpszAutoConfigUrl);
+ GlobalFree(iecfg.lpszAutoConfigUrl);
+ }
+ if (iecfg.lpszProxyBypass) {
+ proxy->bypass_list = ToUtf8(iecfg.lpszProxyBypass);
+ GlobalFree(iecfg.lpszProxyBypass);
+ }
+ if (iecfg.lpszProxy) {
+ if (!ProxyListMatch(purl, proxy->bypass_list, ';')) {
+ ParseProxy(ToUtf8(iecfg.lpszProxy), proxy);
+ }
+ GlobalFree(iecfg.lpszProxy);
+ }
+ }
+ FreeLibrary(winhttp_handle);
+ return success;
+}
+
+// Uses the WinHTTP API to auto detect proxy for the given url. Firefox and IE
+// have slightly different option dialogs for proxy settings. In Firefox,
+// either a location of a proxy configuration file can be specified or auto
+// detection can be selected. In IE theese two options can be independently
+// selected. For the case where both options are selected (only IE) we try to
+// fetch the config file first, and if that fails we'll perform an auto
+// detection.
+//
+// Returns true if we successfully performed an auto detection not depending on
+// whether we found a proxy or not. Returns false on error.
+bool WinHttpAutoDetectProxyForUrl(const char* agent, const char* url,
+ ProxyInfo* proxy) {
+ Url<char> purl(url);
+ bool success = true;
+ HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll");
+ if (winhttp_handle == NULL) {
+ LOG(LS_ERROR) << "Failed to load winhttp.dll.";
+ return false;
+ }
+ pfnWinHttpOpen pWHO =
+ reinterpret_cast<pfnWinHttpOpen>(GetProcAddress(winhttp_handle,
+ "WinHttpOpen"));
+ pfnWinHttpCloseHandle pWHCH =
+ reinterpret_cast<pfnWinHttpCloseHandle>(
+ GetProcAddress(winhttp_handle, "WinHttpCloseHandle"));
+ pfnWinHttpGetProxyForUrl pWHGPFU =
+ reinterpret_cast<pfnWinHttpGetProxyForUrl>(
+ GetProcAddress(winhttp_handle, "WinHttpGetProxyForUrl"));
+ if (pWHO && pWHCH && pWHGPFU) {
+ if (HINTERNET hWinHttp = pWHO(ToUtf16(agent).c_str(),
+ WINHTTP_ACCESS_TYPE_NO_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0)) {
+ BOOL result = FALSE;
+ WINHTTP_PROXY_INFO info;
+ memset(&info, 0, sizeof(info));
+ if (proxy->autodetect) {
+ // Use DHCP and DNS to try to find any proxy to use.
+ WINHTTP_AUTOPROXY_OPTIONS options;
+ memset(&options, 0, sizeof(options));
+ options.fAutoLogonIfChallenged = TRUE;
+
+ options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT;
+ options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP
+ | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+ result = MyWinHttpGetProxyForUrl(
+ pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info);
+ }
+ if (!result && !proxy->autoconfig_url.empty()) {
+ // We have the location of a proxy config file. Download it and
+ // execute it to find proxy settings for our url.
+ WINHTTP_AUTOPROXY_OPTIONS options;
+ memset(&options, 0, sizeof(options));
+ memset(&info, 0, sizeof(info));
+ options.fAutoLogonIfChallenged = TRUE;
+
+ std::wstring autoconfig_url16((ToUtf16)(proxy->autoconfig_url));
+ options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
+ options.lpszAutoConfigUrl = autoconfig_url16.c_str();
+
+ result = MyWinHttpGetProxyForUrl(
+ pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info);
+ }
+ if (result) {
+ // Either the given auto config url was valid or auto
+ // detection found a proxy on this network.
+ if (info.lpszProxy) {
+ // TODO(oja): Does this bypass list differ from the list
+ // retreived from GetWinHttpProxySettings earlier?
+ if (info.lpszProxyBypass) {
+ proxy->bypass_list = ToUtf8(info.lpszProxyBypass);
+ GlobalFree(info.lpszProxyBypass);
+ } else {
+ proxy->bypass_list.clear();
+ }
+ if (!ProxyListMatch(purl, proxy->bypass_list, ';')) {
+ // Found proxy for this URL. If parsing the address turns
+ // out ok then we are successful.
+ success = ParseProxy(ToUtf8(info.lpszProxy), proxy);
+ }
+ GlobalFree(info.lpszProxy);
+ }
+ } else {
+ // We could not find any proxy for this url.
+ LOG(LS_INFO) << "No proxy detected for " << url;
+ }
+ pWHCH(hWinHttp);
+ }
+ } else {
+ LOG(LS_ERROR) << "Failed loading WinHTTP functions.";
+ success = false;
+ }
+ FreeLibrary(winhttp_handle);
+ return success;
+}
+
+#if 0 // Below functions currently not used.
+
+bool GetJsProxySettings(const char* url, ProxyInfo* proxy) {
+ Url<char> purl(url);
+ bool success = false;
+
+ if (HMODULE hModJS = LoadLibrary(_T("jsproxy.dll"))) {
+ pfnInternetGetProxyInfo pIGPI =
+ reinterpret_cast<pfnInternetGetProxyInfo>(
+ GetProcAddress(hModJS, "InternetGetProxyInfo"));
+ if (pIGPI) {
+ char proxy[256], host[256];
+ memset(proxy, 0, sizeof(proxy));
+ char * ptr = proxy;
+ DWORD proxylen = sizeof(proxy);
+ std::string surl = Utf8String(url);
+ DWORD hostlen = _snprintf(host, sizeof(host), "http%s://%S",
+ purl.secure() ? "s" : "", purl.server());
+ if (pIGPI(surl.data(), surl.size(), host, hostlen, &ptr, &proxylen)) {
+ LOG(INFO) << "Proxy: " << proxy;
+ } else {
+ LOG_GLE(INFO) << "InternetGetProxyInfo";
+ }
+ }
+ FreeLibrary(hModJS);
+ }
+ return success;
+}
+
+bool GetWmProxySettings(const char* url, ProxyInfo* proxy) {
+ Url<char> purl(url);
+ bool success = false;
+
+ INSNetSourceCreator * nsc = 0;
+ HRESULT hr = CoCreateInstance(CLSID_ClientNetManager, 0, CLSCTX_ALL,
+ IID_INSNetSourceCreator, (LPVOID *) &nsc);
+ if (SUCCEEDED(hr)) {
+ if (SUCCEEDED(hr = nsc->Initialize())) {
+ VARIANT dispatch;
+ VariantInit(&dispatch);
+ if (SUCCEEDED(hr = nsc->GetNetSourceAdminInterface(L"http", &dispatch))) {
+ IWMSInternalAdminNetSource * ians = 0;
+ if (SUCCEEDED(hr = dispatch.pdispVal->QueryInterface(
+ IID_IWMSInternalAdminNetSource, (LPVOID *) &ians))) {
+ _bstr_t host(purl.server());
+ BSTR proxy = 0;
+ BOOL bProxyEnabled = FALSE;
+ DWORD port, context = 0;
+ if (SUCCEEDED(hr = ians->FindProxyForURL(
+ L"http", host, &bProxyEnabled, &proxy, &port, &context))) {
+ success = true;
+ if (bProxyEnabled) {
+ _bstr_t sproxy = proxy;
+ proxy->ptype = PT_HTTPS;
+ proxy->host = sproxy;
+ proxy->port = port;
+ }
+ }
+ SysFreeString(proxy);
+ if (FAILED(hr = ians->ShutdownProxyContext(context))) {
+ LOG(LS_INFO) << "IWMSInternalAdminNetSource::ShutdownProxyContext"
+ << "failed: " << hr;
+ }
+ ians->Release();
+ }
+ }
+ VariantClear(&dispatch);
+ if (FAILED(hr = nsc->Shutdown())) {
+ LOG(LS_INFO) << "INSNetSourceCreator::Shutdown failed: " << hr;
+ }
+ }
+ nsc->Release();
+ }
+ return success;
+}
+
+bool GetIePerConnectionProxySettings(const char* url, ProxyInfo* proxy) {
+ Url<char> purl(url);
+ bool success = false;
+
+ INTERNET_PER_CONN_OPTION_LIST list;
+ INTERNET_PER_CONN_OPTION options[3];
+ memset(&list, 0, sizeof(list));
+ memset(&options, 0, sizeof(options));
+
+ list.dwSize = sizeof(list);
+ list.dwOptionCount = 3;
+ list.pOptions = options;
+ options[0].dwOption = INTERNET_PER_CONN_FLAGS;
+ options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
+ options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
+ DWORD dwSize = sizeof(list);
+
+ if (!InternetQueryOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &list,
+ &dwSize)) {
+ LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError();
+ } else if ((options[0].Value.dwValue & PROXY_TYPE_PROXY) != 0) {
+ success = true;
+ if (!ProxyListMatch(purl, nonnull(options[2].Value.pszValue), _T(';'))) {
+ ParseProxy(nonnull(options[1].Value.pszValue), proxy);
+ }
+ } else if ((options[0].Value.dwValue & PROXY_TYPE_DIRECT) != 0) {
+ success = true;
+ } else {
+ LOG(LS_INFO) << "unknown internet access type: "
+ << options[0].Value.dwValue;
+ }
+ if (options[1].Value.pszValue) {
+ GlobalFree(options[1].Value.pszValue);
+ }
+ if (options[2].Value.pszValue) {
+ GlobalFree(options[2].Value.pszValue);
+ }
+ return success;
+}
+
+#endif // 0
+
+// Uses the InternetQueryOption function to retrieve proxy settings
+// from the registry. This will only give us the 'static' settings,
+// ie, not any information about auto config etc.
+bool GetIeLanProxySettings(const char* url, ProxyInfo* proxy) {
+ Url<char> purl(url);
+ bool success = false;
+
+ wchar_t buffer[1024];
+ memset(buffer, 0, sizeof(buffer));
+ INTERNET_PROXY_INFO * info = reinterpret_cast<INTERNET_PROXY_INFO *>(buffer);
+ DWORD dwSize = sizeof(buffer);
+
+ if (!InternetQueryOption(0, INTERNET_OPTION_PROXY, info, &dwSize)) {
+ LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError();
+ } else if (info->dwAccessType == INTERNET_OPEN_TYPE_DIRECT) {
+ success = true;
+ } else if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
+ success = true;
+ if (!ProxyListMatch(purl, nonnull(reinterpret_cast<const char*>(
+ info->lpszProxyBypass)), ' ')) {
+ ParseProxy(nonnull(reinterpret_cast<const char*>(info->lpszProxy)),
+ proxy);
+ }
+ } else {
+ LOG(LS_INFO) << "unknown internet access type: " << info->dwAccessType;
+ }
+ return success;
+}
+
+bool GetIeProxySettings(const char* agent, const char* url, ProxyInfo* proxy) {
+ bool success = GetWinHttpProxySettings(url, proxy);
+ if (!success) {
+ // TODO(oja): Should always call this if no proxy were detected by
+ // GetWinHttpProxySettings?
+ // WinHttp failed. Try using the InternetOptionQuery method instead.
+ return GetIeLanProxySettings(url, proxy);
+ }
+ return true;
+}
+
+#endif // WIN32
+
+#ifdef OSX // OSX specific implementation for reading system wide
+ // proxy settings.
+
+bool p_getProxyInfoForTypeFromDictWithKeys(ProxyInfo* proxy,
+ ProxyType type,
+ const CFDictionaryRef proxyDict,
+ const CFStringRef enabledKey,
+ const CFStringRef hostKey,
+ const CFStringRef portKey) {
+ // whether or not we set up the proxy info.
+ bool result = false;
+
+ // we use this as a scratch variable for determining if operations
+ // succeeded.
+ bool converted = false;
+
+ // the data we need to construct the SocketAddress for the proxy.
+ std::string hostname;
+ int port;
+
+ if ((proxyDict != NULL) &&
+ (CFGetTypeID(proxyDict) == CFDictionaryGetTypeID())) {
+ // CoreFoundation stuff that we'll have to get from
+ // the dictionaries and interpret or convert into more usable formats.
+ CFNumberRef enabledCFNum;
+ CFNumberRef portCFNum;
+ CFStringRef hostCFStr;
+
+ enabledCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, enabledKey);
+
+ if (p_isCFNumberTrue(enabledCFNum)) {
+ // let's see if we can get the address and port.
+ hostCFStr = (CFStringRef)CFDictionaryGetValue(proxyDict, hostKey);
+ converted = p_convertHostCFStringRefToCPPString(hostCFStr, hostname);
+ if (converted) {
+ portCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, portKey);
+ converted = p_convertCFNumberToInt(portCFNum, &port);
+ if (converted) {
+ // we have something enabled, with a hostname and a port.
+ // That's sufficient to set up the proxy info.
+ proxy->type = type;
+ proxy->address.SetIP(hostname);
+ proxy->address.SetPort(port);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+// Looks for proxy information in the given dictionary,
+// return true if it found sufficient information to define one,
+// false otherwise. This is guaranteed to not change the values in proxy
+// unless a full-fledged proxy description was discovered in the dictionary.
+// However, at the present time this does not support username or password.
+// Checks first for a SOCKS proxy, then for HTTPS, then HTTP.
+bool GetMacProxySettingsFromDictionary(ProxyInfo* proxy,
+ const CFDictionaryRef proxyDict) {
+ // the function result.
+ bool gotProxy = false;
+
+
+ // first we see if there's a SOCKS proxy in place.
+ gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy,
+ PROXY_SOCKS5,
+ proxyDict,
+ kSCPropNetProxiesSOCKSEnable,
+ kSCPropNetProxiesSOCKSProxy,
+ kSCPropNetProxiesSOCKSPort);
+
+ if (!gotProxy) {
+ // okay, no SOCKS proxy, let's look for https.
+ gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy,
+ PROXY_HTTPS,
+ proxyDict,
+ kSCPropNetProxiesHTTPSEnable,
+ kSCPropNetProxiesHTTPSProxy,
+ kSCPropNetProxiesHTTPSPort);
+ if (!gotProxy) {
+ // Finally, try HTTP proxy. Note that flute doesn't
+ // differentiate between HTTPS and HTTP, hence we are using the
+ // same flute type here, ie. PROXY_HTTPS.
+ gotProxy = p_getProxyInfoForTypeFromDictWithKeys(
+ proxy, PROXY_HTTPS, proxyDict, kSCPropNetProxiesHTTPEnable,
+ kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort);
+ }
+ }
+ return gotProxy;
+}
+
+bool p_putPasswordInProxyInfo(ProxyInfo* proxy) {
+ bool result = true; // by default we assume we're good.
+ // for all we know there isn't any password. We'll set to false
+ // if we find a problem.
+
+ // Ask the keychain for an internet password search for the given protocol.
+ OSStatus oss = 0;
+ SecKeychainAttributeList attrList;
+ attrList.count = 3;
+ SecKeychainAttribute attributes[3];
+ attrList.attr = attributes;
+
+ attributes[0].tag = kSecProtocolItemAttr;
+ attributes[0].length = sizeof(SecProtocolType);
+ SecProtocolType protocol;
+ switch (proxy->type) {
+ case PROXY_HTTPS :
+ protocol = kSecProtocolTypeHTTPS;
+ break;
+ case PROXY_SOCKS5 :
+ protocol = kSecProtocolTypeSOCKS;
+ break;
+ default :
+ LOG(LS_ERROR) << "asked for proxy password for unknown proxy type.";
+ result = false;
+ break;
+ }
+ attributes[0].data = &protocol;
+
+ UInt32 port = proxy->address.port();
+ attributes[1].tag = kSecPortItemAttr;
+ attributes[1].length = sizeof(UInt32);
+ attributes[1].data = &port;
+
+ std::string ip = proxy->address.IPAsString();
+ attributes[2].tag = kSecServerItemAttr;
+ attributes[2].length = ip.length();
+ attributes[2].data = const_cast<char*>(ip.c_str());
+
+ if (result) {
+ LOG(LS_INFO) << "trying to get proxy username/password";
+ SecKeychainSearchRef sref;
+ oss = SecKeychainSearchCreateFromAttributes(NULL,
+ kSecInternetPasswordItemClass,
+ &attrList, &sref);
+ if (0 == oss) {
+ LOG(LS_INFO) << "SecKeychainSearchCreateFromAttributes was good";
+ // Get the first item, if there is one.
+ SecKeychainItemRef iref;
+ oss = SecKeychainSearchCopyNext(sref, &iref);
+ if (0 == oss) {
+ LOG(LS_INFO) << "...looks like we have the username/password data";
+ // If there is, get the username and the password.
+
+ SecKeychainAttributeInfo attribsToGet;
+ attribsToGet.count = 1;
+ UInt32 tag = kSecAccountItemAttr;
+ UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
+ void *data;
+ UInt32 length;
+ SecKeychainAttributeList *localList;
+
+ attribsToGet.tag = &tag;
+ attribsToGet.format = &format;
+ OSStatus copyres = SecKeychainItemCopyAttributesAndData(iref,
+ &attribsToGet,
+ NULL,
+ &localList,
+ &length,
+ &data);
+ if (0 == copyres) {
+ LOG(LS_INFO) << "...and we can pull it out.";
+ // now, we know from experimentation (sadly not from docs)
+ // that the username is in the local attribute list,
+ // and the password in the data,
+ // both without null termination but with info on their length.
+ // grab the password from the data.
+ std::string password;
+ password.append(static_cast<const char*>(data), length);
+
+ // make the password into a CryptString
+ // huh, at the time of writing, you can't.
+ // so we'll skip that for now and come back to it later.
+
+ // now put the username in the proxy.
+ if (1 <= localList->attr->length) {
+ proxy->username.append(
+ static_cast<const char*>(localList->attr->data),
+ localList->attr->length);
+ LOG(LS_INFO) << "username is " << proxy->username;
+ } else {
+ LOG(LS_ERROR) << "got keychain entry with no username";
+ result = false;
+ }
+ } else {
+ LOG(LS_ERROR) << "couldn't copy info from keychain.";
+ result = false;
+ }
+ SecKeychainItemFreeAttributesAndData(localList, data);
+ } else if (errSecItemNotFound == oss) {
+ LOG(LS_INFO) << "...username/password info not found";
+ } else {
+ // oooh, neither 0 nor itemNotFound.
+ LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss;
+ result = false;
+ }
+ } else if (errSecItemNotFound == oss) { // noop
+ } else {
+ // oooh, neither 0 nor itemNotFound.
+ LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss;
+ result = false;
+ }
+ }
+
+ return result;
+}
+
+bool GetMacProxySettings(ProxyInfo* proxy) {
+ // based on the Apple Technical Q&A QA1234
+ // http://developer.apple.com/qa/qa2001/qa1234.html
+ CFDictionaryRef proxyDict = SCDynamicStoreCopyProxies(NULL);
+ bool result = false;
+
+ if (proxyDict != NULL) {
+ // sending it off to another function makes it easier to unit test
+ // since we can make our own dictionary to hand to that function.
+ result = GetMacProxySettingsFromDictionary(proxy, proxyDict);
+
+ if (result) {
+ result = p_putPasswordInProxyInfo(proxy);
+ }
+
+ // We created the dictionary with something that had the
+ // word 'copy' in it, so we have to release it, according
+ // to the Carbon memory management standards.
+ CFRelease(proxyDict);
+ } else {
+ LOG(LS_ERROR) << "SCDynamicStoreCopyProxies failed";
+ }
+
+ return result;
+}
+#endif // OSX
+
+bool AutoDetectProxySettings(const char* agent, const char* url,
+ ProxyInfo* proxy) {
+#ifdef WIN32
+ return WinHttpAutoDetectProxyForUrl(agent, url, proxy);
+#else
+ LOG(LS_WARNING) << "Proxy auto-detection not implemented for this platform";
+ return false;
+#endif
+}
+
+bool GetSystemDefaultProxySettings(const char* agent, const char* url,
+ ProxyInfo* proxy) {
+#ifdef WIN32
+ return GetIeProxySettings(agent, url, proxy);
+#elif OSX
+ return GetMacProxySettings(proxy);
+#else
+ // TODO(oja): Get System settings if browser is not firefox.
+ return GetFirefoxProxySettings(url, proxy);
+#endif
+}
+
+bool GetProxySettingsForUrl(const char* agent, const char* url,
+ ProxyInfo& proxy, bool long_operation) {
+ UserAgent a = GetAgent(agent);
+ bool result;
+ switch (a) {
+ case UA_FIREFOX: {
+ result = GetFirefoxProxySettings(url, &proxy);
+ break;
+ }
+#ifdef WIN32
+ case UA_INTERNETEXPLORER:
+ result = GetIeProxySettings(agent, url, &proxy);
+ break;
+ case UA_UNKNOWN:
+ // Agent not defined, check default browser.
+ if (IsDefaultBrowserFirefox()) {
+ result = GetFirefoxProxySettings(url, &proxy);
+ } else {
+ result = GetIeProxySettings(agent, url, &proxy);
+ }
+ break;
+#endif // WIN32
+ default:
+ result = GetSystemDefaultProxySettings(agent, url, &proxy);
+ break;
+ }
+
+ // TODO(oja): Consider using the 'long_operation' parameter to
+ // decide whether to do the auto detection.
+ if (result && (proxy.autodetect ||
+ !proxy.autoconfig_url.empty())) {
+ // Use WinHTTP to auto detect proxy for us.
+ result = AutoDetectProxySettings(agent, url, &proxy);
+ if (!result) {
+ // Either auto detection is not supported or we simply didn't
+ // find any proxy, reset type.
+ proxy.type = talk_base::PROXY_NONE;
+ }
+ }
+ return result;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/proxydetect.h b/third_party/libjingle/source/talk/base/proxydetect.h
new file mode 100644
index 0000000..36ee672
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/proxydetect.h
@@ -0,0 +1,48 @@
+/*
+ * libjingle
+ * Copyright 2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROXYDETECT_H_
+#define _PROXYDETECT_H_
+
+#include "talk/base/proxyinfo.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+namespace talk_base {
+// Auto-detect the proxy server. Returns true if a proxy is configured,
+// although hostname may be empty if the proxy is not required for
+// the given URL.
+
+bool GetProxySettingsForUrl(const char* agent, const char* url,
+ talk_base::ProxyInfo& proxy,
+ bool long_operation = false);
+
+} // namespace talk_base
+
+#endif // _PROXYDETECT_H_
diff --git a/third_party/libjingle/source/talk/base/proxyinfo.cc b/third_party/libjingle/source/talk/base/proxyinfo.cc
new file mode 100644
index 0000000..1d9c588
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/proxyinfo.cc
@@ -0,0 +1,37 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/proxyinfo.h"
+
+namespace talk_base {
+
+const char * ProxyToString(ProxyType proxy) {
+ const char * const PROXY_NAMES[] = { "none", "https", "socks5", "unknown" };
+ return PROXY_NAMES[proxy];
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/proxyinfo.h b/third_party/libjingle/source/talk/base/proxyinfo.h
new file mode 100644
index 0000000..9e28f1a
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/proxyinfo.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 TALK_BASE_PROXYINFO_H__
+#define TALK_BASE_PROXYINFO_H__
+
+#include <string>
+#include "talk/base/socketaddress.h"
+#include "talk/base/cryptstring.h"
+
+namespace talk_base {
+
+enum ProxyType {
+ PROXY_NONE,
+ PROXY_HTTPS,
+ PROXY_SOCKS5,
+ PROXY_UNKNOWN
+};
+const char * ProxyToString(ProxyType proxy);
+
+struct ProxyInfo {
+ ProxyType type;
+ SocketAddress address;
+ std::string autoconfig_url;
+ bool autodetect;
+ std::string bypass_list;
+ std::string username;
+ CryptString password;
+
+ ProxyInfo() : type(PROXY_NONE), autodetect(false) { }
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_PROXYINFO_H__
diff --git a/third_party/libjingle/source/talk/base/schanneladapter.cc b/third_party/libjingle/source/talk/base/schanneladapter.cc
new file mode 100644
index 0000000..01aa9ce
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/schanneladapter.cc
@@ -0,0 +1,722 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/win32.h"
+#define SECURITY_WIN32
+#include <security.h>
+#include <schannel.h>
+
+#include <iomanip>
+#include <vector>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/schanneladapter.h"
+#include "talk/base/sec_buffer.h"
+#include "talk/base/thread.h"
+
+namespace talk_base {
+
+/////////////////////////////////////////////////////////////////////////////
+// SChannelAdapter
+/////////////////////////////////////////////////////////////////////////////
+
+extern const ConstantLabel SECURITY_ERRORS[];
+
+const ConstantLabel SCHANNEL_BUFFER_TYPES[] = {
+ KLABEL(SECBUFFER_EMPTY), // 0
+ KLABEL(SECBUFFER_DATA), // 1
+ KLABEL(SECBUFFER_TOKEN), // 2
+ KLABEL(SECBUFFER_PKG_PARAMS), // 3
+ KLABEL(SECBUFFER_MISSING), // 4
+ KLABEL(SECBUFFER_EXTRA), // 5
+ KLABEL(SECBUFFER_STREAM_TRAILER), // 6
+ KLABEL(SECBUFFER_STREAM_HEADER), // 7
+ KLABEL(SECBUFFER_MECHLIST), // 11
+ KLABEL(SECBUFFER_MECHLIST_SIGNATURE), // 12
+ KLABEL(SECBUFFER_TARGET), // 13
+ KLABEL(SECBUFFER_CHANNEL_BINDINGS), // 14
+ LASTLABEL
+};
+
+void DescribeBuffer(LoggingSeverity severity, const char* prefix,
+ const SecBuffer& sb) {
+ LOG_V(severity)
+ << prefix
+ << "(" << sb.cbBuffer
+ << ", " << FindLabel(sb.BufferType & ~SECBUFFER_ATTRMASK,
+ SCHANNEL_BUFFER_TYPES)
+ << ", " << sb.pvBuffer << ")";
+}
+
+void DescribeBuffers(LoggingSeverity severity, const char* prefix,
+ const SecBufferDesc* sbd) {
+ if (!LOG_CHECK_LEVEL_V(severity))
+ return;
+ LOG_V(severity) << prefix << "(";
+ for (size_t i=0; i<sbd->cBuffers; ++i) {
+ DescribeBuffer(severity, " ", sbd->pBuffers[i]);
+ }
+ LOG_V(severity) << ")";
+}
+
+const ULONG SSL_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;
+
+typedef std::vector<char> SChannelBuffer;
+
+struct SChannelAdapter::SSLImpl {
+ CredHandle cred;
+ CtxtHandle ctx;
+ bool cred_init, ctx_init;
+ SChannelBuffer inbuf, outbuf, readable;
+ SecPkgContext_StreamSizes sizes;
+
+ SSLImpl() : cred_init(false), ctx_init(false) { }
+};
+
+SChannelAdapter::SChannelAdapter(AsyncSocket* socket)
+ : SSLAdapter(socket), state_(SSL_NONE),
+ restartable_(false), signal_close_(false), message_pending_(false),
+ impl_(new SSLImpl) {
+}
+
+SChannelAdapter::~SChannelAdapter() {
+ Cleanup();
+}
+
+int
+SChannelAdapter::StartSSL(const char* hostname, bool restartable) {
+ if (state_ != SSL_NONE)
+ return ERROR_ALREADY_INITIALIZED;
+
+ ssl_host_name_ = hostname;
+ restartable_ = restartable;
+
+ if (socket_->GetState() != Socket::CS_CONNECTED) {
+ state_ = SSL_WAIT;
+ return 0;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ Error("BeginSSL", err, false);
+ return err;
+ }
+
+ return 0;
+}
+
+int
+SChannelAdapter::BeginSSL() {
+ LOG(LS_VERBOSE) << "BeginSSL: " << ssl_host_name_;
+ ASSERT(state_ == SSL_CONNECTING);
+
+ SECURITY_STATUS ret;
+
+ SCHANNEL_CRED sc_cred = { 0 };
+ sc_cred.dwVersion = SCHANNEL_CRED_VERSION;
+ //sc_cred.dwMinimumCipherStrength = 128; // Note: use system default
+ sc_cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_AUTO_CRED_VALIDATION;
+
+ ret = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL,
+ &sc_cred, NULL, NULL, &impl_->cred, NULL);
+ if (ret != SEC_E_OK) {
+ LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ return ret;
+ }
+ impl_->cred_init = true;
+
+ if (LOG_CHECK_LEVEL(LS_VERBOSE)) {
+ SecPkgCred_CipherStrengths cipher_strengths = { 0 };
+ ret = QueryCredentialsAttributes(&impl_->cred,
+ SECPKG_ATTR_CIPHER_STRENGTHS,
+ &cipher_strengths);
+ if (SUCCEEDED(ret)) {
+ LOG(LS_VERBOSE) << "SChannel cipher strength: "
+ << cipher_strengths.dwMinimumCipherStrength << " - "
+ << cipher_strengths.dwMaximumCipherStrength;
+ }
+
+ SecPkgCred_SupportedAlgs supported_algs = { 0 };
+ ret = QueryCredentialsAttributes(&impl_->cred,
+ SECPKG_ATTR_SUPPORTED_ALGS,
+ &supported_algs);
+ if (SUCCEEDED(ret)) {
+ LOG(LS_VERBOSE) << "SChannel supported algorithms:";
+ for (DWORD i=0; i<supported_algs.cSupportedAlgs; ++i) {
+ ALG_ID alg_id = supported_algs.palgSupportedAlgs[i];
+ PCCRYPT_OID_INFO oinfo = CryptFindOIDInfo(CRYPT_OID_INFO_ALGID_KEY,
+ &alg_id, 0);
+ LPCWSTR alg_name = (NULL != oinfo) ? oinfo->pwszName : L"Unknown";
+ LOG(LS_VERBOSE) << " " << ToUtf8(alg_name) << " (" << alg_id << ")";
+ }
+ CSecBufferBase::FreeSSPI(supported_algs.palgSupportedAlgs);
+ }
+ }
+
+ ULONG flags = SSL_FLAGS_DEFAULT, ret_flags = 0;
+ if (ignore_bad_cert())
+ flags |= ISC_REQ_MANUAL_CRED_VALIDATION;
+
+ CSecBufferBundle<2, CSecBufferBase::FreeSSPI> sb_out;
+ ret = InitializeSecurityContextA(&impl_->cred, NULL,
+ const_cast<char*>(ssl_host_name_.c_str()),
+ flags, 0, 0, NULL, 0,
+ &impl_->ctx, sb_out.desc(),
+ &ret_flags, NULL);
+ if (SUCCEEDED(ret))
+ impl_->ctx_init = true;
+ return ProcessContext(ret, NULL, sb_out.desc());
+}
+
+int
+SChannelAdapter::ContinueSSL() {
+ LOG(LS_VERBOSE) << "ContinueSSL";
+ ASSERT(state_ == SSL_CONNECTING);
+
+ SECURITY_STATUS ret;
+
+ CSecBufferBundle<2> sb_in;
+ sb_in[0].BufferType = SECBUFFER_TOKEN;
+ sb_in[0].cbBuffer = static_cast<unsigned long>(impl_->inbuf.size());
+ sb_in[0].pvBuffer = &impl_->inbuf[0];
+ //DescribeBuffers(LS_VERBOSE, "Input Buffer ", sb_in.desc());
+
+ ULONG flags = SSL_FLAGS_DEFAULT, ret_flags = 0;
+ if (ignore_bad_cert())
+ flags |= ISC_REQ_MANUAL_CRED_VALIDATION;
+
+ CSecBufferBundle<2, CSecBufferBase::FreeSSPI> sb_out;
+ ret = InitializeSecurityContextA(&impl_->cred, &impl_->ctx,
+ const_cast<char*>(ssl_host_name_.c_str()),
+ flags, 0, 0, sb_in.desc(), 0,
+ NULL, sb_out.desc(),
+ &ret_flags, NULL);
+ return ProcessContext(ret, sb_in.desc(), sb_out.desc());
+}
+
+int
+SChannelAdapter::ProcessContext(long int status, _SecBufferDesc* sbd_in,
+ _SecBufferDesc* sbd_out) {
+ LoggingSeverity level = LS_ERROR;
+ if ((status == SEC_E_OK)
+ || (status != SEC_I_CONTINUE_NEEDED)
+ || (status != SEC_E_INCOMPLETE_MESSAGE)) {
+ level = LS_VERBOSE; // Expected messages
+ }
+ LOG_V(level)
+ << "InitializeSecurityContext error: "
+ << ErrorName(status, SECURITY_ERRORS);
+ //if (sbd_in)
+ // DescribeBuffers(LS_VERBOSE, "Input Buffer ", sbd_in);
+ //if (sbd_out)
+ // DescribeBuffers(LS_VERBOSE, "Output Buffer ", sbd_out);
+
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ // Wait for more input from server.
+ return Flush();
+ }
+
+ if (FAILED(status)) {
+ // We can't continue. Common errors:
+ // SEC_E_CERT_EXPIRED - Typically, this means the computer clock is wrong.
+ return status;
+ }
+
+ // Note: we check both input and output buffers for SECBUFFER_EXTRA.
+ // Experience shows it appearing in the input, but the documentation claims
+ // it should appear in the output.
+ size_t extra = 0;
+ if (sbd_in) {
+ for (size_t i=0; i<sbd_in->cBuffers; ++i) {
+ SecBuffer& buffer = sbd_in->pBuffers[i];
+ if (buffer.BufferType == SECBUFFER_EXTRA) {
+ extra += buffer.cbBuffer;
+ }
+ }
+ }
+ if (sbd_out) {
+ for (size_t i=0; i<sbd_out->cBuffers; ++i) {
+ SecBuffer& buffer = sbd_out->pBuffers[i];
+ if (buffer.BufferType == SECBUFFER_EXTRA) {
+ extra += buffer.cbBuffer;
+ } else if (buffer.BufferType == SECBUFFER_TOKEN) {
+ impl_->outbuf.insert(impl_->outbuf.end(),
+ reinterpret_cast<char*>(buffer.pvBuffer),
+ reinterpret_cast<char*>(buffer.pvBuffer) + buffer.cbBuffer);
+ }
+ }
+ }
+
+ if (extra) {
+ ASSERT(extra <= impl_->inbuf.size());
+ size_t consumed = impl_->inbuf.size() - extra;
+ memmove(&impl_->inbuf[0], &impl_->inbuf[consumed], extra);
+ impl_->inbuf.resize(extra);
+ } else {
+ impl_->inbuf.clear();
+ }
+
+ if (SEC_I_CONTINUE_NEEDED == status) {
+ // Send data to server and wait for response.
+ // Note: ContinueSSL will result in a Flush, anyway.
+ return impl_->inbuf.empty() ? Flush() : ContinueSSL();
+ }
+
+ if (SEC_E_OK == status) {
+ LOG(LS_VERBOSE) << "QueryContextAttributes";
+ status = QueryContextAttributes(&impl_->ctx, SECPKG_ATTR_STREAM_SIZES,
+ &impl_->sizes);
+ if (FAILED(status)) {
+ LOG(LS_ERROR) << "QueryContextAttributes error: "
+ << ErrorName(status, SECURITY_ERRORS);
+ return status;
+ }
+
+ state_ = SSL_CONNECTED;
+
+ if (int err = DecryptData()) {
+ return err;
+ } else if (int err = Flush()) {
+ return err;
+ } else {
+ // If we decrypted any data, queue up a notification here
+ PostEvent();
+ // Signal our connectedness
+ AsyncSocketAdapter::OnConnectEvent(this);
+ }
+ return 0;
+ }
+
+ if (SEC_I_INCOMPLETE_CREDENTIALS == status) {
+ // We don't support client authentication in schannel.
+ return status;
+ }
+
+ // We don't expect any other codes
+ ASSERT(false);
+ return status;
+}
+
+int
+SChannelAdapter::DecryptData() {
+ SChannelBuffer& inbuf = impl_->inbuf;
+ SChannelBuffer& readable = impl_->readable;
+
+ while (!inbuf.empty()) {
+ CSecBufferBundle<4> in_buf;
+ in_buf[0].BufferType = SECBUFFER_DATA;
+ in_buf[0].cbBuffer = static_cast<unsigned long>(inbuf.size());
+ in_buf[0].pvBuffer = &inbuf[0];
+
+ //DescribeBuffers(LS_VERBOSE, "Decrypt In ", in_buf.desc());
+ SECURITY_STATUS status = DecryptMessage(&impl_->ctx, in_buf.desc(), 0, 0);
+ //DescribeBuffers(LS_VERBOSE, "Decrypt Out ", in_buf.desc());
+
+ // Note: We are explicitly treating SEC_E_OK, SEC_I_CONTEXT_EXPIRED, and
+ // any other successful results as continue.
+ if (SUCCEEDED(status)) {
+ size_t data_len = 0, extra_len = 0;
+ for (size_t i=0; i<in_buf.desc()->cBuffers; ++i) {
+ if (in_buf[i].BufferType == SECBUFFER_DATA) {
+ data_len += in_buf[i].cbBuffer;
+ readable.insert(readable.end(),
+ reinterpret_cast<char*>(in_buf[i].pvBuffer),
+ reinterpret_cast<char*>(in_buf[i].pvBuffer) + in_buf[i].cbBuffer);
+ } else if (in_buf[i].BufferType == SECBUFFER_EXTRA) {
+ extra_len += in_buf[i].cbBuffer;
+ }
+ }
+ // There is a bug on Win2K where SEC_I_CONTEXT_EXPIRED is misclassified.
+ if ((data_len == 0) && (inbuf[0] == 0x15)) {
+ status = SEC_I_CONTEXT_EXPIRED;
+ }
+ if (extra_len) {
+ size_t consumed = inbuf.size() - extra_len;
+ memmove(&inbuf[0], &inbuf[consumed], extra_len);
+ inbuf.resize(extra_len);
+ } else {
+ inbuf.clear();
+ }
+ // TODO: Handle SEC_I_CONTEXT_EXPIRED to do clean shutdown
+ if (status != SEC_E_OK) {
+ LOG(LS_INFO) << "DecryptMessage returned continuation code: "
+ << ErrorName(status, SECURITY_ERRORS);
+ }
+ continue;
+ }
+
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ break;
+ } else {
+ return status;
+ }
+ }
+
+ return 0;
+}
+
+void
+SChannelAdapter::Cleanup() {
+ if (impl_->ctx_init)
+ DeleteSecurityContext(&impl_->ctx);
+ if (impl_->cred_init)
+ FreeCredentialsHandle(&impl_->cred);
+ delete impl_;
+}
+
+void
+SChannelAdapter::PostEvent() {
+ // Check if there's anything notable to signal
+ if (impl_->readable.empty() && !signal_close_)
+ return;
+
+ // Only one post in the queue at a time
+ if (message_pending_)
+ return;
+
+ if (Thread* thread = Thread::Current()) {
+ message_pending_ = true;
+ thread->Post(this);
+ } else {
+ LOG(LS_ERROR) << "No thread context available for SChannelAdapter";
+ ASSERT(false);
+ }
+}
+
+void
+SChannelAdapter::Error(const char* context, int err, bool signal) {
+ LOG(LS_WARNING) << "SChannelAdapter::Error("
+ << context << ", "
+ << ErrorName(err, SECURITY_ERRORS) << ")";
+ state_ = SSL_ERROR;
+ SetError(err);
+ if (signal)
+ AsyncSocketAdapter::OnCloseEvent(this, err);
+}
+
+int
+SChannelAdapter::Read() {
+ char buffer[4096];
+ SChannelBuffer& inbuf = impl_->inbuf;
+ while (true) {
+ int ret = AsyncSocketAdapter::Recv(buffer, sizeof(buffer));
+ if (ret > 0) {
+ inbuf.insert(inbuf.end(), buffer, buffer + ret);
+ } else if (GetError() == EWOULDBLOCK) {
+ return 0; // Blocking
+ } else {
+ return GetError();
+ }
+ }
+}
+
+int
+SChannelAdapter::Flush() {
+ int result = 0;
+ size_t pos = 0;
+ SChannelBuffer& outbuf = impl_->outbuf;
+ while (pos < outbuf.size()) {
+ int sent = AsyncSocketAdapter::Send(&outbuf[pos], outbuf.size() - pos);
+ if (sent > 0) {
+ pos += sent;
+ } else if (GetError() == EWOULDBLOCK) {
+ break; // Blocking
+ } else {
+ result = GetError();
+ break;
+ }
+ }
+ if (int remainder = outbuf.size() - pos) {
+ memmove(&outbuf[0], &outbuf[pos], remainder);
+ outbuf.resize(remainder);
+ } else {
+ outbuf.clear();
+ }
+ return result;
+}
+
+//
+// AsyncSocket Implementation
+//
+
+int
+SChannelAdapter::Send(const void* pv, size_t cb) {
+ switch (state_) {
+ case SSL_NONE:
+ return AsyncSocketAdapter::Send(pv, cb);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ default:
+ return SOCKET_ERROR;
+ }
+
+ size_t written = 0;
+ SChannelBuffer& outbuf = impl_->outbuf;
+ while (written < cb) {
+ const size_t encrypt_len = std::min<size_t>(cb - written,
+ impl_->sizes.cbMaximumMessage);
+
+ CSecBufferBundle<4> out_buf;
+ out_buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ out_buf[0].cbBuffer = impl_->sizes.cbHeader;
+ out_buf[1].BufferType = SECBUFFER_DATA;
+ out_buf[1].cbBuffer = static_cast<unsigned long>(encrypt_len);
+ out_buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ out_buf[2].cbBuffer = impl_->sizes.cbTrailer;
+
+ size_t packet_len = out_buf[0].cbBuffer
+ + out_buf[1].cbBuffer
+ + out_buf[2].cbBuffer;
+
+ SChannelBuffer message;
+ message.resize(packet_len);
+ out_buf[0].pvBuffer = &message[0];
+ out_buf[1].pvBuffer = &message[out_buf[0].cbBuffer];
+ out_buf[2].pvBuffer = &message[out_buf[0].cbBuffer + out_buf[1].cbBuffer];
+
+ memcpy(out_buf[1].pvBuffer,
+ static_cast<const char*>(pv) + written,
+ encrypt_len);
+
+ //DescribeBuffers(LS_VERBOSE, "Encrypt In ", out_buf.desc());
+ SECURITY_STATUS res = EncryptMessage(&impl_->ctx, 0, out_buf.desc(), 0);
+ //DescribeBuffers(LS_VERBOSE, "Encrypt Out ", out_buf.desc());
+
+ if (FAILED(res)) {
+ Error("EncryptMessage", res, false);
+ return SOCKET_ERROR;
+ }
+
+ // We assume that the header and data segments do not change length,
+ // or else encrypting the concatenated packet in-place is wrong.
+ ASSERT(out_buf[0].cbBuffer == impl_->sizes.cbHeader);
+ ASSERT(out_buf[1].cbBuffer == static_cast<unsigned long>(encrypt_len));
+
+ // However, the length of the trailer may change due to padding.
+ ASSERT(out_buf[2].cbBuffer <= impl_->sizes.cbTrailer);
+
+ packet_len = out_buf[0].cbBuffer
+ + out_buf[1].cbBuffer
+ + out_buf[2].cbBuffer;
+
+ written += encrypt_len;
+ outbuf.insert(outbuf.end(), &message[0], &message[packet_len-1]+1);
+ }
+
+ if (int err = Flush()) {
+ state_ = SSL_ERROR;
+ SetError(err);
+ return SOCKET_ERROR;
+ }
+
+ return static_cast<int>(written);
+}
+
+int
+SChannelAdapter::Recv(void* pv, size_t cb) {
+ switch (state_) {
+ case SSL_NONE:
+ return AsyncSocketAdapter::Recv(pv, cb);
+
+ case SSL_WAIT:
+ case SSL_CONNECTING:
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+
+ case SSL_CONNECTED:
+ break;
+
+ case SSL_ERROR:
+ default:
+ return SOCKET_ERROR;
+ }
+
+ SChannelBuffer& readable = impl_->readable;
+ if (readable.empty()) {
+ SetError(EWOULDBLOCK);
+ return SOCKET_ERROR;
+ }
+ size_t read = _min(cb, readable.size());
+ memcpy(pv, &readable[0], read);
+ if (size_t remaining = readable.size() - read) {
+ memmove(&readable[0], &readable[read], remaining);
+ readable.resize(remaining);
+ } else {
+ readable.clear();
+ }
+
+ PostEvent();
+ return static_cast<int>(read);
+}
+
+int
+SChannelAdapter::Close() {
+ if (!impl_->readable.empty()) {
+ LOG(WARNING) << "SChannelAdapter::Close with readable data";
+ // Note: this isn't strictly an error, but we're using it temporarily to
+ // track bugs.
+ //ASSERT(false);
+ }
+ if (state_ == SSL_CONNECTED) {
+ DWORD token = SCHANNEL_SHUTDOWN;
+ CSecBufferBundle<1> sb_in;
+ sb_in[0].BufferType = SECBUFFER_TOKEN;
+ sb_in[0].cbBuffer = sizeof(token);
+ sb_in[0].pvBuffer = &token;
+ ApplyControlToken(&impl_->ctx, sb_in.desc());
+ // TODO: In theory, to do a nice shutdown, we need to begin shutdown
+ // negotiation with more calls to InitializeSecurityContext. Since the
+ // socket api doesn't support nice shutdown at this point, we don't bother.
+ }
+ Cleanup();
+ impl_ = new SSLImpl;
+ state_ = restartable_ ? SSL_WAIT : SSL_NONE;
+ signal_close_ = false;
+ message_pending_ = false;
+ return AsyncSocketAdapter::Close();
+}
+
+Socket::ConnState
+SChannelAdapter::GetState() const {
+ if (signal_close_)
+ return CS_CONNECTED;
+ ConnState state = socket_->GetState();
+ if ((state == CS_CONNECTED)
+ && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING)))
+ state = CS_CONNECTING;
+ return state;
+}
+
+void
+SChannelAdapter::OnConnectEvent(AsyncSocket* socket) {
+ LOG(LS_VERBOSE) << "SChannelAdapter::OnConnectEvent";
+ if (state_ != SSL_WAIT) {
+ ASSERT(state_ == SSL_NONE);
+ AsyncSocketAdapter::OnConnectEvent(socket);
+ return;
+ }
+
+ state_ = SSL_CONNECTING;
+ if (int err = BeginSSL()) {
+ Error("BeginSSL", err);
+ }
+}
+
+void
+SChannelAdapter::OnReadEvent(AsyncSocket* socket) {
+ if (state_ == SSL_NONE) {
+ AsyncSocketAdapter::OnReadEvent(socket);
+ return;
+ }
+
+ if (int err = Read()) {
+ Error("Read", err);
+ return;
+ }
+
+ if (impl_->inbuf.empty())
+ return;
+
+ if (state_ == SSL_CONNECTED) {
+ if (int err = DecryptData()) {
+ Error("DecryptData", err);
+ } else if (!impl_->readable.empty()) {
+ AsyncSocketAdapter::OnReadEvent(this);
+ }
+ } else if (state_ == SSL_CONNECTING) {
+ if (int err = ContinueSSL()) {
+ Error("ContinueSSL", err);
+ }
+ }
+}
+
+void
+SChannelAdapter::OnWriteEvent(AsyncSocket* socket) {
+ if (state_ == SSL_NONE) {
+ AsyncSocketAdapter::OnWriteEvent(socket);
+ return;
+ }
+
+ if (int err = Flush()) {
+ Error("Flush", err);
+ return;
+ }
+
+ // See if we have more data to write
+ if (!impl_->outbuf.empty())
+ return;
+
+ // Buffer is empty, submit notification
+ if (state_ == SSL_CONNECTED) {
+ AsyncSocketAdapter::OnWriteEvent(socket);
+ }
+}
+
+void
+SChannelAdapter::OnCloseEvent(AsyncSocket* socket, int err) {
+ if ((state_ == SSL_NONE) || impl_->readable.empty()) {
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+ return;
+ }
+
+ // If readable is non-empty, then we have a pending Message
+ // that will allow us to signal close (eventually).
+ signal_close_ = true;
+}
+
+void
+SChannelAdapter::OnMessage(Message* pmsg) {
+ if (!message_pending_)
+ return; // This occurs when socket is closed
+
+ message_pending_ = false;
+ if (!impl_->readable.empty()) {
+ AsyncSocketAdapter::OnReadEvent(this);
+ } else if (signal_close_) {
+ signal_close_ = false;
+ AsyncSocketAdapter::OnCloseEvent(this, 0); // TODO: cache this error?
+ }
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/schanneladapter.h b/third_party/libjingle/source/talk/base/schanneladapter.h
new file mode 100644
index 0000000..a5ab7b3
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/schanneladapter.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 TALK_BASE_SCHANNELADAPTER_H__
+#define TALK_BASE_SCHANNELADAPTER_H__
+
+#include <string>
+#include "talk/base/ssladapter.h"
+#include "talk/base/messagequeue.h"
+struct _SecBufferDesc;
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SChannelAdapter : public SSLAdapter, public MessageHandler {
+public:
+ SChannelAdapter(AsyncSocket* socket);
+ virtual ~SChannelAdapter();
+
+ virtual int StartSSL(const char* hostname, bool restartable);
+ virtual int Send(const void* pv, size_t cb);
+ virtual int Recv(void* pv, size_t cb);
+ virtual int Close();
+
+ // Note that the socket returns ST_CONNECTING while SSL is being negotiated.
+ virtual ConnState GetState() const;
+
+protected:
+ enum SSLState {
+ SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR
+ };
+ struct SSLImpl;
+
+ virtual void OnConnectEvent(AsyncSocket* socket);
+ virtual void OnReadEvent(AsyncSocket* socket);
+ virtual void OnWriteEvent(AsyncSocket* socket);
+ virtual void OnCloseEvent(AsyncSocket* socket, int err);
+ virtual void OnMessage(Message* pmsg);
+
+ int BeginSSL();
+ int ContinueSSL();
+ int ProcessContext(long int status, _SecBufferDesc* sbd_in,
+ _SecBufferDesc* sbd_out);
+ int DecryptData();
+
+ int Read();
+ int Flush();
+ void Error(const char* context, int err, bool signal = true);
+ void Cleanup();
+
+ void PostEvent();
+
+private:
+ SSLState state_;
+ std::string ssl_host_name_;
+ // If true, socket will retain SSL configuration after Close.
+ bool restartable_;
+ // If true, we are delaying signalling close until all data is read.
+ bool signal_close_;
+ // If true, we are waiting to be woken up to signal readability or closure.
+ bool message_pending_;
+ SSLImpl* impl_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SCHANNELADAPTER_H__
diff --git a/third_party/libjingle/source/talk/base/scoped_ptr.h b/third_party/libjingle/source/talk/base/scoped_ptr.h
new file mode 100644
index 0000000..5927990
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/scoped_ptr.h
@@ -0,0 +1,257 @@
+// (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.
+#ifndef TALK_BASE_SCOPED_PTR_H__
+#define TALK_BASE_SCOPED_PTR_H__
+
+#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 talk_base {
+
+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 = NULL): ptr(p) {}
+
+ ~scoped_ptr() {
+ typedef char type_must_be_complete[sizeof(T)];
+ delete ptr;
+ }
+
+ void reset(T* p = NULL) {
+ typedef char type_must_be_complete[sizeof(T)];
+
+ if (ptr != p) {
+ T* obj = ptr;
+ ptr = p;
+ // Delete last, in case obj destructor indirectly results in ~scoped_ptr
+ delete obj;
+ }
+ }
+
+ T& operator*() const {
+ assert(ptr != NULL);
+ return *ptr;
+ }
+
+ T* operator->() const {
+ assert(ptr != NULL);
+ 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 = NULL;
+ return tmp;
+ }
+
+ T** accept() {
+ if (ptr) {
+ delete ptr;
+ ptr = NULL;
+ }
+ 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 = NULL) : ptr(p) {}
+
+ ~scoped_array() {
+ typedef char type_must_be_complete[sizeof(T)];
+ delete[] ptr;
+ }
+
+ void reset(T* p = NULL) {
+ typedef char type_must_be_complete[sizeof(T)];
+
+ if (ptr != p) {
+ T* arr = ptr;
+ ptr = p;
+ // Delete last, in case arr destructor indirectly results in ~scoped_array
+ delete [] arr;
+ }
+ }
+
+ T& operator[](std::ptrdiff_t i) const {
+ assert(ptr != NULL);
+ 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 = NULL;
+ return tmp;
+ }
+
+ T** accept() {
+ if (ptr) {
+ delete [] ptr;
+ ptr = NULL;
+ }
+ 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() {
+ FF(static_cast<void*>(ptr));
+ }
+
+ void reset(T* p = 0) {
+ 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);
+}
+
+} // namespace talk_base
+
+#endif // #ifndef TALK_BASE_SCOPED_PTR_H__
diff --git a/third_party/libjingle/source/talk/base/sec_buffer.h b/third_party/libjingle/source/talk/base/sec_buffer.h
new file mode 100644
index 0000000..585e27f
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sec_buffer.h
@@ -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.
+ */
+
+// @file Contains utility classes that make it easier to use SecBuffers
+
+#ifndef TALK_BASE_SEC_BUFFER_H__
+#define TALK_BASE_SEC_BUFFER_H__
+
+namespace talk_base {
+
+// A base class for CSecBuffer<T>. Contains
+// all implementation that does not require
+// template arguments.
+class CSecBufferBase : public SecBuffer {
+ public:
+ CSecBufferBase() {
+ Clear();
+ }
+
+ // Uses the SSPI to free a pointer, must be
+ // used for buffers returned from SSPI APIs.
+ static void FreeSSPI(void *ptr) {
+ if ( ptr ) {
+ SECURITY_STATUS status;
+ status = ::FreeContextBuffer(ptr);
+ ASSERT(SEC_E_OK == status); // "Freeing context buffer"
+ }
+ }
+
+ // Deletes a buffer with operator delete
+ static void FreeDelete(void *ptr) {
+ delete [] reinterpret_cast<char*>(ptr);
+ }
+
+ // A noop delete, for buffers over other
+ // people's memory
+ static void FreeNone(void *ptr) {
+ }
+
+ protected:
+ // Clears the buffer to EMPTY & NULL
+ void Clear() {
+ this->BufferType = SECBUFFER_EMPTY;
+ this->cbBuffer = 0;
+ this->pvBuffer = NULL;
+ }
+};
+
+// Wrapper class for SecBuffer to take care
+// of initialization and destruction.
+template <void (*pfnFreeBuffer)(void *ptr)>
+class CSecBuffer: public CSecBufferBase {
+ public:
+ // Initializes buffer to empty & NULL
+ CSecBuffer() {
+ }
+
+ // Frees any allocated memory
+ ~CSecBuffer() {
+ Release();
+ }
+
+ // Frees the buffer appropriately, and re-nulls
+ void Release() {
+ pfnFreeBuffer(this->pvBuffer);
+ Clear();
+ }
+
+ private:
+ // A placeholder function for compile-time asserts on the class
+ void CompileAsserts() {
+ // never invoked...
+ assert(false); // _T("Notreached")
+
+ // This class must not extend the size of SecBuffer, since
+ // we use arrays of CSecBuffer in CSecBufferBundle below
+ cassert(sizeof(CSecBuffer<SSPIFree> == sizeof(SecBuffer)));
+ }
+};
+
+// Contains all generic implementation for the
+// SecBufferBundle class
+class SecBufferBundleBase {
+ public:
+};
+
+// A template class that bundles a SecBufferDesc with
+// one or more SecBuffers for convenience. Can take
+// care of deallocating buffers appropriately, as indicated
+// by pfnFreeBuffer function.
+// By default does no deallocation.
+template <int num_buffers,
+ void (*pfnFreeBuffer)(void *ptr) = CSecBufferBase::FreeNone>
+class CSecBufferBundle : public SecBufferBundleBase {
+ public:
+ // Constructs a security buffer bundle with num_buffers
+ // buffers, all of which are empty and nulled.
+ CSecBufferBundle() {
+ desc_.ulVersion = SECBUFFER_VERSION;
+ desc_.cBuffers = num_buffers;
+ desc_.pBuffers = buffers_;
+ }
+
+ // Frees all currently used buffers.
+ ~CSecBufferBundle() {
+ Release();
+ }
+
+ // Accessor for the descriptor
+ PSecBufferDesc desc() {
+ return &desc_;
+ }
+
+ // Accessor for the descriptor
+ const PSecBufferDesc desc() const {
+ return &desc_;
+ }
+
+ // returns the i-th security buffer
+ SecBuffer &operator[] (size_t num) {
+ ASSERT(num < num_buffers); // "Buffer index out of bounds"
+ return buffers_[num];
+ }
+
+ // returns the i-th security buffer
+ const SecBuffer &operator[] (size_t num) const {
+ ASSERT(num < num_buffers); // "Buffer index out of bounds"
+ return buffers_[num];
+ }
+
+ // Frees all non-NULL security buffers,
+ // using the deallocation function
+ void Release() {
+ for ( size_t i = 0; i < num_buffers; ++i ) {
+ buffers_[i].Release();
+ }
+ }
+
+ private:
+ // Our descriptor
+ SecBufferDesc desc_;
+ // Our bundled buffers, each takes care of its own
+ // initialization and destruction
+ CSecBuffer<pfnFreeBuffer> buffers_[num_buffers];
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SEC_BUFFER_H__
diff --git a/third_party/libjingle/source/talk/base/signalthread.cc b/third_party/libjingle/source/talk/base/signalthread.cc
new file mode 100644
index 0000000..ad7c09e
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/signalthread.cc
@@ -0,0 +1,156 @@
+/*
+ * libjingle
+ * Copyright 2004--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/signalthread.h"
+
+#include "talk/base/common.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// SignalThread
+///////////////////////////////////////////////////////////////////////////////
+
+SignalThread::SignalThread() : main_(Thread::Current()), state_(kInit) {
+ main_->SignalQueueDestroyed.connect(this,
+ &SignalThread::OnMainThreadDestroyed);
+ refcount_ = 1;
+ worker_.parent_ = this;
+}
+
+SignalThread::~SignalThread() {
+ ASSERT(refcount_ == 0);
+}
+
+void SignalThread::SetPriority(ThreadPriority priority) {
+ EnterExit ee(this);
+ ASSERT(main_->IsCurrent());
+ ASSERT(kInit == state_);
+ worker_.SetPriority(priority);
+}
+
+void SignalThread::Start() {
+ EnterExit ee(this);
+ ASSERT(main_->IsCurrent());
+ if (kInit == state_ || kComplete == state_) {
+ state_ = kRunning;
+ OnWorkStart();
+ worker_.Start();
+ } else {
+ ASSERT(false);
+ }
+}
+
+void SignalThread::Destroy(bool wait) {
+ EnterExit ee(this);
+ ASSERT(main_->IsCurrent());
+ if ((kInit == state_) || (kComplete == state_)) {
+ refcount_--;
+ } else if (kRunning == state_ || kReleasing == state_) {
+ state_ = kStopping;
+ // OnWorkStop() must follow Quit(), so that when the thread wakes up due to
+ // OWS(), ContinueWork() will return false.
+ worker_.Quit();
+ OnWorkStop();
+ if (wait) {
+ // Release the thread's lock so that it can return from ::Run.
+ cs_.Leave();
+ worker_.Stop();
+ cs_.Enter();
+ refcount_--;
+ }
+ } else {
+ ASSERT(false);
+ }
+}
+
+void SignalThread::Release() {
+ EnterExit ee(this);
+ ASSERT(main_->IsCurrent());
+ if (kComplete == state_) {
+ refcount_--;
+ } else if (kRunning == state_) {
+ state_ = kReleasing;
+ } else {
+ // if (kInit == state_) use Destroy()
+ ASSERT(false);
+ }
+}
+
+bool SignalThread::ContinueWork() {
+ EnterExit ee(this);
+ ASSERT(worker_.IsCurrent());
+ return worker_.ProcessMessages(0);
+}
+
+void SignalThread::OnMessage(Message *msg) {
+ EnterExit ee(this);
+ if (ST_MSG_WORKER_DONE == msg->message_id) {
+ ASSERT(main_->IsCurrent());
+ OnWorkDone();
+ bool do_delete = false;
+ if (kRunning == state_) {
+ state_ = kComplete;
+ } else {
+ do_delete = true;
+ }
+ if (kStopping != state_) {
+ // Before signaling that the work is done, make sure that the worker
+ // thread actually is done. We got here because DoWork() finished and
+ // Run() posted the ST_MSG_WORKER_DONE message. This means the worker
+ // thread is about to go away anyway, but sometimes it doesn't actually
+ // finish before SignalWorkDone is processed, and for a reusable
+ // SignalThread this makes an assert in thread.cc fire.
+ //
+ // Calling Stop() on the worker ensures that the OS thread that underlies
+ // the worker will finish, and will be set to NULL, enabling us to call
+ // Start() again.
+ worker_.Stop();
+ SignalWorkDone(this);
+ }
+ if (do_delete) {
+ refcount_--;
+ }
+ }
+}
+
+void SignalThread::Run() {
+ DoWork();
+ {
+ EnterExit ee(this);
+ if (main_) {
+ main_->Post(this, ST_MSG_WORKER_DONE);
+ }
+ }
+}
+
+void SignalThread::OnMainThreadDestroyed() {
+ EnterExit ee(this);
+ main_ = NULL;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/signalthread.h b/third_party/libjingle/source/talk/base/signalthread.h
new file mode 100644
index 0000000..9ac1899
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/signalthread.h
@@ -0,0 +1,153 @@
+/*
+ * libjingle
+ * Copyright 2004--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_SIGNALTHREAD_H_
+#define TALK_BASE_SIGNALTHREAD_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// SignalThread - Base class for worker threads. The main thread should call
+// Start() to begin work, and then follow one of these models:
+// Normal: Wait for SignalWorkDone, and then call Release to destroy.
+// Cancellation: Call Release(true), to abort the worker thread.
+// Fire-and-forget: Call Release(false), which allows the thread to run to
+// completion, and then self-destruct without further notification.
+// Periodic tasks: Wait for SignalWorkDone, then eventually call Start()
+// again to repeat the task. When the instance isn't needed anymore,
+// call Release. DoWork, OnWorkStart and OnWorkStop are called again,
+// on a new thread.
+// The subclass should override DoWork() to perform the background task. By
+// periodically calling ContinueWork(), it can check for cancellation.
+// OnWorkStart and OnWorkDone can be overridden to do pre- or post-work
+// tasks in the context of the main thread.
+///////////////////////////////////////////////////////////////////////////////
+
+class SignalThread : public sigslot::has_slots<>, protected MessageHandler {
+ public:
+ SignalThread();
+
+ // Context: Main Thread. Call before Start to change the worker's priority.
+ void SetPriority(ThreadPriority priority);
+
+ // Context: Main Thread. Call to begin the worker thread.
+ void Start();
+
+ // Context: Main Thread. If the worker thread is not running, deletes the
+ // object immediately. Otherwise, asks the worker thread to abort processing,
+ // and schedules the object to be deleted once the worker exits.
+ // SignalWorkDone will not be signalled. If wait is true, does not return
+ // until the thread is deleted.
+ void Destroy(bool wait);
+
+ // Context: Main Thread. If the worker thread is complete, deletes the
+ // object immediately. Otherwise, schedules the object to be deleted once
+ // the worker thread completes. SignalWorkDone will be signalled.
+ void Release();
+
+ // Context: Main Thread. Signalled when work is complete.
+ sigslot::signal1<SignalThread *> SignalWorkDone;
+
+ enum { ST_MSG_WORKER_DONE, ST_MSG_FIRST_AVAILABLE };
+
+ protected:
+ virtual ~SignalThread();
+
+ Thread* worker() { return &worker_; }
+
+ // Context: Main Thread. Subclass should override to do pre-work setup.
+ virtual void OnWorkStart() { }
+
+ // Context: Worker Thread. Subclass should override to do work.
+ virtual void DoWork() = 0;
+
+ // Context: Worker Thread. Subclass should call periodically to
+ // dispatch messages and determine if the thread should terminate.
+ bool ContinueWork();
+
+ // Context: Worker Thread. Subclass should override when extra work is
+ // needed to abort the worker thread.
+ virtual void OnWorkStop() { }
+
+ // Context: Main Thread. Subclass should override to do post-work cleanup.
+ virtual void OnWorkDone() { }
+
+ // Context: Any Thread. If subclass overrides, be sure to call the base
+ // implementation. Do not use (message_id < ST_MSG_FIRST_AVAILABLE)
+ virtual void OnMessage(Message *msg);
+
+ private:
+ enum State {
+ kInit, // Initialized, but not started
+ kRunning, // Started and doing work
+ kReleasing, // Same as running, but to be deleted when work is done
+ kComplete, // Work is done
+ kStopping, // Work is being interrupted
+ };
+
+ friend class Worker;
+ class Worker : public Thread {
+ public:
+ SignalThread* parent_;
+ virtual void Run() { parent_->Run(); }
+ };
+
+ friend class EnterExit;
+ class EnterExit {
+ public:
+ explicit EnterExit(SignalThread* t) : t_(t) {
+ t_->cs_.Enter();
+ t_->refcount_ += 1;
+ }
+ ~EnterExit() {
+ bool d = (0 == (--(t_->refcount_)));
+ t_->cs_.Leave();
+ if (d)
+ delete t_;
+ }
+ private:
+ SignalThread* t_;
+ };
+
+ void Run();
+ void OnMainThreadDestroyed();
+
+ Thread* main_;
+ Worker worker_;
+ CriticalSection cs_;
+ State state_;
+ int refcount_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SIGNALTHREAD_H_
diff --git a/third_party/libjingle/source/talk/base/sigslot.h b/third_party/libjingle/source/talk/base/sigslot.h
new file mode 100644
index 0000000..e9b85c7
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sigslot.h
@@ -0,0 +1,2816 @@
+// 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 TALK_BASE_SIGSLOT_H__
+#define TALK_BASE_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
+
+// TODO: change this namespace to talk_base?
+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 ~_connection_base0() {}
+ 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 ~_connection_base1() {}
+ 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 ~_connection_base2() {}
+ 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 ~_connection_base3() {}
+ 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 ~_connection_base4() {}
+ 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 ~_connection_base5() {}
+ 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 ~_connection_base6() {}
+ 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 ~_connection_base7() {}
+ 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 ~_connection_base8() {}
+ 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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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();
+ }
+
+ bool is_empty()
+ {
+ 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();
+ return it == itEnd;
+ }
+
+ 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)
+ {
+ delete *it;
+ m_connected_slots.erase(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 ~_connection0()
+ {
+ }
+
+ 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 ~_connection1()
+ {
+ }
+
+ 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 ~_connection2()
+ {
+ }
+
+ 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 ~_connection3()
+ {
+ }
+
+ 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 ~_connection4()
+ {
+ }
+
+ 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 ~_connection5()
+ {
+ }
+
+ 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 ~_connection6()
+ {
+ }
+
+ 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 ~_connection7()
+ {
+ }
+
+ 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 ~_connection8()
+ {
+ }
+
+ 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 // TALK_BASE_SIGSLOT_H__
diff --git a/third_party/libjingle/source/talk/base/sigslotrepeater.h b/third_party/libjingle/source/talk/base/sigslotrepeater.h
new file mode 100644
index 0000000..3bcdc95
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sigslotrepeater.h
@@ -0,0 +1,107 @@
+/*
+ * libjingle
+ * Copyright 2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_SIGSLOTREPEATER_H__
+#define TALK_BASE_SIGSLOTREPEATER_H__
+
+// repeaters are both signals and slots, which are designed as intermediate
+// pass-throughs for signals and slots which don't know about each other (for
+// modularity or encapsulation). This eliminates the need to declare a signal
+// handler whose sole purpose is to fire another signal. The repeater connects
+// to the originating signal using the 'repeat' method. When the repeated
+// signal fires, the repeater will also fire.
+
+#include "talk/base/sigslot.h"
+
+namespace sigslot {
+
+ template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class repeater0 : public signal0<mt_policy>,
+ public has_slots<mt_policy>
+ {
+ public:
+ typedef signal0<mt_policy> base_type;
+ typedef repeater0<mt_policy> this_type;
+
+ repeater0() { }
+ repeater0(const this_type& s) : base_type(s) { }
+
+ void reemit() { signal0<mt_policy>::emit(); }
+ void repeat(base_type &s) { s.connect(this, &this_type::reemit); }
+ };
+
+ template<class arg1_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class repeater1 : public signal1<arg1_type, mt_policy>,
+ public has_slots<mt_policy>
+ {
+ public:
+ typedef signal1<arg1_type, mt_policy> base_type;
+ typedef repeater1<arg1_type, mt_policy> this_type;
+
+ repeater1() { }
+ repeater1(const this_type& s) : base_type(s) { }
+
+ void reemit(arg1_type a1) { signal1<arg1_type, mt_policy>::emit(a1); }
+ void repeat(base_type& s) { s.connect(this, &this_type::reemit); }
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class repeater2 : public signal2<arg1_type, arg2_type, mt_policy>,
+ public has_slots<mt_policy>
+ {
+ public:
+ typedef signal2<arg1_type, arg2_type, mt_policy> base_type;
+ typedef repeater2<arg1_type, arg2_type, mt_policy> this_type;
+
+ repeater2() { }
+ repeater2(const this_type& s) : base_type(s) { }
+
+ void reemit(arg1_type a1, arg2_type a2) { signal2<arg1_type, arg2_type, mt_policy>::emit(a1,a2); }
+ void repeat(base_type& s) { s.connect(this, &this_type::reemit); }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type,
+ class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class repeater3 : public signal3<arg1_type, arg2_type, arg3_type, mt_policy>,
+ public has_slots<mt_policy>
+ {
+ public:
+ typedef signal3<arg1_type, arg2_type, arg3_type, mt_policy> base_type;
+ typedef repeater3<arg1_type, arg2_type, arg3_type, mt_policy> this_type;
+
+ repeater3() { }
+ repeater3(const this_type& s) : base_type(s) { }
+
+ void reemit(arg1_type a1, arg2_type a2, arg3_type a3) {
+ signal3<arg1_type, arg2_type, arg3_type, mt_policy>::emit(a1,a2,a3);
+ }
+ void repeat(base_type& s) { s.connect(this, &this_type::reemit); }
+ };
+
+} // namespace sigslot
+
+#endif // TALK_BASE_SIGSLOTREPEATER_H__
diff --git a/third_party/libjingle/source/talk/base/socket.h b/third_party/libjingle/source/talk/base/socket.h
new file mode 100644
index 0000000..3094918
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socket.h
@@ -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.
+ */
+
+#ifndef TALK_BASE_SOCKET_H__
+#define TALK_BASE_SOCKET_H__
+
+#include <errno.h>
+
+#ifdef POSIX
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#define SOCKET_EACCES EACCES
+#endif
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+#include "talk/base/basictypes.h"
+#include "talk/base/socketaddress.h"
+
+// 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
+#undef ETIMEDOUT // remove pthreads.h's definition
+#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 SOCKET_EACCES WSAEACCES
+#endif // WIN32
+
+#ifdef POSIX
+#define INVALID_SOCKET (-1)
+#define SOCKET_ERROR (-1)
+#define closesocket(s) close(s)
+#endif // POSIX
+
+namespace talk_base {
+
+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,
+ OPT_RCVBUF, // receive buffer size
+ OPT_SNDBUF, // send buffer size
+ OPT_NODELAY // whether Nagle algorithm is enabled
+ };
+ virtual int GetOption(Option opt, int* value) = 0;
+ virtual int SetOption(Option opt, int value) = 0;
+
+protected:
+ Socket() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(Socket);
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKET_H__
diff --git a/third_party/libjingle/source/talk/base/socketadapters.cc b/third_party/libjingle/source/talk/base/socketadapters.cc
new file mode 100644
index 0000000..f9947b5
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketadapters.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.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <time.h>
+#include <errno.h>
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define SECURITY_WIN32
+#include <security.h>
+#endif
+
+#include "talk/base/bytebuffer.h"
+#include "talk/base/common.h"
+#include "talk/base/httpcommon.h"
+#include "talk/base/logging.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+
+#ifdef WIN32
+#include "talk/base/sec_buffer.h"
+#endif // WIN32
+
+namespace talk_base {
+
+BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t size)
+ : AsyncSocketAdapter(socket), buffer_size_(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(juberti): 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(juberti): Do something better like forwarding the error to the user.
+ LOG_ERR(INFO) << "Recv";
+ return;
+ }
+
+ data_len_ += len;
+
+ ProcessInput(buffer_, &data_len_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// This is a SSL v2 CLIENT_HELLO message.
+// TODO(juberti): Should this have a session id? The response doesn't have a
+// certificate, so the hello should have a session id.
+static const uint8 kSslClientHello[] = {
+ 0x80, 0x46, // msg len
+ 0x01, // CLIENT_HELLO
+ 0x03, 0x01, // SSL 3.1
+ 0x00, 0x2d, // ciphersuite len
+ 0x00, 0x00, // session id len
+ 0x00, 0x10, // challenge len
+ 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x07, 0x00, 0xc0, // ciphersuites
+ 0x06, 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80, //
+ 0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x0a, //
+ 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64, //
+ 0x00, 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, //
+ 0x1f, 0x17, 0x0c, 0xa6, 0x2f, 0x00, 0x78, 0xfc, // challenge
+ 0x46, 0x55, 0x2e, 0xb1, 0x83, 0x39, 0xf1, 0xea //
+};
+
+// This is a TLSv1 SERVER_HELLO message.
+static const uint8 kSslServerHello[] = {
+ 0x16, // handshake message
+ 0x03, 0x01, // SSL 3.1
+ 0x00, 0x4a, // message len
+ 0x02, // SERVER_HELLO
+ 0x00, 0x00, 0x46, // handshake len
+ 0x03, 0x01, // SSL 3.1
+ 0x42, 0x85, 0x45, 0xa7, 0x27, 0xa9, 0x5d, 0xa0, // server random
+ 0xb3, 0xc5, 0xe7, 0x53, 0xda, 0x48, 0x2b, 0x3f, //
+ 0xc6, 0x5a, 0xca, 0x89, 0xc1, 0x58, 0x52, 0xa1, //
+ 0x78, 0x3c, 0x5b, 0x17, 0x46, 0x00, 0x85, 0x3f, //
+ 0x20, // session id len
+ 0x0e, 0xd3, 0x06, 0x72, 0x5b, 0x5b, 0x1b, 0x5f, // session id
+ 0x15, 0xac, 0x13, 0xf9, 0x88, 0x53, 0x9d, 0x9b, //
+ 0xe8, 0x3d, 0x7b, 0x0c, 0x30, 0x32, 0x6e, 0x38, //
+ 0x4d, 0xa2, 0x75, 0x57, 0x41, 0x6c, 0x34, 0x5c, //
+ 0x00, 0x04, // RSA/RC4-128/MD5
+ 0x00 // null compression
+};
+
+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(juberti): we could buffer output too...
+ VERIFY(sizeof(kSslClientHello) ==
+ DirectSend(kSslClientHello, sizeof(kSslClientHello)));
+}
+
+void AsyncSSLSocket::ProcessInput(char* data, size_t* len) {
+ if (*len < sizeof(kSslServerHello))
+ return;
+
+ if (memcmp(kSslServerHello, data, sizeof(kSslServerHello)) != 0) {
+ Close();
+ SignalCloseEvent(this, 0); // TODO(juberti): error code?
+ return;
+ }
+
+ *len -= sizeof(kSslServerHello);
+ if (*len > 0) {
+ memmove(data, data + sizeof(kSslServerHello), *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);
+}
+
+AsyncSSLServerSocket::AsyncSSLServerSocket(AsyncSocket* socket)
+ : BufferedReadAdapter(socket, 1024) {
+ BufferInput(true);
+}
+
+void AsyncSSLServerSocket::ProcessInput(char* data, size_t* len) {
+ // We only accept client hello messages.
+ if (*len < sizeof(kSslClientHello)) {
+ return;
+ }
+
+ if (memcmp(kSslClientHello, data, sizeof(kSslClientHello)) != 0) {
+ Close();
+ SignalCloseEvent(this, 0);
+ return;
+ }
+
+ *len -= sizeof(kSslClientHello);
+
+ // Clients should not send more data until the handshake is completed.
+ ASSERT(*len == 0);
+
+ // Send a server hello back to the client.
+ DirectSend(kSslServerHello, sizeof(kSslServerHello));
+
+ // Handshake completed for us, redirect input to our parent.
+ BufferInput(false);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket,
+ const std::string& user_agent,
+ const SocketAddress& proxy,
+ const std::string& username,
+ const CryptString& password)
+ : BufferedReadAdapter(socket, 1024), proxy_(proxy), agent_(user_agent),
+ user_(username), pass_(password), force_connect_(false), state_(PS_ERROR),
+ context_(0) {
+}
+
+AsyncHttpsProxySocket::~AsyncHttpsProxySocket() {
+ delete context_;
+}
+
+int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) {
+ int ret;
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect("
+ << proxy_.ToString() << ")";
+ dest_ = addr;
+ state_ = PS_INIT;
+ if (ShouldIssueConnect()) {
+ BufferInput(true);
+ }
+ ret = BufferedReadAdapter::Connect(proxy_);
+ // TODO(juberti): Set state_ appropriately if Connect fails.
+ return ret;
+}
+
+SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const {
+ return dest_;
+}
+
+int AsyncHttpsProxySocket::Close() {
+ headers_.clear();
+ state_ = PS_ERROR;
+ dest_.Clear();
+ delete context_;
+ context_ = NULL;
+ return BufferedReadAdapter::Close();
+}
+
+Socket::ConnState AsyncHttpsProxySocket::GetState() const {
+ if (state_ < PS_TUNNEL) {
+ return CS_CONNECTING;
+ } else if (state_ == PS_TUNNEL) {
+ return CS_CONNECTED;
+ } else {
+ return CS_CLOSED;
+ }
+}
+
+void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent";
+ if (!ShouldIssueConnect()) {
+ 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(juberti): signal this??
+}
+
+bool AsyncHttpsProxySocket::ShouldIssueConnect() const {
+ // TODO(juberti): Think about whether a more sophisticated test
+ // than dest port == 80 is needed.
+ return force_connect_ || (dest_.port() != 80);
+}
+
+void AsyncHttpsProxySocket::SendRequest() {
+ std::stringstream ss;
+ ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n";
+ ss << "User-Agent: " << agent_ << "\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(juberti): Raise a signal 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) {
+ unsigned int code;
+ if (sscanf(data, "HTTP/%*u.%*u %u", &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 (HttpAuthenticate(data + 19, len - 19,
+ proxy_, "CONNECT", "/",
+ user_, pass_, context_, response, auth_method)) {
+ case HAR_IGNORE:
+ LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method;
+ if (!unknown_mechanisms_.empty())
+ unknown_mechanisms_.append(", ");
+ unknown_mechanisms_.append(auth_method);
+ break;
+ case HAR_RESPONSE:
+ headers_ = "Proxy-Authorization: ";
+ headers_.append(response);
+ headers_.append("\r\n");
+ state_ = PS_SKIP_HEADERS;
+ unknown_mechanisms_.clear();
+ break;
+ case HAR_CREDENTIALS:
+ defer_error_ = SOCKET_EACCES;
+ state_ = PS_ERROR_HEADERS;
+ unknown_mechanisms_.clear();
+ break;
+ case HAR_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(juberti): 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 CryptString& password)
+ : BufferedReadAdapter(socket, 1024), state_(SS_ERROR), proxy_(proxy),
+ user_(username), pass_(password) {
+}
+
+int AsyncSocksProxySocket::Connect(const SocketAddress& addr) {
+ int ret;
+ dest_ = addr;
+ state_ = SS_INIT;
+ BufferInput(true);
+ ret = BufferedReadAdapter::Connect(proxy_);
+ // TODO(juberti): Set state_ appropriately if Connect fails.
+ return ret;
+}
+
+SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const {
+ return dest_;
+}
+
+int AsyncSocksProxySocket::Close() {
+ state_ = SS_ERROR;
+ dest_.Clear();
+ return BufferedReadAdapter::Close();
+}
+
+Socket::ConnState AsyncSocksProxySocket::GetState() const {
+ if (state_ < SS_TUNNEL) {
+ return CS_CONNECTING;
+ } else if (state_ == SS_TUNNEL) {
+ return CS_CONNECTED;
+ } else {
+ return CS_CLOSED;
+ }
+}
+
+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(SOCKET_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(juberti): 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(SOCKET_EACCES);
+ SignalCloseEvent(this, error);
+}
+
+AsyncSocksProxyServerSocket::AsyncSocksProxyServerSocket(AsyncSocket* socket)
+ : AsyncProxyServerSocket(socket, kBufferSize), state_(SS_HELLO) {
+ BufferInput(true);
+}
+
+void AsyncSocksProxyServerSocket::ProcessInput(char* data, size_t* len) {
+ // TODO(juberti): See if the whole message has arrived
+ ASSERT(state_ < SS_CONNECT_PENDING);
+
+ ByteBuffer response(data, *len);
+ if (state_ == SS_HELLO) {
+ HandleHello(&response);
+ } else if (state_ == SS_AUTH) {
+ HandleAuth(&response);
+ } else if (state_ == SS_CONNECT) {
+ HandleConnect(&response);
+ }
+
+ // Consume parsed data
+ *len = response.Length();
+ memcpy(data, response.Data(), *len);
+}
+
+void AsyncSocksProxyServerSocket::DirectSend(const ByteBuffer& buf) {
+ BufferedReadAdapter::DirectSend(buf.Data(), buf.Length());
+}
+
+void AsyncSocksProxyServerSocket::HandleHello(ByteBuffer* request) {
+ uint8 ver, num_methods;
+ if (!request->ReadUInt8(ver) ||
+ !request->ReadUInt8(num_methods)) {
+ Error(0);
+ return;
+ }
+
+ if (ver != 5) {
+ Error(0);
+ return;
+ }
+
+ // Handle either no-auth (0) or user/pass auth (2)
+ uint8 method = 0xFF;
+ if (num_methods > 0 && !request->ReadUInt8(method)) {
+ Error(0);
+ return;
+ }
+
+ // TODO(juberti): Ask the server which method to use.
+ SendHelloReply(method);
+ if (method == 0) {
+ state_ = SS_CONNECT;
+ } else if (method == 2) {
+ state_ = SS_AUTH;
+ } else {
+ state_ = SS_ERROR;
+ }
+}
+
+void AsyncSocksProxyServerSocket::SendHelloReply(int method) {
+ ByteBuffer response;
+ response.WriteUInt8(5); // Socks Version
+ response.WriteUInt8(method); // Auth method
+ DirectSend(response);
+}
+
+void AsyncSocksProxyServerSocket::HandleAuth(ByteBuffer* request) {
+ uint8 ver, user_len, pass_len;
+ std::string user, pass;
+ if (!request->ReadUInt8(ver) ||
+ !request->ReadUInt8(user_len) ||
+ !request->ReadString(user, user_len) ||
+ !request->ReadUInt8(pass_len) ||
+ !request->ReadString(pass, pass_len)) {
+ Error(0);
+ return;
+ }
+
+ // TODO(juberti): Allow for checking of credentials.
+ SendAuthReply(0);
+ state_ = SS_CONNECT;
+}
+
+void AsyncSocksProxyServerSocket::SendAuthReply(int result) {
+ ByteBuffer response;
+ response.WriteUInt8(1); // Negotiation Version
+ response.WriteUInt8(result);
+ DirectSend(response);
+}
+
+void AsyncSocksProxyServerSocket::HandleConnect(ByteBuffer* request) {
+ uint8 ver, command, reserved, addr_type;
+ uint32 ip;
+ uint16 port;
+ if (!request->ReadUInt8(ver) ||
+ !request->ReadUInt8(command) ||
+ !request->ReadUInt8(reserved) ||
+ !request->ReadUInt8(addr_type) ||
+ !request->ReadUInt32(ip) ||
+ !request->ReadUInt16(port)) {
+ Error(0);
+ return;
+ }
+
+ if (ver != 5 || command != 1 ||
+ reserved != 0 || addr_type != 1) {
+ Error(0);
+ return;
+ }
+
+ SignalConnectRequest(this, SocketAddress(ip, port));
+ state_ = SS_CONNECT_PENDING;
+}
+
+void AsyncSocksProxyServerSocket::SendConnectResult(int result,
+ const SocketAddress& addr) {
+ if (state_ != SS_CONNECT_PENDING)
+ return;
+
+ ByteBuffer response;
+ response.WriteUInt8(5); // Socks version
+ response.WriteUInt8((result != 0)); // 0x01 is generic error
+ response.WriteUInt8(0); // reserved
+ response.WriteUInt8(1); // IPv4 address
+ response.WriteUInt32(addr.ip());
+ response.WriteUInt16(addr.port());
+ DirectSend(response);
+ BufferInput(false);
+ state_ = SS_TUNNEL;
+}
+
+void AsyncSocksProxyServerSocket::Error(int error) {
+ state_ = SS_ERROR;
+ BufferInput(false);
+ Close();
+ SetError(SOCKET_EACCES);
+ SignalCloseEvent(this, error);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+LoggingSocketAdapter::LoggingSocketAdapter(AsyncSocket* socket,
+ LoggingSeverity level,
+ const char * label, bool hex_mode)
+ : AsyncSocketAdapter(socket), level_(level), hex_mode_(hex_mode) {
+ label_.append("[");
+ label_.append(label);
+ label_.append("]");
+}
+
+int LoggingSocketAdapter::Send(const void *pv, size_t cb) {
+ int res = AsyncSocketAdapter::Send(pv, cb);
+ if (res > 0)
+ LogMultiline(level_, label_.c_str(), false, pv, res, hex_mode_, &lms_);
+ return res;
+}
+
+int LoggingSocketAdapter::SendTo(const void *pv, size_t cb,
+ const SocketAddress& addr) {
+ int res = AsyncSocketAdapter::SendTo(pv, cb, addr);
+ if (res > 0)
+ LogMultiline(level_, label_.c_str(), false, pv, res, hex_mode_, &lms_);
+ return res;
+}
+
+int LoggingSocketAdapter::Recv(void *pv, size_t cb) {
+ int res = AsyncSocketAdapter::Recv(pv, cb);
+ if (res > 0)
+ LogMultiline(level_, label_.c_str(), true, pv, res, hex_mode_, &lms_);
+ return res;
+}
+
+int LoggingSocketAdapter::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) {
+ int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr);
+ if (res > 0)
+ LogMultiline(level_, label_.c_str(), true, pv, res, hex_mode_, &lms_);
+ return res;
+}
+
+int LoggingSocketAdapter::Close() {
+ LogMultiline(level_, label_.c_str(), false, NULL, 0, hex_mode_, &lms_);
+ LogMultiline(level_, label_.c_str(), true, NULL, 0, hex_mode_, &lms_);
+ LOG_V(level_) << label_ << " Closed locally";
+ return socket_->Close();
+}
+
+void LoggingSocketAdapter::OnConnectEvent(AsyncSocket * socket) {
+ LOG_V(level_) << label_ << " Connected";
+ AsyncSocketAdapter::OnConnectEvent(socket);
+}
+
+void LoggingSocketAdapter::OnCloseEvent(AsyncSocket * socket, int err) {
+ LogMultiline(level_, label_.c_str(), false, NULL, 0, hex_mode_, &lms_);
+ LogMultiline(level_, label_.c_str(), true, NULL, 0, hex_mode_, &lms_);
+ LOG_V(level_) << label_ << " Closed with error: " << err;
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/socketadapters.h b/third_party/libjingle/source/talk/base/socketadapters.h
new file mode 100644
index 0000000..71f976b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketadapters.h
@@ -0,0 +1,261 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_SOCKETADAPTERS_H_
+#define TALK_BASE_SOCKETADAPTERS_H_
+
+#include <map>
+#include <string>
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/cryptstring.h"
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+struct HttpAuthContext;
+class ByteBuffer;
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that can buffer and process data internally,
+// as in the case of connecting to a proxy, where you must speak the proxy
+// protocol before commencing normal socket behavior.
+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_;
+ DISALLOW_EVIL_CONSTRUCTORS(BufferedReadAdapter);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Interface for implementing proxy server sockets.
+class AsyncProxyServerSocket : public BufferedReadAdapter {
+ public:
+ AsyncProxyServerSocket(AsyncSocket* socket, size_t buffer_size)
+ : BufferedReadAdapter(socket, buffer_size) {}
+ sigslot::signal2<AsyncProxyServerSocket*,
+ const SocketAddress&> SignalConnectRequest;
+ virtual void SendConnectResult(int err, const SocketAddress& addr) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that performs the client side of a
+// fake SSL handshake. Used for "ssltcp" P2P functionality.
+class AsyncSSLSocket : public BufferedReadAdapter {
+ public:
+ explicit AsyncSSLSocket(AsyncSocket* socket);
+
+ virtual int Connect(const SocketAddress& addr);
+
+ protected:
+ virtual void OnConnectEvent(AsyncSocket* socket);
+ virtual void ProcessInput(char* data, size_t* len);
+ DISALLOW_EVIL_CONSTRUCTORS(AsyncSSLSocket);
+};
+
+// Implements a socket adapter that performs the server side of a
+// fake SSL handshake. Used when implementing a relay server that does "ssltcp".
+class AsyncSSLServerSocket : public BufferedReadAdapter {
+ public:
+ explicit AsyncSSLServerSocket(AsyncSocket* socket);
+
+ protected:
+ virtual void ProcessInput(char* data, size_t* len);
+ DISALLOW_EVIL_CONSTRUCTORS(AsyncSSLServerSocket);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that speaks the HTTP/S proxy protocol.
+class AsyncHttpsProxySocket : public BufferedReadAdapter {
+ public:
+ AsyncHttpsProxySocket(AsyncSocket* socket, const std::string& user_agent,
+ const SocketAddress& proxy,
+ const std::string& username, const CryptString& password);
+ virtual ~AsyncHttpsProxySocket();
+
+ // If connect is forced, the adapter will always issue an HTTP CONNECT to the
+ // target address. Otherwise, it will connect only if the destination port
+ // is not port 80.
+ void SetForceConnect(bool force) { force_connect_ = force; }
+
+ virtual int Connect(const SocketAddress& addr);
+ virtual SocketAddress GetRemoteAddress() const;
+ virtual int Close();
+ virtual ConnState GetState() const;
+
+ protected:
+ virtual void OnConnectEvent(AsyncSocket* socket);
+ virtual void OnCloseEvent(AsyncSocket* socket, int err);
+ virtual void ProcessInput(char* data, size_t* len);
+
+ bool ShouldIssueConnect() const;
+ void SendRequest();
+ void ProcessLine(char* data, size_t len);
+ void EndResponse();
+ void Error(int error);
+
+ private:
+ SocketAddress proxy_, dest_;
+ std::string agent_, user_, headers_;
+ CryptString pass_;
+ bool force_connect_;
+ size_t content_length_;
+ int defer_error_;
+ bool expect_close_;
+ enum ProxyState {
+ PS_INIT, PS_LEADER, PS_AUTHENTICATE, PS_SKIP_HEADERS, PS_ERROR_HEADERS,
+ PS_TUNNEL_HEADERS, PS_SKIP_BODY, PS_TUNNEL, PS_WAIT_CLOSE, PS_ERROR
+ } state_;
+ HttpAuthContext * context_;
+ std::string unknown_mechanisms_;
+ DISALLOW_EVIL_CONSTRUCTORS(AsyncHttpsProxySocket);
+};
+
+/* TODO(juberti): Implement this.
+class AsyncHttpsProxyServerSocket : public AsyncProxyServerSocket {
+ public:
+ explicit AsyncHttpsProxyServerSocket(AsyncSocket* socket);
+
+ private:
+ virtual void ProcessInput(char * data, size_t& len);
+ void Error(int error);
+ DISALLOW_EVIL_CONSTRUCTORS(AsyncHttpsProxyServerSocket);
+};
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that speaks the SOCKS proxy protocol.
+class AsyncSocksProxySocket : public BufferedReadAdapter {
+ public:
+ AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy,
+ const std::string& username, const CryptString& password);
+
+ virtual int Connect(const SocketAddress& addr);
+ virtual SocketAddress GetRemoteAddress() const;
+ virtual int Close();
+ virtual ConnState GetState() 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:
+ enum State {
+ SS_INIT, SS_HELLO, SS_AUTH, SS_CONNECT, SS_TUNNEL, SS_ERROR
+ };
+ State state_;
+ SocketAddress proxy_, dest_;
+ std::string user_;
+ CryptString pass_;
+ DISALLOW_EVIL_CONSTRUCTORS(AsyncSocksProxySocket);
+};
+
+// Implements a proxy server socket for the SOCKS protocol.
+class AsyncSocksProxyServerSocket : public AsyncProxyServerSocket {
+ public:
+ explicit AsyncSocksProxyServerSocket(AsyncSocket* socket);
+
+ private:
+ virtual void ProcessInput(char* data, size_t* len);
+ void DirectSend(const ByteBuffer& buf);
+
+ void HandleHello(ByteBuffer* request);
+ void SendHelloReply(int method);
+ void HandleAuth(ByteBuffer* request);
+ void SendAuthReply(int result);
+ void HandleConnect(ByteBuffer* request);
+ virtual void SendConnectResult(int result, const SocketAddress& addr);
+
+ void Error(int error);
+
+ static const int kBufferSize = 1024;
+ enum State {
+ SS_HELLO, SS_AUTH, SS_CONNECT, SS_CONNECT_PENDING, SS_TUNNEL, SS_ERROR
+ };
+ State state_;
+ DISALLOW_EVIL_CONSTRUCTORS(AsyncSocksProxyServerSocket);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that logs everything that it sends and receives.
+class LoggingSocketAdapter : public AsyncSocketAdapter {
+ public:
+ LoggingSocketAdapter(AsyncSocket* socket, LoggingSeverity level,
+ const char * label, bool hex_mode = false);
+
+ 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);
+ virtual int Close();
+
+ protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void OnCloseEvent(AsyncSocket * socket, int err);
+
+ private:
+ LoggingSeverity level_;
+ std::string label_;
+ bool hex_mode_;
+ LogMultilineState lms_;
+ DISALLOW_EVIL_CONSTRUCTORS(LoggingSocketAdapter);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETADAPTERS_H_
diff --git a/third_party/libjingle/source/talk/base/socketaddress.cc b/third_party/libjingle/source/talk/base/socketaddress.cc
new file mode 100644
index 0000000..66dd303
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketaddress.cc
@@ -0,0 +1,358 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#endif
+
+#include <sstream>
+
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/nethelpers.h"
+#include "talk/base/socketaddress.h"
+
+#ifdef WIN32
+// Win32 doesn't provide inet_aton, so we add our own version here.
+// Since inet_addr returns 0xFFFFFFFF on error, if we get this value
+// we need to test the input to see if the address really was 255.255.255.255.
+// This is slightly fragile, but better than doing nothing.
+int inet_aton(const char* cp, struct in_addr* inp) {
+ inp->s_addr = inet_addr(cp);
+ return (inp->s_addr == INADDR_NONE &&
+ strcmp(cp, "255.255.255.255") != 0) ? 0 : 1;
+}
+#endif // WIN32
+
+namespace talk_base {
+
+SocketAddress::SocketAddress() {
+ Clear();
+}
+
+SocketAddress::SocketAddress(const std::string& hostname, int port) {
+ SetIP(hostname);
+ SetPort(port);
+}
+
+SocketAddress::SocketAddress(uint32 ip, int port) {
+ SetIP(ip);
+ SetPort(port);
+}
+
+SocketAddress::SocketAddress(const SocketAddress& addr) {
+ this->operator=(addr);
+}
+
+void SocketAddress::Clear() {
+ hostname_.clear();
+ ip_ = 0;
+ port_ = 0;
+}
+
+bool SocketAddress::IsNil() const {
+ return hostname_.empty() && (0 == ip_) && (0 == port_);
+}
+
+bool SocketAddress::IsComplete() const {
+ return (0 != ip_) && (0 != port_);
+}
+
+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;
+}
+
+void SocketAddress::SetIP(const std::string& hostname) {
+ hostname_ = hostname;
+ ip_ = StringToIP(hostname);
+}
+
+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::FromString(const std::string& str) {
+ std::string::size_type pos = str.find(':');
+ if (std::string::npos == pos)
+ return false;
+ SetPort(strtoul(str.substr(pos + 1).c_str(), NULL, 10));
+ SetIP(str.substr(0, pos));
+ return true;
+}
+
+std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
+ os << addr.IPAsString() << ":" << addr.port();
+ return os;
+}
+
+bool SocketAddress::IsAnyIP() const {
+ return (ip_ == 0);
+}
+
+bool SocketAddress::IsLoopbackIP() const {
+ if (0 == ip_) {
+ return (0 == stricmp(hostname_.c_str(), "localhost"));
+ } else {
+ return ((ip_ >> 24) == 127);
+ }
+}
+
+bool SocketAddress::IsLocalIP() const {
+ if (IsLoopbackIP())
+ return true;
+
+ std::vector<uint32> ips;
+ if (0 == ip_) {
+ if (!hostname_.empty()
+ && (0 == stricmp(hostname_.c_str(), GetHostname().c_str()))) {
+ return true;
+ }
+ } else if (GetLocalIPs(ips)) {
+ for (size_t i = 0; i < ips.size(); ++i) {
+ if (ips[i] == ip_) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool SocketAddress::IsPrivateIP() const {
+ return ((ip_ >> 24) == 127) ||
+ ((ip_ >> 24) == 10) ||
+ ((ip_ >> 20) == ((172 << 4) | 1)) ||
+ ((ip_ >> 16) == ((192 << 8) | 168)) ||
+ ((ip_ >> 16) == ((169 << 8) | 254));
+}
+
+bool SocketAddress::IsUnresolvedIP() const {
+ return IsAny() && !hostname_.empty();
+}
+
+bool SocketAddress::ResolveIP(bool force, int* error) {
+ if (hostname_.empty()) {
+ // nothing to resolve
+ } else if (!force && !IsAny()) {
+ // already resolved
+ } else {
+ LOG_F(LS_VERBOSE) << "(" << hostname_ << ")";
+ int errcode = 0;
+ if (hostent* pHost = SafeGetHostByName(hostname_.c_str(), &errcode)) {
+ ip_ = NetworkToHost32(*reinterpret_cast<uint32*>(pHost->h_addr_list[0]));
+ LOG_F(LS_VERBOSE) << "(" << hostname_ << ") resolved to: "
+ << IPToString(ip_);
+ FreeHostEnt(pHost);
+ } else {
+ LOG_F(LS_ERROR) << "(" << hostname_ << ") error: " << errcode;
+ }
+ if (error) {
+ *error = errcode;
+ }
+ }
+ return (ip_ != 0);
+}
+
+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_) + 2;
+}
+
+bool SocketAddress::Write_(char* buf, int len) const {
+ if (len < static_cast<int>(Size_()))
+ return false;
+ buf[0] = 0;
+ buf[1] = AF_INET;
+ SetBE16(buf + 2, port_);
+ SetBE32(buf + 4, ip_);
+ return true;
+}
+
+bool SocketAddress::Read_(const char* buf, int len) {
+ if (len < static_cast<int>(Size_()) || buf[1] != AF_INET)
+ return false;
+ port_ = GetBE16(buf + 2);
+ ip_ = GetBE32(buf + 4);
+ return true;
+}
+
+void SocketAddress::ToSockAddr(sockaddr_in* saddr) const {
+ memset(saddr, 0, sizeof(*saddr));
+ saddr->sin_family = AF_INET;
+ saddr->sin_port = HostToNetwork16(port_);
+ if (0 == ip_) {
+ saddr->sin_addr.s_addr = INADDR_ANY;
+ } else {
+ saddr->sin_addr.s_addr = HostToNetwork32(ip_);
+ }
+}
+
+bool SocketAddress::FromSockAddr(const sockaddr_in& saddr) {
+ if (saddr.sin_family != AF_INET)
+ return false;
+ SetIP(NetworkToHost32(saddr.sin_addr.s_addr));
+ SetPort(NetworkToHost16(saddr.sin_port));
+ return true;
+}
+
+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();
+}
+
+bool SocketAddress::StringToIP(const std::string& hostname, uint32* ip) {
+ in_addr addr;
+ if (inet_aton(hostname.c_str(), &addr) == 0)
+ return false;
+ *ip = NetworkToHost32(addr.s_addr);
+ return true;
+}
+
+uint32 SocketAddress::StringToIP(const std::string& hostname) {
+ uint32 ip = 0;
+ StringToIP(hostname, &ip);
+ return ip;
+}
+
+std::string SocketAddress::GetHostname() {
+ char hostname[256];
+ if (gethostname(hostname, ARRAY_SIZE(hostname)) == 0)
+ return hostname;
+ return "";
+}
+
+bool SocketAddress::GetLocalIPs(std::vector<uint32>& ips) {
+ ips.clear();
+
+ const std::string hostname = GetHostname();
+ if (hostname.empty())
+ return false;
+
+ int errcode;
+ if (hostent* pHost = SafeGetHostByName(hostname.c_str(), &errcode)) {
+ for (size_t i = 0; pHost->h_addr_list[i]; ++i) {
+ uint32 ip =
+ NetworkToHost32(*reinterpret_cast<uint32 *>(pHost->h_addr_list[i]));
+ ips.push_back(ip);
+ }
+ FreeHostEnt(pHost);
+ return !ips.empty();
+ }
+ LOG(LS_ERROR) << "gethostbyname error: " << errcode;
+ return false;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/socketaddress.h b/third_party/libjingle/source/talk/base/socketaddress.h
new file mode 100644
index 0000000..4af80c9
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketaddress.h
@@ -0,0 +1,191 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_SOCKETADDRESS_H_
+#define TALK_BASE_SOCKETADDRESS_H_
+
+#include <string>
+#include <vector>
+#include <iosfwd>
+#include "talk/base/basictypes.h"
+#undef SetPort
+
+struct sockaddr_in;
+
+namespace talk_base {
+
+// 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 nil 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);
+
+ // 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);
+
+ // Resets to the nil address.
+ void Clear();
+
+ // Determines if this is a nil address (empty hostname, any IP, null port)
+ bool IsNil() const;
+
+ // Returns true if ip and port are set.
+ bool IsComplete() const;
+
+ // 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.
+ // Does not resolve the address; use Resolve to do so.
+ void SetIP(const std::string& hostname);
+
+ // 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 hostname
+ const std::string& hostname() const { return hostname_; }
+
+ // 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 hostname:port
+ std::string ToString() const;
+
+ // Parses hostname:port
+ bool FromString(const std::string& str);
+
+ friend std::ostream& operator<<(std::ostream& os, const SocketAddress& addr);
+
+ // Determines whether this represents a missing / any IP address. Hostname
+ // and/or port may be set.
+ bool IsAnyIP() const;
+ inline bool IsAny() const { return IsAnyIP(); } // deprecated
+
+ // Determines whether the IP address refers to a loopback address, i.e. within
+ // the range 127.0.0.0/8.
+ bool IsLoopbackIP() const;
+
+ // Determines wither the IP address refers to any adapter on the local
+ // machine, including the loopback adapter.
+ 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 IsUnresolvedIP() const;
+ inline bool IsUnresolved() const { return IsUnresolvedIP(); } // deprecated
+
+ // Attempt to resolve a hostname to IP address.
+ // Returns false if resolution is required but failed, and sets error.
+ // 'force' will cause re-resolution of hostname.
+ bool ResolveIP(bool force = false, int* error = NULL);
+
+ // Determines whether this address is identical to the given one.
+ bool operator ==(const SocketAddress& addr) const;
+ inline bool operator !=(const SocketAddress& addr) const {
+ return !this->operator ==(addr);
+ }
+
+ // 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;
+
+ // Determines 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, according to RFC 3489.
+ bool Write_(char* buf, int len) const;
+
+ // Reads this address from the given buffer, according to RFC 3489.
+ bool Read_(const char* buf, int len);
+
+ // Write this address to a sockaddr_in.
+ void ToSockAddr(sockaddr_in* saddr) const;
+
+ // Read this address from a sockaddr_in.
+ bool FromSockAddr(const sockaddr_in& saddr);
+
+ // 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.
+ // Only dotted names (A.B.C.D) are resolved.
+ static bool StringToIP(const std::string& str, uint32* ip);
+ static uint32 StringToIP(const std::string& str); // deprecated
+
+ // Get local machine's hostname
+ static std::string GetHostname();
+
+ // Get a list of the local machine's ip addresses
+ static bool GetLocalIPs(std::vector<uint32>& ips);
+
+ private:
+ std::string hostname_;
+ uint32 ip_;
+ uint16 port_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETADDRESS_H_
diff --git a/third_party/libjingle/source/talk/base/socketfactory.h b/third_party/libjingle/source/talk/base/socketfactory.h
new file mode 100644
index 0000000..2a4aee2
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketfactory.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 TALK_BASE_SOCKETFACTORY_H__
+#define TALK_BASE_SOCKETFACTORY_H__
+
+#include "talk/base/socket.h"
+#include "talk/base/asyncsocket.h"
+
+namespace talk_base {
+
+class SocketFactory {
+public:
+ virtual ~SocketFactory() {}
+
+ // 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 talk_base
+
+#endif // TALK_BASE_SOCKETFACTORY_H__
diff --git a/third_party/libjingle/source/talk/base/socketpool.cc b/third_party/libjingle/source/talk/base/socketpool.cc
new file mode 100644
index 0000000..6e89964
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketpool.cc
@@ -0,0 +1,278 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <iomanip>
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/logging.h"
+#include "talk/base/socketfactory.h"
+#include "talk/base/socketpool.h"
+#include "talk/base/socketstream.h"
+#include "talk/base/thread.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamCache - Caches a set of open streams, defers creation to a separate
+// StreamPool.
+///////////////////////////////////////////////////////////////////////////////
+
+StreamCache::StreamCache(StreamPool* pool) : pool_(pool) {
+}
+
+StreamCache::~StreamCache() {
+ for (ConnectedList::iterator it = active_.begin(); it != active_.end();
+ ++it) {
+ delete it->second;
+ }
+ for (ConnectedList::iterator it = cached_.begin(); it != cached_.end();
+ ++it) {
+ delete it->second;
+ }
+}
+
+StreamInterface* StreamCache::RequestConnectedStream(
+ const SocketAddress& remote, int* err) {
+ LOG_F(LS_VERBOSE) << "(" << remote << ")";
+ for (ConnectedList::iterator it = cached_.begin(); it != cached_.end();
+ ++it) {
+ if (remote == it->first) {
+ it->second->SignalEvent.disconnect(this);
+ // Move from cached_ to active_
+ active_.push_front(*it);
+ cached_.erase(it);
+ if (err)
+ *err = 0;
+ LOG_F(LS_VERBOSE) << "Providing cached stream";
+ return active_.front().second;
+ }
+ }
+ if (StreamInterface* stream = pool_->RequestConnectedStream(remote, err)) {
+ // We track active streams so that we can remember their address
+ active_.push_front(ConnectedStream(remote, stream));
+ LOG_F(LS_VERBOSE) << "Providing new stream";
+ return active_.front().second;
+ }
+ return NULL;
+}
+
+void StreamCache::ReturnConnectedStream(StreamInterface* stream) {
+ for (ConnectedList::iterator it = active_.begin(); it != active_.end();
+ ++it) {
+ if (stream == it->second) {
+ LOG_F(LS_VERBOSE) << "(" << it->first << ")";
+ if (stream->GetState() == SS_CLOSED) {
+ // Return closed streams
+ LOG_F(LS_VERBOSE) << "Returning closed stream";
+ pool_->ReturnConnectedStream(it->second);
+ } else {
+ // Monitor open streams
+ stream->SignalEvent.connect(this, &StreamCache::OnStreamEvent);
+ LOG_F(LS_VERBOSE) << "Caching stream";
+ cached_.push_front(*it);
+ }
+ active_.erase(it);
+ return;
+ }
+ }
+ ASSERT(false);
+}
+
+void StreamCache::OnStreamEvent(StreamInterface* stream, int events, int err) {
+ if ((events & SE_CLOSE) == 0) {
+ LOG_F(LS_WARNING) << "(" << events << ", " << err
+ << ") received non-close event";
+ return;
+ }
+ for (ConnectedList::iterator it = cached_.begin(); it != cached_.end();
+ ++it) {
+ if (stream == it->second) {
+ LOG_F(LS_VERBOSE) << "(" << it->first << ")";
+ // We don't cache closed streams, so return it.
+ it->second->SignalEvent.disconnect(this);
+ LOG_F(LS_VERBOSE) << "Returning closed stream";
+ pool_->ReturnConnectedStream(it->second);
+ cached_.erase(it);
+ return;
+ }
+ }
+ ASSERT(false);
+}
+
+//////////////////////////////////////////////////////////////////////
+// NewSocketPool
+//////////////////////////////////////////////////////////////////////
+
+NewSocketPool::NewSocketPool(SocketFactory* factory) : factory_(factory) {
+}
+
+NewSocketPool::~NewSocketPool() {
+}
+
+StreamInterface*
+NewSocketPool::RequestConnectedStream(const SocketAddress& remote, int* err) {
+ AsyncSocket* socket = factory_->CreateAsyncSocket(SOCK_STREAM);
+ if (!socket) {
+ ASSERT(false);
+ if (err)
+ *err = -1;
+ return NULL;
+ }
+ if ((socket->Connect(remote) != 0) && !socket->IsBlocking()) {
+ if (err)
+ *err = socket->GetError();
+ delete socket;
+ return NULL;
+ }
+ if (err)
+ *err = 0;
+ return new SocketStream(socket);
+}
+
+void
+NewSocketPool::ReturnConnectedStream(StreamInterface* stream) {
+ Thread::Current()->Dispose(stream);
+}
+
+//////////////////////////////////////////////////////////////////////
+// ReuseSocketPool
+//////////////////////////////////////////////////////////////////////
+
+ReuseSocketPool::ReuseSocketPool(SocketFactory* factory)
+: factory_(factory), stream_(NULL), checked_out_(false) {
+}
+
+ReuseSocketPool::~ReuseSocketPool() {
+ ASSERT(!checked_out_);
+ delete stream_;
+}
+
+StreamInterface*
+ReuseSocketPool::RequestConnectedStream(const SocketAddress& remote, int* err) {
+ // Only one socket can be used from this "pool" at a time
+ ASSERT(!checked_out_);
+ if (!stream_) {
+ LOG_F(LS_VERBOSE) << "Creating new socket";
+ AsyncSocket* socket = factory_->CreateAsyncSocket(SOCK_STREAM);
+ if (!socket) {
+ ASSERT(false);
+ if (err)
+ *err = -1;
+ return NULL;
+ }
+ stream_ = new SocketStream(socket);
+ }
+ if ((stream_->GetState() == SS_OPEN) && (remote == remote_)) {
+ LOG_F(LS_VERBOSE) << "Reusing connection to: " << remote_;
+ } else {
+ remote_ = remote;
+ stream_->Close();
+ if ((stream_->GetSocket()->Connect(remote_) != 0)
+ && !stream_->GetSocket()->IsBlocking()) {
+ if (err)
+ *err = stream_->GetSocket()->GetError();
+ return NULL;
+ } else {
+ LOG_F(LS_VERBOSE) << "Opening connection to: " << remote_;
+ }
+ }
+ stream_->SignalEvent.disconnect(this);
+ checked_out_ = true;
+ if (err)
+ *err = 0;
+ return stream_;
+}
+
+void
+ReuseSocketPool::ReturnConnectedStream(StreamInterface* stream) {
+ ASSERT(stream == stream_);
+ ASSERT(checked_out_);
+ checked_out_ = false;
+ // Until the socket is reused, monitor it to determine if it closes.
+ stream_->SignalEvent.connect(this, &ReuseSocketPool::OnStreamEvent);
+}
+
+void
+ReuseSocketPool::OnStreamEvent(StreamInterface* stream, int events, int err) {
+ LOG_F(LS_VERBOSE) << "Connection closed with error: " << err;
+ ASSERT(stream == stream_);
+ ASSERT(!checked_out_);
+ ASSERT(0 != (events & SE_CLOSE));
+ // Socket has closed. We'll reconnect it the next time it is used.
+ stream_->Close();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// LoggingPoolAdapter - Adapts a StreamPool to supply streams with attached
+// LoggingAdapters.
+///////////////////////////////////////////////////////////////////////////////
+
+LoggingPoolAdapter::LoggingPoolAdapter(
+ StreamPool* pool, LoggingSeverity level, const std::string& label,
+ bool binary_mode)
+ : pool_(pool), level_(level), label_(label), binary_mode_(binary_mode) {
+}
+
+LoggingPoolAdapter::~LoggingPoolAdapter() {
+ for (StreamList::iterator it = recycle_bin_.begin();
+ it != recycle_bin_.end(); ++it) {
+ delete *it;
+ }
+}
+
+StreamInterface* LoggingPoolAdapter::RequestConnectedStream(
+ const SocketAddress& remote, int* err) {
+ if (StreamInterface* stream = pool_->RequestConnectedStream(remote, err)) {
+ ASSERT(SS_CLOSED != stream->GetState());
+ std::stringstream ss;
+ ss << label_ << "(0x" << std::setfill('0') << std::hex << std::setw(8)
+ << stream << ")";
+ LOG_V(level_) << ss.str()
+ << ((SS_OPEN == stream->GetState()) ? " Connected"
+ : " Connecting")
+ << " to " << remote;
+ if (recycle_bin_.empty()) {
+ return new LoggingAdapter(stream, level_, ss.str(), binary_mode_);
+ }
+ LoggingAdapter* logging = recycle_bin_.front();
+ recycle_bin_.pop_front();
+ logging->set_label(ss.str());
+ logging->Attach(stream);
+ return logging;
+ }
+ return NULL;
+}
+
+void LoggingPoolAdapter::ReturnConnectedStream(StreamInterface* stream) {
+ LoggingAdapter* logging = static_cast<LoggingAdapter*>(stream);
+ pool_->ReturnConnectedStream(logging->Detach());
+ recycle_bin_.push_back(logging);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/socketpool.h b/third_party/libjingle/source/talk/base/socketpool.h
new file mode 100644
index 0000000..3bfd108
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketpool.h
@@ -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.
+ */
+
+#ifndef TALK_BASE_SOCKETPOOL_H__
+#define TALK_BASE_SOCKETPOOL_H__
+
+#include <deque>
+#include <list>
+#include "talk/base/logging.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socketaddress.h"
+
+namespace talk_base {
+
+class AsyncSocket;
+class LoggingAdapter;
+class SocketFactory;
+class SocketStream;
+class StreamInterface;
+
+//////////////////////////////////////////////////////////////////////
+// StreamPool
+//////////////////////////////////////////////////////////////////////
+
+class StreamPool {
+public:
+ virtual ~StreamPool() { }
+
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err) = 0;
+ virtual void ReturnConnectedStream(StreamInterface* stream) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamCache - Caches a set of open streams, defers creation/destruction to
+// the supplied StreamPool.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamCache : public StreamPool, public sigslot::has_slots<> {
+public:
+ StreamCache(StreamPool* pool);
+ virtual ~StreamCache();
+
+ // StreamPool Interface
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err);
+ virtual void ReturnConnectedStream(StreamInterface* stream);
+
+private:
+ typedef std::pair<SocketAddress, StreamInterface*> ConnectedStream;
+ typedef std::list<ConnectedStream> ConnectedList;
+
+ void OnStreamEvent(StreamInterface* stream, int events, int err);
+
+ // We delegate stream creation and deletion to this pool.
+ StreamPool* pool_;
+ // Streams that are in use (returned from RequestConnectedStream).
+ ConnectedList active_;
+ // Streams which were returned to us, but are still open.
+ ConnectedList cached_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// NewSocketPool
+// Creates a new stream on every request
+///////////////////////////////////////////////////////////////////////////////
+
+class NewSocketPool : public StreamPool {
+public:
+ NewSocketPool(SocketFactory* factory);
+ virtual ~NewSocketPool();
+
+ // StreamPool Interface
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err);
+ virtual void ReturnConnectedStream(StreamInterface* stream);
+
+private:
+ SocketFactory* factory_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ReuseSocketPool
+// Maintains a single socket at a time, and will reuse it without closing if
+// the destination address is the same.
+///////////////////////////////////////////////////////////////////////////////
+
+class ReuseSocketPool : public StreamPool, public sigslot::has_slots<> {
+public:
+ ReuseSocketPool(SocketFactory* factory);
+ virtual ~ReuseSocketPool();
+
+ // StreamPool Interface
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err);
+ virtual void ReturnConnectedStream(StreamInterface* stream);
+
+private:
+ void OnStreamEvent(StreamInterface* stream, int events, int err);
+
+ SocketFactory* factory_;
+ SocketStream* stream_;
+ SocketAddress remote_;
+ bool checked_out_; // Whether the stream is currently checked out
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// LoggingPoolAdapter - Adapts a StreamPool to supply streams with attached
+// LoggingAdapters.
+///////////////////////////////////////////////////////////////////////////////
+
+class LoggingPoolAdapter : public StreamPool {
+public:
+ LoggingPoolAdapter(StreamPool* pool, LoggingSeverity level,
+ const std::string& label, bool binary_mode);
+ virtual ~LoggingPoolAdapter();
+
+ // StreamPool Interface
+ virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote,
+ int* err);
+ virtual void ReturnConnectedStream(StreamInterface* stream);
+
+private:
+ StreamPool* pool_;
+ LoggingSeverity level_;
+ std::string label_;
+ bool binary_mode_;
+ typedef std::deque<LoggingAdapter*> StreamList;
+ StreamList recycle_bin_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKETPOOL_H__
diff --git a/third_party/libjingle/source/talk/base/socketserver.h b/third_party/libjingle/source/talk/base/socketserver.h
new file mode 100644
index 0000000..151ce61
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketserver.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.
+ */
+
+#ifndef TALK_BASE_SOCKETSERVER_H_
+#define TALK_BASE_SOCKETSERVER_H_
+
+#include "talk/base/socketfactory.h"
+
+namespace talk_base {
+
+class MessageQueue;
+
+// 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:
+ // When the socket server is installed into a Thread, this function is
+ // called to allow the socket server to use the thread's message queue for
+ // any messaging that it might need to perform.
+ virtual void SetMessageQueue(MessageQueue* queue) {}
+
+ // Sleeps until:
+ // 1) cms milliseconds have elapsed (unless cms == kForever)
+ // 2) WakeUp() is called
+ // While sleeping, I/O is performed if process_io is true.
+ 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 talk_base
+
+#endif // TALK_BASE_SOCKETSERVER_H_
diff --git a/third_party/libjingle/source/talk/base/socketstream.h b/third_party/libjingle/source/talk/base/socketstream.h
new file mode 100644
index 0000000..f0711cd
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/socketstream.h
@@ -0,0 +1,149 @@
+/*
+ * libjingle
+ * Copyright 2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_SOCKET_STREAM_H__
+#define TALK_BASE_SOCKET_STREAM_H__
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/common.h"
+#include "talk/base/stream.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SocketStream : public StreamInterface, public sigslot::has_slots<> {
+ public:
+ SocketStream(AsyncSocket* socket) : socket_(NULL) {
+ Attach(socket);
+ }
+ virtual ~SocketStream() { delete socket_; }
+
+ void Attach(AsyncSocket* socket) {
+ if (socket_)
+ delete socket_;
+ socket_ = socket;
+ if (socket_) {
+ socket_->SignalConnectEvent.connect(this, &SocketStream::OnConnectEvent);
+ socket_->SignalReadEvent.connect(this, &SocketStream::OnReadEvent);
+ socket_->SignalWriteEvent.connect(this, &SocketStream::OnWriteEvent);
+ socket_->SignalCloseEvent.connect(this, &SocketStream::OnCloseEvent);
+ }
+ }
+
+ AsyncSocket* Detach() {
+ AsyncSocket* socket = socket_;
+ if (socket_) {
+ socket_->SignalConnectEvent.disconnect(this);
+ socket_->SignalReadEvent.disconnect(this);
+ socket_->SignalWriteEvent.disconnect(this);
+ socket_->SignalCloseEvent.disconnect(this);
+ socket_ = NULL;
+ }
+ return socket;
+ }
+
+ AsyncSocket* GetSocket() { return socket_; }
+
+ virtual StreamState GetState() const {
+ ASSERT(socket_ != NULL);
+ switch (socket_->GetState()) {
+ case Socket::CS_CONNECTED:
+ return SS_OPEN;
+ case Socket::CS_CONNECTING:
+ return SS_OPENING;
+ case Socket::CS_CLOSED:
+ default:
+ return SS_CLOSED;
+ }
+ }
+
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ ASSERT(socket_ != NULL);
+ int result = socket_->Recv(buffer, buffer_len);
+ if (result < 0) {
+ if (socket_->IsBlocking())
+ return SR_BLOCK;
+ if (error)
+ *error = socket_->GetError();
+ return SR_ERROR;
+ }
+ if ((result > 0) || (buffer_len == 0)) {
+ if (read)
+ *read = result;
+ return SR_SUCCESS;
+ }
+ return SR_EOS;
+ }
+
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ ASSERT(socket_ != NULL);
+ int result = socket_->Send(data, data_len);
+ if (result < 0) {
+ if (socket_->IsBlocking())
+ return SR_BLOCK;
+ if (error)
+ *error = socket_->GetError();
+ return SR_ERROR;
+ }
+ if (written)
+ *written = result;
+ return SR_SUCCESS;
+ }
+
+ virtual void Close() { ASSERT(socket_ != NULL); socket_->Close(); }
+
+ private:
+ void OnConnectEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+ SignalEvent(this, SE_OPEN | SE_READ | SE_WRITE, 0);
+ }
+ void OnReadEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+ SignalEvent(this, SE_READ, 0);
+ }
+ void OnWriteEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+ SignalEvent(this, SE_WRITE, 0);
+ }
+ void OnCloseEvent(AsyncSocket* socket, int err) {
+ ASSERT(socket == socket_);
+ SignalEvent(this, SE_CLOSE, err);
+ }
+
+ AsyncSocket* socket_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SocketStream);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SOCKET_STREAM_H__
diff --git a/third_party/libjingle/source/talk/base/ssladapter.cc b/third_party/libjingle/source/talk/base/ssladapter.cc
new file mode 100644
index 0000000..2368e7a
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/ssladapter.cc
@@ -0,0 +1,102 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+// Decide which (if any) implementation of SSL we will use.
+#if !defined(SSL_USE_SCHANNEL) && !defined(SSL_USE_OPENSSL)
+#ifdef WIN32
+#define SSL_USE_SCHANNEL 1
+#else // !WIN32
+#define SSL_USE_OPENSSL HAVE_OPENSSL_SSL_H
+#endif // !WIN32
+#endif
+
+#include "talk/base/ssladapter.h"
+
+#if SSL_USE_SCHANNEL
+
+#include "schanneladapter.h"
+
+#elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL
+
+#include "openssladapter.h"
+
+#endif // SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+SSLAdapter*
+SSLAdapter::Create(AsyncSocket* socket) {
+#if SSL_USE_SCHANNEL
+ return new SChannelAdapter(socket);
+#elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL
+ return new OpenSSLAdapter(socket);
+#else // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
+ return NULL;
+#endif // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SSL_USE_OPENSSL
+
+bool InitializeSSL(VerificationCallback callback) {
+ return OpenSSLAdapter::InitializeSSL(callback);
+}
+
+bool InitializeSSLThread() {
+ return OpenSSLAdapter::InitializeSSLThread();
+}
+
+bool CleanupSSL() {
+ return OpenSSLAdapter::CleanupSSL();
+}
+
+#else // !SSL_USE_OPENSSL
+
+bool InitializeSSL(VerificationCallback callback) {
+ return true;
+}
+
+bool InitializeSSLThread() {
+ return true;
+}
+
+bool CleanupSSL() {
+ return true;
+}
+
+#endif // !SSL_USE_OPENSSL
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/ssladapter.h b/third_party/libjingle/source/talk/base/ssladapter.h
new file mode 100644
index 0000000..687cc24
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/ssladapter.h
@@ -0,0 +1,76 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_SSLADAPTER_H__
+#define TALK_BASE_SSLADAPTER_H__
+
+#include "talk/base/asyncsocket.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SSLAdapter : public AsyncSocketAdapter {
+public:
+ SSLAdapter(AsyncSocket* socket)
+ : AsyncSocketAdapter(socket), ignore_bad_cert_(false) { }
+
+ bool ignore_bad_cert() const { return ignore_bad_cert_; }
+ void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; }
+
+ // StartSSL returns 0 if successful.
+ // If StartSSL is called while the socket is closed or connecting, the SSL
+ // negotiation will begin as soon as the socket connects.
+ virtual int StartSSL(const char* hostname, bool restartable) = 0;
+
+ // Create the default SSL adapter for this platform
+ static SSLAdapter* Create(AsyncSocket* socket);
+
+private:
+ // If true, the server certificate need not match the configured hostname.
+ bool ignore_bad_cert_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef bool (*VerificationCallback)(void* cert);
+
+// Call this on the main thread, before using SSL.
+// Call CleanupSSLThread when finished with SSL.
+bool InitializeSSL(VerificationCallback callback = NULL);
+
+// Call to initialize additional threads.
+bool InitializeSSLThread();
+
+// Call to cleanup additional threads, and also the main thread.
+bool CleanupSSL();
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SSLADAPTER_H__
diff --git a/third_party/libjingle/source/talk/base/sslidentity.cc b/third_party/libjingle/source/talk/base/sslidentity.cc
new file mode 100644
index 0000000..665e700
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sslidentity.cc
@@ -0,0 +1,72 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Handling of certificates and keypairs for SSLStreamAdapter's peer mode.
+
+#include <string>
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+// Decide which (if any) implementation of SSL we will use.
+#if !defined(SSL_USE_SCHANNEL) && !defined(SSL_USE_OPENSSL)
+#ifdef WIN32
+#define SSL_USE_SCHANNEL 1
+#else // !WIN32
+#define SSL_USE_OPENSSL HAVE_OPENSSL_SSL_H
+#endif // !WIN32
+#endif
+
+#include "talk/base/sslidentity.h"
+
+#if SSL_USE_SCHANNEL
+
+#error "Not implemented yet"
+
+#elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL
+
+#include "talk/base/opensslidentity.h"
+
+namespace talk_base {
+
+SSLCertificate* SSLCertificate::FromPEMString(const std::string& pem_string,
+ int* pem_length) {
+ return OpenSSLCertificate::FromPEMString(pem_string, pem_length);
+}
+
+SSLIdentity* SSLIdentity::Generate(const std::string& common_name) {
+ return OpenSSLIdentity::Generate(common_name);
+}
+
+} // namespace talk_base
+
+#else // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
+
+#error "No SSL implementation"
+
+#endif // SSL_USE_OPENSSL/!SSL_USE_SCHANNEL
diff --git a/third_party/libjingle/source/talk/base/sslidentity.h b/third_party/libjingle/source/talk/base/sslidentity.h
new file mode 100644
index 0000000..ed996b4
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sslidentity.h
@@ -0,0 +1,91 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Handling of certificates and keypairs for SSLStreamAdapter's peer mode.
+
+#ifndef TALK_BASE_SSLIDENTITY_H__
+#define TALK_BASE_SSLIDENTITY_H__
+
+#include <string>
+
+namespace talk_base {
+
+// Abstract interface overridden by SSL library specific
+// implementations.
+
+// A somewhat opaque type used to encapsulate a certificate.
+// Wraps the SSL library's notion of a certificate, with reference counting.
+// The SSLCertificate object is pretty much immutable once created.
+// (The OpenSSL implementation only does reference counting and
+// possibly caching of intermediate results.)
+class SSLCertificate {
+ public:
+ // Parses and build a certificate from a PEM encoded string.
+ // Returns NULL on failure.
+ // The length of the string representation of the certificate is
+ // stored in *pem_length if it is non-NULL, and only if
+ // parsing was successful.
+ // Caller is responsible for freeing the returned object.
+ static SSLCertificate* FromPEMString(const std::string& pem_string,
+ int* pem_length);
+ virtual ~SSLCertificate() {}
+
+ // Returns a new SSLCertificate object instance wrapping the same
+ // underlying certificate.
+ // Caller is responsible for freeing the returned object.
+ virtual SSLCertificate* GetReference() = 0;
+
+ // Returns a PEM encoded string representation of the certificate.
+ virtual std::string ToPEMString() const = 0;
+};
+
+// Our identity in an SSL negotiation: a keypair and certificate (both
+// with the same public key).
+// This too is pretty much immutable once created.
+class SSLIdentity {
+ public:
+ // Generates an identity (keypair and self-signed certificate). If
+ // common_name is non-empty, it will be used for the certificate's
+ // subject and issuer name, otherwise a random string will be used.
+ // Returns NULL on failure.
+ // Caller is responsible for freeing the returned object.
+ static SSLIdentity* Generate(const std::string& common_name);
+
+ virtual ~SSLIdentity() {}
+
+ // Returns a new SSLIdentity object instance wrapping the same
+ // identity information.
+ // Caller is responsible for freeing the returned object.
+ virtual SSLIdentity* GetReference() = 0;
+
+ // Returns a temporary reference to the certificate.
+ virtual SSLCertificate& certificate() const = 0;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SSLIDENTITY_H__
diff --git a/third_party/libjingle/source/talk/base/sslsocketfactory.cc b/third_party/libjingle/source/talk/base/sslsocketfactory.cc
new file mode 100644
index 0000000..fcb2c0c
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sslsocketfactory.cc
@@ -0,0 +1,181 @@
+/*
+ * libjingle
+ * Copyright 2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/autodetectproxy.h"
+#include "talk/base/httpcommon.h"
+#include "talk/base/httpcommon-inl.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/ssladapter.h"
+#include "talk/base/sslsocketfactory.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// ProxySocketAdapter
+// TODO: Consider combining AutoDetectProxy and ProxySocketAdapter. I think
+// the socket adapter is the more appropriate idiom for automatic proxy
+// detection. We may or may not want to combine proxydetect.* as well.
+///////////////////////////////////////////////////////////////////////////////
+
+class ProxySocketAdapter : public AsyncSocketAdapter {
+ public:
+ ProxySocketAdapter(SslSocketFactory* factory, int type)
+ : AsyncSocketAdapter(NULL), factory_(factory), type_(type),
+ detect_(NULL) {
+ }
+ virtual ~ProxySocketAdapter() {
+ Close();
+ }
+
+ virtual int Connect(const SocketAddress& addr) {
+ ASSERT(NULL == detect_);
+ ASSERT(NULL == socket_);
+ remote_ = addr;
+ if (remote_.IsAnyIP() && remote_.hostname().empty()) {
+ LOG_F(LS_ERROR) << "Empty address";
+ return SOCKET_ERROR;
+ }
+ Url<char> url("/", remote_.IPAsString(), remote_.port());
+ detect_ = new AutoDetectProxy(factory_->agent_);
+ detect_->set_server_url(url.url());
+ detect_->SignalWorkDone.connect(this,
+ &ProxySocketAdapter::OnProxyDetectionComplete);
+ detect_->Start();
+ return SOCKET_ERROR;
+ }
+ virtual int GetError() const {
+ if (socket_) {
+ return socket_->GetError();
+ }
+ return detect_ ? EWOULDBLOCK : EADDRNOTAVAIL;
+ }
+ virtual int Close() {
+ if (socket_) {
+ return socket_->Close();
+ }
+ if (detect_) {
+ detect_->Destroy(false);
+ detect_ = NULL;
+ }
+ return 0;
+ }
+ virtual ConnState GetState() const {
+ if (socket_) {
+ return socket_->GetState();
+ }
+ return detect_ ? CS_CONNECTING : CS_CLOSED;
+ }
+
+private:
+ // AutoDetectProxy Slots
+ void OnProxyDetectionComplete(SignalThread* thread) {
+ ASSERT(detect_ == thread);
+ Attach(factory_->CreateProxySocket(detect_->proxy(), type_));
+ detect_->Release();
+ detect_ = NULL;
+ if (0 == AsyncSocketAdapter::Connect(remote_)) {
+ SignalConnectEvent(this);
+ } else if (!IsBlockingError(socket_->GetError())) {
+ SignalCloseEvent(this, socket_->GetError());
+ }
+ }
+
+ SslSocketFactory* factory_;
+ int type_;
+ SocketAddress remote_;
+ AutoDetectProxy* detect_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SslSocketFactory
+///////////////////////////////////////////////////////////////////////////////
+
+Socket* SslSocketFactory::CreateSocket(int type) {
+ return factory_->CreateSocket(type);
+}
+
+AsyncSocket* SslSocketFactory::CreateAsyncSocket(int type) {
+ if (autodetect_proxy_) {
+ return new ProxySocketAdapter(this, type);
+ } else {
+ return CreateProxySocket(proxy_, type);
+ }
+}
+
+AsyncSocket* SslSocketFactory::CreateProxySocket(const ProxyInfo& proxy,
+ int type) {
+ AsyncSocket* socket = factory_->CreateAsyncSocket(type);
+ if (!socket)
+ return NULL;
+
+ // Binary logging happens at the lowest level
+ if (!logging_label_.empty() && binary_mode_) {
+ socket = new LoggingSocketAdapter(socket, logging_level_,
+ logging_label_.c_str(), binary_mode_);
+ }
+
+ if (proxy.type) {
+ AsyncSocket* proxy_socket = 0;
+ if (proxy_.type == PROXY_SOCKS5) {
+ proxy_socket = new AsyncSocksProxySocket(socket, proxy.address,
+ proxy.username, proxy.password);
+ } else {
+ // Note: we are trying unknown proxies as HTTPS currently
+ AsyncHttpsProxySocket* http_proxy =
+ new AsyncHttpsProxySocket(socket, agent_, proxy.address,
+ proxy.username, proxy.password);
+ http_proxy->SetForceConnect(force_connect_ || !hostname_.empty());
+ proxy_socket = http_proxy;
+ }
+ if (!proxy_socket) {
+ delete socket;
+ return NULL;
+ }
+ socket = proxy_socket; // for our purposes the proxy is now the socket
+ }
+
+ if (!hostname_.empty()) {
+ if (SSLAdapter* ssl_adapter = SSLAdapter::Create(socket)) {
+ ssl_adapter->set_ignore_bad_cert(ignore_bad_cert_);
+ ssl_adapter->StartSSL(hostname_.c_str(), true);
+ socket = ssl_adapter;
+ } else {
+ LOG_F(LS_ERROR) << "SSL unavailable";
+ }
+ }
+
+ // Regular logging occurs at the highest level
+ if (!logging_label_.empty() && !binary_mode_) {
+ socket = new LoggingSocketAdapter(socket, logging_level_,
+ logging_label_.c_str(), binary_mode_);
+ }
+ return socket;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/sslsocketfactory.h b/third_party/libjingle/source/talk/base/sslsocketfactory.h
new file mode 100644
index 0000000..d42689a
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sslsocketfactory.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_SSLSOCKETFACTORY_H__
+#define TALK_BASE_SSLSOCKETFACTORY_H__
+
+#include "talk/base/proxyinfo.h"
+#include "talk/base/socketserver.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// SslSocketFactory
+///////////////////////////////////////////////////////////////////////////////
+
+class SslSocketFactory : public SocketFactory {
+ public:
+ SslSocketFactory(SocketFactory* factory, const std::string& user_agent)
+ : factory_(factory), agent_(user_agent), autodetect_proxy_(true),
+ force_connect_(false), logging_level_(LS_VERBOSE), binary_mode_(false) {
+ }
+
+ void SetAutoDetectProxy() {
+ autodetect_proxy_ = true;
+ }
+ void SetForceConnect(bool force) {
+ force_connect_ = force;
+ }
+ void SetProxy(const ProxyInfo& proxy) {
+ autodetect_proxy_ = false;
+ proxy_ = proxy;
+ }
+ bool autodetect_proxy() const { return autodetect_proxy_; }
+ const ProxyInfo& proxy() const { return proxy_; }
+
+ void UseSSL(const char* hostname) { hostname_ = hostname; }
+ void DisableSSL() { hostname_.clear(); }
+ void SetIgnoreBadCert(bool ignore) { ignore_bad_cert_ = ignore; }
+ bool ignore_bad_cert() const { return ignore_bad_cert_; }
+
+ void SetLogging(LoggingSeverity level, const std::string& label,
+ bool binary_mode = false) {
+ logging_level_ = level;
+ logging_label_ = label;
+ binary_mode_ = binary_mode;
+ }
+
+ // SocketFactory Interface
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+
+ private:
+ friend class ProxySocketAdapter;
+ AsyncSocket* CreateProxySocket(const ProxyInfo& proxy, int type);
+
+ SocketFactory* factory_;
+ std::string agent_;
+ bool autodetect_proxy_, force_connect_;
+ ProxyInfo proxy_;
+ std::string hostname_, logging_label_;
+ LoggingSeverity logging_level_;
+ bool binary_mode_;
+ bool ignore_bad_cert_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SSLSOCKETFACTORY_H__
diff --git a/third_party/libjingle/source/talk/base/sslstreamadapter.cc b/third_party/libjingle/source/talk/base/sslstreamadapter.cc
new file mode 100644
index 0000000..17121fd
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sslstreamadapter.cc
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+// Decide which (if any) implementation of SSL we will use.
+#if !defined(SSL_USE_SCHANNEL) && !defined(SSL_USE_OPENSSL)
+#ifdef WIN32
+#define SSL_USE_SCHANNEL 1
+#else // !WIN32
+#define SSL_USE_OPENSSL HAVE_OPENSSL_SSL_H
+#endif // !WIN32
+#endif
+
+#include "talk/base/sslstreamadapter.h"
+
+#if SSL_USE_SCHANNEL
+
+#error "Not implemented yet"
+
+#elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL
+
+#include "talk/base/opensslstreamadapter.h"
+
+#endif // SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace talk_base {
+
+SSLStreamAdapter* SSLStreamAdapter::Create(StreamInterface* stream) {
+#if SSL_USE_SCHANNEL
+ // not implemented yet
+ // return new SChannelStreamAdapter(stream);
+#elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL
+ return new OpenSSLStreamAdapter(stream);
+#else // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
+ return NULL;
+#endif // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/sslstreamadapter.h b/third_party/libjingle/source/talk/base/sslstreamadapter.h
new file mode 100644
index 0000000..62cef08
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/sslstreamadapter.h
@@ -0,0 +1,123 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_SSLSTREAMADAPTER_H__
+#define TALK_BASE_SSLSTREAMADAPTER_H__
+
+#include "talk/base/stream.h"
+#include "talk/base/sslidentity.h"
+
+namespace talk_base {
+
+// SSLStreamAdapter : A StreamInterfaceAdapter that does SSL/TLS.
+// After SSL has been started, the stream will only open on successful
+// SSL verification of certificates, and the communication is
+// encrypted of course.
+//
+// This class was written with SSLAdapter as a starting point. It
+// offers a similar interface, with two differences: there is no
+// support for a restartable SSL connection, and this class has a
+// peer-to-peer mode.
+//
+// The SSL library requires initialization and cleanup. Static method
+// for doing this are in SSLAdapter. They should possibly be moved out
+// to a neutral class.
+
+class SSLStreamAdapter : public StreamAdapterInterface {
+ public:
+ // Instantiate an SSLStreamAdapter wrapping the given stream,
+ // (using the selected implementation for the platform).
+ // Caller is responsible for freeing the returned object.
+ static SSLStreamAdapter* Create(StreamInterface* stream);
+
+ explicit SSLStreamAdapter(StreamInterface* stream)
+ : StreamAdapterInterface(stream), ignore_bad_cert_(false) { }
+
+ void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; }
+ bool ignore_bad_cert() const { return ignore_bad_cert_; }
+
+ // Specify our SSL identity: key and certificate. Mostly this is
+ // only used in the peer-to-peer mode (unless we actually want to
+ // provide a client certificate to a server).
+ // SSLStream takes ownership of the SSLIdentity object and will
+ // free it when appropriate. Should be called no more than once on a
+ // given SSLStream instance.
+ virtual void SetIdentity(SSLIdentity* identity) = 0;
+
+ // Call this to indicate that we are to play the server's role in
+ // the peer-to-peer mode.
+ virtual void SetServerRole() = 0;
+
+ // The mode of operation is selected by calling either
+ // StartSSLWithServer or StartSSLWithPeer.
+ // Use of the stream prior to calling either of these functions will
+ // pass data in clear text.
+ // Calling one of these functions causes SSL negotiation to begin as
+ // soon as possible: right away if the underlying wrapped stream is
+ // already opened, or else as soon as it opens.
+ //
+ // These functions return a negative error code on failure.
+ // Returning 0 means success so far, but negotiation is probably not
+ // complete and will continue asynchronously. In that case, the
+ // exposed stream will open after successful negotiation and
+ // verification, or an SE_CLOSE event will be raised if negotiation
+ // fails.
+
+ // StartSSLWithServer starts SSL negotiation with a server in
+ // traditional mode. server_name specifies the expected server name
+ // which the server's certificate needs to specify.
+ virtual int StartSSLWithServer(const char* server_name) = 0;
+
+ // StartSSLWithPeer starts negotiation in the special peer-to-peer
+ // mode.
+ // Generally, SetIdentity() and possibly SetServerRole() should have
+ // been called before this.
+ // SetPeerCertificate() must also be called. It may be called after
+ // StartSSLWithPeer() but must be called before the underlying
+ // stream opens.
+ virtual int StartSSLWithPeer() = 0;
+
+ // Specify the certificate that our peer is expected to use in
+ // peer-to-peer mode. Only this certificate will be accepted during
+ // SSL verification. The certificate is assumed to have been
+ // obtained through some other secure channel (such as the XMPP
+ // channel). (This could also specify the certificate authority that
+ // will sign the peer's certificate.)
+ // SSLStream takes ownership of the SSLCertificate object and will
+ // free it when appropriate. Should be called no more than once on a
+ // given SSLStream instance.
+ virtual void SetPeerCertificate(SSLCertificate* cert) = 0;
+
+ // If true, the server certificate need not match the configured
+ // server_name, and in fact missing certificate authority and other
+ // verification errors are ignored.
+ bool ignore_bad_cert_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_SSLSTREAMADAPTER_H__
diff --git a/third_party/libjingle/source/talk/base/stream.cc b/third_party/libjingle/source/talk/base/stream.cc
new file mode 100644
index 0000000..16eaf70
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/stream.cc
@@ -0,0 +1,1017 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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(POSIX)
+#include <sys/file.h>
+#endif // POSIX
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string>
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/thread.h"
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#define fileno _fileno
+#endif
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamInterface
+///////////////////////////////////////////////////////////////////////////////
+
+enum {
+ MSG_POST_EVENT = 0xF1F1
+};
+
+struct PostEventData : public MessageData {
+ int events, error;
+ PostEventData(int ev, int er) : events(ev), error(er) { }
+};
+
+StreamResult StreamInterface::WriteAll(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ StreamResult result = SR_SUCCESS;
+ size_t total_written = 0, current_written;
+ while (total_written < data_len) {
+ result = Write(static_cast<const char*>(data) + total_written,
+ data_len - total_written, &current_written, error);
+ if (result != SR_SUCCESS)
+ break;
+ total_written += current_written;
+ }
+ if (written)
+ *written = total_written;
+ return result;
+}
+
+StreamResult StreamInterface::ReadAll(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ StreamResult result = SR_SUCCESS;
+ size_t total_read = 0, current_read;
+ while (total_read < buffer_len) {
+ result = Read(static_cast<char*>(buffer) + total_read,
+ buffer_len - total_read, &current_read, error);
+ if (result != SR_SUCCESS)
+ break;
+ total_read += current_read;
+ }
+ if (read)
+ *read = total_read;
+ return result;
+}
+
+StreamResult StreamInterface::ReadLine(std::string* line) {
+ line->clear();
+ StreamResult result = SR_SUCCESS;
+ while (true) {
+ char ch;
+ result = Read(&ch, sizeof(ch), NULL, NULL);
+ if (result != SR_SUCCESS) {
+ break;
+ }
+ if (ch == '\n') {
+ break;
+ }
+ line->push_back(ch);
+ }
+ if (!line->empty()) { // give back the line we've collected so far with
+ result = SR_SUCCESS; // a success code. Otherwise return the last code
+ }
+ return result;
+}
+
+void StreamInterface::PostEvent(Thread* t, int events, int err) {
+ t->Post(this, MSG_POST_EVENT, new PostEventData(events, err));
+}
+
+void StreamInterface::PostEvent(int events, int err) {
+ PostEvent(Thread::Current(), events, err);
+}
+
+void StreamInterface::OnMessage(Message* msg) {
+ if (MSG_POST_EVENT == msg->message_id) {
+ PostEventData* pe = static_cast<PostEventData*>(msg->pdata);
+ SignalEvent(this, pe->events, pe->error);
+ delete msg->pdata;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamTap
+///////////////////////////////////////////////////////////////////////////////
+
+StreamTap::StreamTap(StreamInterface* stream, StreamInterface* tap)
+: StreamAdapterInterface(stream), tap_(NULL), tap_result_(SR_SUCCESS),
+ tap_error_(0)
+{
+ AttachTap(tap);
+}
+
+void StreamTap::AttachTap(StreamInterface* tap) {
+ tap_.reset(tap);
+}
+
+StreamInterface* StreamTap::DetachTap() {
+ return tap_.release();
+}
+
+StreamResult StreamTap::GetTapResult(int* error) {
+ if (error) {
+ *error = tap_error_;
+ }
+ return tap_result_;
+}
+
+StreamResult StreamTap::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ size_t backup_read;
+ if (!read) {
+ read = &backup_read;
+ }
+ StreamResult res = StreamAdapterInterface::Read(buffer, buffer_len,
+ read, error);
+ if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) {
+ tap_result_ = tap_->WriteAll(buffer, *read, NULL, &tap_error_);
+ }
+ return res;
+}
+
+StreamResult StreamTap::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ size_t backup_written;
+ if (!written) {
+ written = &backup_written;
+ }
+ StreamResult res = StreamAdapterInterface::Write(data, data_len,
+ written, error);
+ if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) {
+ tap_result_ = tap_->WriteAll(data, *written, NULL, &tap_error_);
+ }
+ return res;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamSegment
+///////////////////////////////////////////////////////////////////////////////
+
+StreamSegment::StreamSegment(StreamInterface* stream)
+: StreamAdapterInterface(stream), start_(SIZE_UNKNOWN), pos_(0),
+ length_(SIZE_UNKNOWN)
+{
+ // It's ok for this to fail, in which case start_ is left as SIZE_UNKNOWN.
+ stream->GetPosition(&start_);
+}
+
+StreamSegment::StreamSegment(StreamInterface* stream, size_t length)
+: StreamAdapterInterface(stream), start_(SIZE_UNKNOWN), pos_(0),
+ length_(length)
+{
+ // It's ok for this to fail, in which case start_ is left as SIZE_UNKNOWN.
+ stream->GetPosition(&start_);
+}
+
+StreamResult StreamSegment::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error)
+{
+ if (SIZE_UNKNOWN != length_) {
+ if (pos_ >= length_)
+ return SR_EOS;
+ buffer_len = _min(buffer_len, length_ - pos_);
+ }
+ size_t backup_read;
+ if (!read) {
+ read = &backup_read;
+ }
+ StreamResult result = StreamAdapterInterface::Read(buffer, buffer_len,
+ read, error);
+ if (SR_SUCCESS == result) {
+ pos_ += *read;
+ }
+ return result;
+}
+
+bool StreamSegment::SetPosition(size_t position) {
+ if (SIZE_UNKNOWN == start_)
+ return false; // Not seekable
+ if ((SIZE_UNKNOWN != length_) && (position > length_))
+ return false; // Seek past end of segment
+ if (!StreamAdapterInterface::SetPosition(start_ + position))
+ return false;
+ pos_ = position;
+ return true;
+}
+
+bool StreamSegment::GetPosition(size_t* position) const {
+ if (SIZE_UNKNOWN == start_)
+ return false; // Not seekable
+ if (!StreamAdapterInterface::GetPosition(position))
+ return false;
+ if (position) {
+ ASSERT(*position >= start_);
+ *position -= start_;
+ }
+ return true;
+}
+
+bool StreamSegment::GetSize(size_t* size) const {
+ if (!StreamAdapterInterface::GetSize(size))
+ return false;
+ if (size) {
+ if (SIZE_UNKNOWN != start_) {
+ ASSERT(*size >= start_);
+ *size -= start_;
+ }
+ if (SIZE_UNKNOWN != length_) {
+ *size = _min(*size, length_);
+ }
+ }
+ return true;
+}
+
+bool StreamSegment::GetAvailable(size_t* size) const {
+ if (!StreamAdapterInterface::GetAvailable(size))
+ return false;
+ if (size && (SIZE_UNKNOWN != length_))
+ *size = _min(*size, length_ - pos_);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// NullStream
+///////////////////////////////////////////////////////////////////////////////
+
+NullStream::NullStream() {
+}
+
+NullStream::~NullStream() {
+}
+
+StreamState NullStream::GetState() const {
+ return SS_OPEN;
+}
+
+StreamResult NullStream::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ if (error) *error = -1;
+ return SR_ERROR;
+}
+
+StreamResult NullStream::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (written) *written = data_len;
+ return SR_SUCCESS;
+}
+
+void NullStream::Close() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FileStream
+///////////////////////////////////////////////////////////////////////////////
+
+FileStream::FileStream() : file_(NULL) {
+}
+
+FileStream::~FileStream() {
+ FileStream::Close();
+}
+
+bool FileStream::Open(const std::string& filename, const char* mode) {
+ Close();
+#ifdef WIN32
+ std::wstring wfilename;
+ if (Utf8ToWindowsFilename(filename, &wfilename)) {
+ file_ = _wfopen(wfilename.c_str(), ToUtf16(mode).c_str());
+ } else {
+ file_ = NULL;
+ }
+#else
+ file_ = fopen(filename.c_str(), mode);
+#endif
+ return (file_ != NULL);
+}
+
+bool FileStream::OpenShare(const std::string& filename, const char* mode,
+ int shflag) {
+ Close();
+#ifdef WIN32
+ std::wstring wfilename;
+ if (Utf8ToWindowsFilename(filename, &wfilename)) {
+ file_ = _wfsopen(wfilename.c_str(), ToUtf16(mode).c_str(), shflag);
+ } else {
+ file_ = NULL;
+ }
+#else
+ return Open(filename, mode);
+#endif
+ return (file_ != NULL);
+}
+
+bool FileStream::DisableBuffering() {
+ if (!file_)
+ return false;
+ return (setvbuf(file_, NULL, _IONBF, 0) == 0);
+}
+
+StreamState FileStream::GetState() const {
+ return (file_ == NULL) ? SS_CLOSED : SS_OPEN;
+}
+
+StreamResult FileStream::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ if (!file_)
+ return SR_EOS;
+ size_t result = fread(buffer, 1, buffer_len, file_);
+ if ((result == 0) && (buffer_len > 0)) {
+ if (feof(file_))
+ return SR_EOS;
+ if (error)
+ *error = errno;
+ return SR_ERROR;
+ }
+ if (read)
+ *read = result;
+ return SR_SUCCESS;
+}
+
+StreamResult FileStream::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (!file_)
+ return SR_EOS;
+ size_t result = fwrite(data, 1, data_len, file_);
+ if ((result == 0) && (data_len > 0)) {
+ if (error)
+ *error = errno;
+ return SR_ERROR;
+ }
+ if (written)
+ *written = result;
+ return SR_SUCCESS;
+}
+
+void FileStream::Close() {
+ if (file_) {
+ DoClose();
+ file_ = NULL;
+ }
+}
+
+bool FileStream::SetPosition(size_t position) {
+ if (!file_)
+ return false;
+ return (fseek(file_, position, SEEK_SET) == 0);
+}
+
+bool FileStream::GetPosition(size_t* position) const {
+ ASSERT(NULL != position);
+ if (!file_)
+ return false;
+ long result = ftell(file_);
+ if (result < 0)
+ return false;
+ if (position)
+ *position = result;
+ return true;
+}
+
+bool FileStream::GetSize(size_t* size) const {
+ ASSERT(NULL != size);
+ if (!file_)
+ return false;
+ struct stat file_stats;
+ if (fstat(fileno(file_), &file_stats) != 0)
+ return false;
+ if (size)
+ *size = file_stats.st_size;
+ return true;
+}
+
+bool FileStream::GetAvailable(size_t* size) const {
+ ASSERT(NULL != size);
+ if (!GetSize(size))
+ return false;
+ long result = ftell(file_);
+ if (result < 0)
+ return false;
+ if (size)
+ *size -= result;
+ return true;
+}
+
+bool FileStream::ReserveSize(size_t size) {
+ // TODO: extend the file to the proper length
+ return true;
+}
+
+bool FileStream::GetSize(const std::string& filename, size_t* size) {
+ struct stat file_stats;
+ if (stat(filename.c_str(), &file_stats) != 0)
+ return false;
+ *size = file_stats.st_size;
+ return true;
+}
+
+bool FileStream::Flush() {
+ if (file_) {
+ return (0 == fflush(file_));
+ }
+ // try to flush empty file?
+ ASSERT(false);
+ return false;
+}
+
+#if defined(POSIX)
+
+bool FileStream::TryLock() {
+ if (file_ == NULL) {
+ // Stream not open.
+ ASSERT(false);
+ return false;
+ }
+
+ return flock(fileno(file_), LOCK_EX|LOCK_NB) == 0;
+}
+
+bool FileStream::Unlock() {
+ if (file_ == NULL) {
+ // Stream not open.
+ ASSERT(false);
+ return false;
+ }
+
+ return flock(fileno(file_), LOCK_UN) == 0;
+}
+
+#endif
+
+void FileStream::DoClose() {
+ fclose(file_);
+}
+
+#ifdef POSIX
+
+// Have to identically rewrite the FileStream destructor or else it would call
+// the base class's Close() instead of the sub-class's.
+POpenStream::~POpenStream() {
+ POpenStream::Close();
+}
+
+bool POpenStream::Open(const std::string& subcommand, const char* mode) {
+ Close();
+ file_ = popen(subcommand.c_str(), mode);
+ return file_ != NULL;
+}
+
+bool POpenStream::OpenShare(const std::string& subcommand, const char* mode,
+ int shflag) {
+ return Open(subcommand, mode);
+}
+
+void POpenStream::DoClose() {
+ wait_status_ = pclose(file_);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryStream
+///////////////////////////////////////////////////////////////////////////////
+
+MemoryStreamBase::MemoryStreamBase()
+ : buffer_(NULL), buffer_length_(0), data_length_(0), seek_position_(0) {
+}
+
+StreamState MemoryStreamBase::GetState() const {
+ return SS_OPEN;
+}
+
+StreamResult MemoryStreamBase::Read(void* buffer, size_t bytes,
+ size_t* bytes_read, int* error) {
+ if (seek_position_ >= data_length_) {
+ return SR_EOS;
+ }
+ size_t available = data_length_ - seek_position_;
+ if (bytes > available) {
+ // Read partial buffer
+ bytes = available;
+ }
+ memcpy(buffer, &buffer_[seek_position_], bytes);
+ seek_position_ += bytes;
+ if (bytes_read) {
+ *bytes_read = bytes;
+ }
+ return SR_SUCCESS;
+}
+
+StreamResult MemoryStreamBase::Write(const void* buffer, size_t bytes,
+ size_t* bytes_written, int* error) {
+ size_t available = buffer_length_ - seek_position_;
+ if (0 == available) {
+ // Increase buffer size to the larger of:
+ // a) new position rounded up to next 256 bytes
+ // b) double the previous length
+ size_t new_buffer_length = _max(((seek_position_ + bytes) | 0xFF) + 1,
+ buffer_length_ * 2);
+ StreamResult result = DoReserve(new_buffer_length, error);
+ if (SR_SUCCESS != result) {
+ return result;
+ }
+ ASSERT(buffer_length_ >= new_buffer_length);
+ available = buffer_length_ - seek_position_;
+ }
+
+ if (bytes > available) {
+ bytes = available;
+ }
+ memcpy(&buffer_[seek_position_], buffer, bytes);
+ seek_position_ += bytes;
+ if (data_length_ < seek_position_) {
+ data_length_ = seek_position_;
+ }
+ if (bytes_written) {
+ *bytes_written = bytes;
+ }
+ return SR_SUCCESS;
+}
+
+void MemoryStreamBase::Close() {
+ // nothing to do
+}
+
+bool MemoryStreamBase::SetPosition(size_t position) {
+ if (position > data_length_)
+ return false;
+ seek_position_ = position;
+ return true;
+}
+
+bool MemoryStreamBase::GetPosition(size_t *position) const {
+ if (position)
+ *position = seek_position_;
+ return true;
+}
+
+bool MemoryStreamBase::GetSize(size_t *size) const {
+ if (size)
+ *size = data_length_;
+ return true;
+}
+
+bool MemoryStreamBase::GetAvailable(size_t *size) const {
+ if (size)
+ *size = data_length_ - seek_position_;
+ return true;
+}
+
+bool MemoryStreamBase::ReserveSize(size_t size) {
+ return (SR_SUCCESS == DoReserve(size, NULL));
+}
+
+StreamResult MemoryStreamBase::DoReserve(size_t size, int* error) {
+ return (buffer_length_ >= size) ? SR_SUCCESS : SR_EOS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MemoryStream::MemoryStream() {
+}
+
+MemoryStream::MemoryStream(const char* data) {
+ SetData(data, strlen(data));
+}
+
+MemoryStream::MemoryStream(const void* data, size_t length) {
+ SetData(data, length);
+}
+
+MemoryStream::~MemoryStream() {
+ delete [] buffer_;
+}
+
+void MemoryStream::SetData(const void* data, size_t length) {
+ data_length_ = buffer_length_ = length;
+ delete [] buffer_;
+ buffer_ = new char[buffer_length_];
+ memcpy(buffer_, data, data_length_);
+ seek_position_ = 0;
+}
+
+StreamResult MemoryStream::DoReserve(size_t size, int* error) {
+ if (buffer_length_ >= size)
+ return SR_SUCCESS;
+
+ if (char* new_buffer = new char[size]) {
+ memcpy(new_buffer, buffer_, data_length_);
+ delete [] buffer_;
+ buffer_ = new_buffer;
+ buffer_length_ = size;
+ return SR_SUCCESS;
+ }
+
+ if (error) {
+ *error = ENOMEM;
+ }
+ return SR_ERROR;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ExternalMemoryStream::ExternalMemoryStream() {
+}
+
+ExternalMemoryStream::ExternalMemoryStream(void* data, size_t length) {
+ SetData(data, length);
+}
+
+void ExternalMemoryStream::SetData(void* data, size_t length) {
+ data_length_ = buffer_length_ = length;
+ buffer_ = static_cast<char*>(data);
+ seek_position_ = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FifoBuffer
+///////////////////////////////////////////////////////////////////////////////
+
+FifoBuffer::FifoBuffer(size_t size)
+ : state_(SS_OPEN), buffer_(new char[size]), buffer_length_(size),
+ data_length_(0), read_position_(0), owner_(Thread::Current()) {
+ // all events are done on the owner_ thread
+}
+
+FifoBuffer::~FifoBuffer() {
+}
+
+bool FifoBuffer::GetBuffered(size_t* size) const {
+ CritScope cs(&crit_);
+ *size = data_length_;
+ return true;
+}
+
+bool FifoBuffer::SetCapacity(size_t size) {
+ CritScope cs(&crit_);
+ if (data_length_ > size) {
+ return false;
+ }
+
+ if (size != buffer_length_) {
+ char* buffer = new char[size];
+ const size_t copy = data_length_;
+ const size_t tail_copy = _min(copy, buffer_length_ - read_position_);
+ memcpy(buffer, &buffer_[read_position_], tail_copy);
+ memcpy(buffer + tail_copy, &buffer_[0], copy - tail_copy);
+ buffer_.reset(buffer);
+ read_position_ = 0;
+ buffer_length_ = size;
+ }
+ return true;
+}
+
+StreamState FifoBuffer::GetState() const {
+ return state_;
+}
+
+StreamResult FifoBuffer::Read(void* buffer, size_t bytes,
+ size_t* bytes_read, int* error) {
+ CritScope cs(&crit_);
+ const size_t available = data_length_;
+ if (0 == available) {
+ return (state_ != SS_CLOSED) ? SR_BLOCK : SR_EOS;
+ }
+
+ const bool was_writable = data_length_ < buffer_length_;
+ const size_t copy = _min(bytes, available);
+ const size_t tail_copy = _min(copy, buffer_length_ - read_position_);
+ char* const p = static_cast<char*>(buffer);
+ memcpy(p, &buffer_[read_position_], tail_copy);
+ memcpy(p + tail_copy, &buffer_[0], copy - tail_copy);
+ read_position_ = (read_position_ + copy) % buffer_length_;
+ data_length_ -= copy;
+ if (bytes_read) {
+ *bytes_read = copy;
+ }
+ // if we were full before, and now we're not, post an event
+ if (!was_writable && copy > 0) {
+ PostEvent(owner_, SE_WRITE, 0);
+ }
+
+ return SR_SUCCESS;
+}
+
+StreamResult FifoBuffer::Write(const void* buffer, size_t bytes,
+ size_t* bytes_written, int* error) {
+ CritScope cs(&crit_);
+ if (state_ == SS_CLOSED) {
+ return SR_EOS;
+ }
+
+ const size_t available = buffer_length_ - data_length_;
+ if (0 == available) {
+ return SR_BLOCK;
+ }
+
+ const bool was_readable = (data_length_ > 0);
+ const size_t write_position = (read_position_ + data_length_)
+ % buffer_length_;
+ const size_t copy = _min(bytes, available);
+ const size_t tail_copy = _min(copy, buffer_length_ - write_position);
+ const char* const p = static_cast<const char*>(buffer);
+ memcpy(&buffer_[write_position], p, tail_copy);
+ memcpy(&buffer_[0], p + tail_copy, copy - tail_copy);
+ data_length_ += copy;
+ if (bytes_written) {
+ *bytes_written = copy;
+ }
+ // if we didn't have any data to read before, and now we do, post an event
+ if (!was_readable && copy > 0) {
+ PostEvent(owner_, SE_READ, 0);
+ }
+
+ return SR_SUCCESS;
+}
+
+void FifoBuffer::Close() {
+ CritScope cs(&crit_);
+ state_ = SS_CLOSED;
+}
+
+const void* FifoBuffer::GetReadData(size_t* size) {
+ CritScope cs(&crit_);
+ *size = (read_position_ + data_length_ <= buffer_length_) ?
+ data_length_ : buffer_length_ - read_position_;
+ return &buffer_[read_position_];
+}
+
+void FifoBuffer::ConsumeReadData(size_t size) {
+ CritScope cs(&crit_);
+ ASSERT(size <= data_length_);
+ const bool was_writable = data_length_ < buffer_length_;
+ read_position_ = (read_position_ + size) % buffer_length_;
+ data_length_ -= size;
+ if (!was_writable && size > 0) {
+ PostEvent(owner_, SE_WRITE, 0);
+ }
+}
+
+void* FifoBuffer::GetWriteBuffer(size_t* size) {
+ CritScope cs(&crit_);
+ if (state_ == SS_CLOSED) {
+ return NULL;
+ }
+
+ // if empty, reset the write position to the beginning, so we can get
+ // the biggest possible block
+ if (data_length_ == 0) {
+ read_position_ = 0;
+ }
+
+ const size_t write_position = (read_position_ + data_length_)
+ % buffer_length_;
+ *size = (write_position >= read_position_) ?
+ buffer_length_ - write_position : read_position_ - write_position;
+ return &buffer_[write_position];
+}
+
+void FifoBuffer::ConsumeWriteBuffer(size_t size) {
+ CritScope cs(&crit_);
+ ASSERT(size <= buffer_length_ - data_length_);
+ const bool was_readable = (data_length_ > 0);
+ data_length_ += size;
+ if (!was_readable && size > 0) {
+ PostEvent(owner_, SE_READ, 0);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// LoggingAdapter
+///////////////////////////////////////////////////////////////////////////////
+
+LoggingAdapter::LoggingAdapter(StreamInterface* stream, LoggingSeverity level,
+ const std::string& label, bool hex_mode)
+: StreamAdapterInterface(stream), level_(level), hex_mode_(hex_mode)
+{
+ set_label(label);
+}
+
+void LoggingAdapter::set_label(const std::string& label) {
+ label_.assign("[");
+ label_.append(label);
+ label_.append("]");
+}
+
+StreamResult LoggingAdapter::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ size_t local_read; if (!read) read = &local_read;
+ StreamResult result = StreamAdapterInterface::Read(buffer, buffer_len, read,
+ error);
+ if (result == SR_SUCCESS) {
+ LogMultiline(level_, label_.c_str(), true, buffer, *read, hex_mode_, &lms_);
+ }
+ return result;
+}
+
+StreamResult LoggingAdapter::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ size_t local_written; if (!written) written = &local_written;
+ StreamResult result = StreamAdapterInterface::Write(data, data_len, written,
+ error);
+ if (result == SR_SUCCESS) {
+ LogMultiline(level_, label_.c_str(), false, data, *written, hex_mode_,
+ &lms_);
+ }
+ return result;
+}
+
+void LoggingAdapter::Close() {
+ LogMultiline(level_, label_.c_str(), false, NULL, 0, hex_mode_, &lms_);
+ LogMultiline(level_, label_.c_str(), true, NULL, 0, hex_mode_, &lms_);
+ LOG_V(level_) << label_ << " Closed locally";
+ StreamAdapterInterface::Close();
+}
+
+void LoggingAdapter::OnEvent(StreamInterface* stream, int events, int err) {
+ if (events & SE_OPEN) {
+ LOG_V(level_) << label_ << " Open";
+ } else if (events & SE_CLOSE) {
+ LogMultiline(level_, label_.c_str(), false, NULL, 0, hex_mode_, &lms_);
+ LogMultiline(level_, label_.c_str(), true, NULL, 0, hex_mode_, &lms_);
+ LOG_V(level_) << label_ << " Closed with error: " << err;
+ }
+ StreamAdapterInterface::OnEvent(stream, events, err);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream - Reads/Writes to an external std::string
+///////////////////////////////////////////////////////////////////////////////
+
+StringStream::StringStream(std::string& str)
+: str_(str), read_pos_(0), read_only_(false)
+{
+}
+
+StringStream::StringStream(const std::string& str)
+: str_(const_cast<std::string&>(str)), read_pos_(0), read_only_(true)
+{
+}
+
+StreamState StringStream::GetState() const {
+ return SS_OPEN;
+}
+
+StreamResult StringStream::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ size_t available = _min(buffer_len, str_.size() - read_pos_);
+ if (!available)
+ return SR_EOS;
+ memcpy(buffer, str_.data() + read_pos_, available);
+ read_pos_ += available;
+ if (read)
+ *read = available;
+ return SR_SUCCESS;
+}
+
+StreamResult StringStream::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ if (read_only_) {
+ if (error) {
+ *error = -1;
+ }
+ return SR_ERROR;
+ }
+ str_.append(static_cast<const char*>(data),
+ static_cast<const char*>(data) + data_len);
+ if (written)
+ *written = data_len;
+ return SR_SUCCESS;
+}
+
+void StringStream::Close() {
+}
+
+bool StringStream::SetPosition(size_t position) {
+ if (position > str_.size())
+ return false;
+ read_pos_ = position;
+ return true;
+}
+
+bool StringStream::GetPosition(size_t* position) const {
+ if (position)
+ *position = read_pos_;
+ return true;
+}
+
+bool StringStream::GetSize(size_t* size) const {
+ if (size)
+ *size = str_.size();
+ return true;
+}
+
+bool StringStream::GetAvailable(size_t* size) const {
+ if (size)
+ *size = str_.size() - read_pos_;
+ return true;
+}
+
+bool StringStream::ReserveSize(size_t size) {
+ if (read_only_)
+ return false;
+ str_.reserve(size);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+StreamResult Flow(StreamInterface* source,
+ char* buffer, size_t buffer_len,
+ StreamInterface* sink,
+ size_t* data_len /* = NULL */) {
+ ASSERT(buffer_len > 0);
+
+ StreamResult result;
+ size_t count, read_pos, write_pos;
+ if (data_len) {
+ read_pos = *data_len;
+ } else {
+ read_pos = 0;
+ }
+
+ bool end_of_stream = false;
+ do {
+ // Read until buffer is full, end of stream, or error
+ while (!end_of_stream && (read_pos < buffer_len)) {
+ result = source->Read(buffer + read_pos, buffer_len - read_pos,
+ &count, NULL);
+ if (result == SR_EOS) {
+ end_of_stream = true;
+ } else if (result != SR_SUCCESS) {
+ if (data_len) {
+ *data_len = read_pos;
+ }
+ return result;
+ } else {
+ read_pos += count;
+ }
+ }
+
+ // Write until buffer is empty, or error (including end of stream)
+ write_pos = 0;
+ while (write_pos < read_pos) {
+ result = sink->Write(buffer + write_pos, read_pos - write_pos,
+ &count, NULL);
+ if (result != SR_SUCCESS) {
+ if (data_len) {
+ *data_len = read_pos - write_pos;
+ if (write_pos > 0) {
+ memmove(buffer, buffer + write_pos, *data_len);
+ }
+ }
+ return result;
+ }
+ write_pos += count;
+ }
+
+ read_pos = 0;
+ } while (!end_of_stream);
+
+ if (data_len) {
+ *data_len = 0;
+ }
+ return SR_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/stream.h b/third_party/libjingle/source/talk/base/stream.h
new file mode 100644
index 0000000..84ebe75
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/stream.h
@@ -0,0 +1,748 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_STREAM_H__
+#define TALK_BASE_STREAM_H__
+
+#include "talk/base/basictypes.h"
+#include "talk/base/criticalsection.h"
+#include "talk/base/logging.h"
+#include "talk/base/messagehandler.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamInterface is a generic asynchronous stream interface, supporting read,
+// write, and close operations, and asynchronous signalling of state changes.
+// The interface is designed with file, memory, and socket implementations in
+// mind. Some implementations offer extended operations, such as seeking.
+///////////////////////////////////////////////////////////////////////////////
+
+// The following enumerations are declared outside of the StreamInterface
+// class for brevity in use.
+
+// The SS_OPENING state indicates that the stream will signal open or closed
+// in the future.
+enum StreamState { SS_CLOSED, SS_OPENING, SS_OPEN };
+
+// Stream read/write methods return this value to indicate various success
+// and failure conditions described below.
+enum StreamResult { SR_ERROR, SR_SUCCESS, SR_BLOCK, SR_EOS };
+
+// StreamEvents are used to asynchronously signal state transitionss. The flags
+// may be combined.
+// SE_OPEN: The stream has transitioned to the SS_OPEN state
+// SE_CLOSE: The stream has transitioned to the SS_CLOSED state
+// SE_READ: Data is available, so Read is likely to not return SR_BLOCK
+// SE_WRITE: Data can be written, so Write is likely to not return SR_BLOCK
+enum StreamEvent { SE_OPEN = 1, SE_READ = 2, SE_WRITE = 4, SE_CLOSE = 8 };
+
+class Thread;
+
+class StreamInterface : public MessageHandler {
+ public:
+ virtual ~StreamInterface() { }
+
+ virtual StreamState GetState() const = 0;
+
+ // Read attempts to fill buffer of size buffer_len. Write attempts to send
+ // data_len bytes stored in data. The variables read and write are set only
+ // on SR_SUCCESS (see below). Likewise, error is only set on SR_ERROR.
+ // Read and Write return a value indicating:
+ // SR_ERROR: an error occurred, which is returned in a non-null error
+ // argument. Interpretation of the error requires knowledge of the
+ // stream's concrete type, which limits its usefulness.
+ // SR_SUCCESS: some number of bytes were successfully written, which is
+ // returned in a non-null read/write argument.
+ // SR_BLOCK: the stream is in non-blocking mode, and the operation would
+ // block, or the stream is in SS_OPENING state.
+ // SR_EOS: the end-of-stream has been reached, or the stream is in the
+ // SS_CLOSED state.
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) = 0;
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error) = 0;
+ // Attempt to transition to the SS_CLOSED state. SE_CLOSE will not be
+ // signalled as a result of this call.
+ virtual void Close() = 0;
+
+ // Streams may signal one or more StreamEvents to indicate state changes.
+ // The first argument identifies the stream on which the state change occured.
+ // The second argument is a bit-wise combination of StreamEvents.
+ // If SE_CLOSE is signalled, then the third argument is the associated error
+ // code. Otherwise, the value is undefined.
+ // Note: Not all streams will support asynchronous event signalling. However,
+ // SS_OPENING and SR_BLOCK returned from stream member functions imply that
+ // certain events will be raised in the future.
+ sigslot::signal3<StreamInterface*, int, int> SignalEvent;
+
+ // Like calling SignalEvent, but posts a message to the specified thread,
+ // which will call SignalEvent. This helps unroll the stack and prevent
+ // re-entrancy.
+ void PostEvent(Thread* t, int events, int err);
+ // Like the aforementioned method, but posts to the current thread.
+ void PostEvent(int events, int err);
+
+ //
+ // OPTIONAL OPERATIONS
+ //
+ // Not all implementations will support the following operations. In general,
+ // a stream will only support an operation if it reasonably efficient to do
+ // so. For example, while a socket could buffer incoming data to support
+ // seeking, it will not do so. Instead, a buffering stream adapter should
+ // be used.
+ //
+ // Even though several of these operations are related, you should
+ // always use whichever operation is most relevant. For example, you may
+ // be tempted to use GetSize() and GetPosition() to deduce the result of
+ // GetAvailable(). However, a stream which is read-once may support the
+ // latter operation but not the former.
+ //
+
+ // The following four methods are used to avoid coping data multiple times.
+
+ // GetReadData returns a pointer to a buffer which is owned by the stream.
+ // The buffer contains data_len bytes. NULL is returned if no data is
+ // available, or if the method fails. If the caller processes the data, it
+ // must call ConsumeReadData with the number of processed bytes. GetReadData
+ // does not require a matching call to ConsumeReadData if the data is not
+ // processed. Read and ConsumeReadData invalidate the buffer returned by
+ // GetReadData.
+ virtual const void* GetReadData(size_t* data_len) { return NULL; }
+ virtual void ConsumeReadData(size_t used) {}
+
+ // GetWriteBuffer returns a pointer to a buffer which is owned by the stream.
+ // The buffer has a capacity of buf_len bytes. NULL is returned if there is
+ // no buffer available, or if the method fails. The call may write data to
+ // the buffer, and then call ConsumeWriteBuffer with the number of bytes
+ // written. GetWriteBuffer does not require a matching call to
+ // ConsumeWriteData if no data is written. Write, ForceWrite, and
+ // ConsumeWriteData invalidate the buffer returned by GetWriteBuffer.
+ // TODO: Allow the caller to specify a minimum buffer size. If the specified
+ // amount of buffer is not yet available, return NULL and Signal SE_WRITE
+ // when it is available. If the requested amount is too large, return an
+ // error.
+ virtual void* GetWriteBuffer(size_t* buf_len) { return NULL; }
+ virtual void ConsumeWriteBuffer(size_t used) {}
+
+ // Write data_len bytes found in data, circumventing any throttling which
+ // would could cause SR_BLOCK to be returned. Returns true if all the data
+ // was written. Otherwise, the method is unsupported, or an unrecoverable
+ // error occurred, and the error value is set. This method should be used
+ // sparingly to write critical data which should not be throttled. A stream
+ // which cannot circumvent its blocking constraints should not implement this
+ // method.
+ // NOTE: This interface is being considered experimentally at the moment. It
+ // would be used by JUDP and BandwidthStream as a way to circumvent certain
+ // soft limits in writing.
+ //virtual bool ForceWrite(const void* data, size_t data_len, int* error) {
+ // if (error) *error = -1;
+ // return false;
+ //}
+
+ // Seek to a byte offset from the beginning of the stream. Returns false if
+ // the stream does not support seeking, or cannot seek to the specified
+ // position.
+ virtual bool SetPosition(size_t position) { return false; }
+
+ // Get the byte offset of the current position from the start of the stream.
+ // Returns false if the position is not known.
+ virtual bool GetPosition(size_t* position) const { return false; }
+
+ // Get the byte length of the entire stream. Returns false if the length
+ // is not known.
+ virtual bool GetSize(size_t* size) const { return false; }
+
+ // Return the number of Read()-able bytes remaining before end-of-stream.
+ // Returns false if not known.
+ virtual bool GetAvailable(size_t* size) const { return false; }
+
+ // Return the number of Write()-able bytes remaining before end-of-stream.
+ // Returns false if not known.
+ virtual bool GetWriteRemaining(size_t* size) const { return false; }
+
+ // Communicates the amount of data which will be written to the stream. The
+ // stream may choose to preallocate memory to accomodate this data. The
+ // stream may return false to indicate that there is not enough room (ie,
+ // Write will return SR_EOS/SR_ERROR at some point). Note that calling this
+ // function should not affect the existing state of data in the stream.
+ virtual bool ReserveSize(size_t size) { return true; }
+
+ //
+ // CONVENIENCE METHODS
+ //
+ // These methods are implemented in terms of other methods, for convenience.
+ //
+
+ // Seek to the start of the stream.
+ inline bool Rewind() { return SetPosition(0); }
+
+ // WriteAll is a helper function which repeatedly calls Write until all the
+ // data is written, or something other than SR_SUCCESS is returned. Note that
+ // unlike Write, the argument 'written' is always set, and may be non-zero
+ // on results other than SR_SUCCESS. The remaining arguments have the
+ // same semantics as Write.
+ StreamResult WriteAll(const void* data, size_t data_len,
+ size_t* written, int* error);
+
+ // Similar to ReadAll. Calls Read until buffer_len bytes have been read, or
+ // until a non-SR_SUCCESS result is returned. 'read' is always set.
+ StreamResult ReadAll(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+
+ // ReadLine is a helper function which repeatedly calls Read until it hits
+ // the end-of-line character, or something other than SR_SUCCESS.
+ // TODO: this is too inefficient to keep here. Break this out into a buffered
+ // readline object or adapter
+ StreamResult ReadLine(std::string *line);
+
+ protected:
+ StreamInterface() { }
+
+ // MessageHandler Interface
+ virtual void OnMessage(Message* msg);
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(StreamInterface);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamAdapterInterface is a convenient base-class for adapting a stream.
+// By default, all operations are pass-through. Override the methods that you
+// require adaptation. Streams should really be upgraded to reference-counted.
+// In the meantime, use the owned flag to indicate whether the adapter should
+// own the adapted stream.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamAdapterInterface : public StreamInterface,
+ public sigslot::has_slots<> {
+ public:
+ explicit StreamAdapterInterface(StreamInterface* stream, bool owned = true)
+ : stream_(stream), owned_(owned) {
+ if (NULL != stream_)
+ stream_->SignalEvent.connect(this, &StreamAdapterInterface::OnEvent);
+ }
+
+ // Core Stream Interface
+ virtual StreamState GetState() const {
+ return stream_->GetState();
+ }
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ return stream_->Read(buffer, buffer_len, read, error);
+ }
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ return stream_->Write(data, data_len, written, error);
+ }
+ virtual void Close() {
+ stream_->Close();
+ }
+
+ // Optional Stream Interface
+ /* Note: Many stream adapters were implemented prior to this Read/Write
+ interface. Therefore, a simple pass through of data in those cases may
+ be broken. At a later time, we should do a once-over pass of all
+ adapters, and make them compliant with these interfaces, after which this
+ code can be uncommented.
+ virtual const void* GetReadData(size_t* data_len) {
+ return stream_->GetReadData(data_len);
+ }
+ virtual void ConsumeReadData(size_t used) {
+ stream_->ConsumeReadData(used);
+ }
+
+ virtual void* GetWriteBuffer(size_t* buf_len) {
+ return stream_->GetWriteBuffer(buf_len);
+ }
+ virtual void ConsumeWriteBuffer(size_t used) {
+ stream_->ConsumeWriteBuffer(used);
+ }
+ */
+
+ /* Note: This interface is currently undergoing evaluation.
+ virtual bool ForceWrite(const void* data, size_t data_len, int* error) {
+ return stream_->ForceWrite(data, data_len, error);
+ }
+ */
+
+ virtual bool SetPosition(size_t position) {
+ return stream_->SetPosition(position);
+ }
+ virtual bool GetPosition(size_t* position) const {
+ return stream_->GetPosition(position);
+ }
+ virtual bool GetSize(size_t* size) const {
+ return stream_->GetSize(size);
+ }
+ virtual bool GetAvailable(size_t* size) const {
+ return stream_->GetAvailable(size);
+ }
+ virtual bool GetWriteRemaining(size_t* size) const {
+ return stream_->GetWriteRemaining(size);
+ }
+ virtual bool ReserveSize(size_t size) {
+ return stream_->ReserveSize(size);
+ }
+
+ void Attach(StreamInterface* stream, bool owned = true) {
+ if (NULL != stream_)
+ stream_->SignalEvent.disconnect(this);
+ if (owned_)
+ delete stream_;
+ stream_ = stream;
+ owned_ = owned;
+ if (NULL != stream_)
+ stream_->SignalEvent.connect(this, &StreamAdapterInterface::OnEvent);
+ }
+ StreamInterface* Detach() {
+ if (NULL != stream_)
+ stream_->SignalEvent.disconnect(this);
+ StreamInterface* stream = stream_;
+ stream_ = NULL;
+ return stream;
+ }
+
+ protected:
+ virtual ~StreamAdapterInterface() {
+ if (owned_)
+ delete stream_;
+ }
+ // Note that the adapter presents itself as the origin of the stream events,
+ // since users of the adapter may not recognize the adapted object.
+ virtual void OnEvent(StreamInterface* stream, int events, int err) {
+ SignalEvent(this, events, err);
+ }
+ StreamInterface* stream() { return stream_; }
+
+ private:
+ StreamInterface* stream_;
+ bool owned_;
+ DISALLOW_EVIL_CONSTRUCTORS(StreamAdapterInterface);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamTap is a non-modifying, pass-through adapter, which copies all data
+// in either direction to the tap. Note that errors or blocking on writing to
+// the tap will prevent further tap writes from occurring.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamTap : public StreamAdapterInterface {
+ public:
+ explicit StreamTap(StreamInterface* stream, StreamInterface* tap);
+
+ void AttachTap(StreamInterface* tap);
+ StreamInterface* DetachTap();
+ StreamResult GetTapResult(int* error);
+
+ // StreamAdapterInterface Interface
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+
+ private:
+ scoped_ptr<StreamInterface> tap_;
+ StreamResult tap_result_;
+ int tap_error_;
+ DISALLOW_EVIL_CONSTRUCTORS(StreamTap);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamSegment adapts a read stream, to expose a subset of the adapted
+// stream's data. This is useful for cases where a stream contains multiple
+// documents concatenated together. StreamSegment can expose a subset of
+// the data as an independent stream, including support for rewinding and
+// seeking.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamSegment : public StreamAdapterInterface {
+ public:
+ // The current position of the adapted stream becomes the beginning of the
+ // segment. If a length is specified, it bounds the length of the segment.
+ explicit StreamSegment(StreamInterface* stream);
+ explicit StreamSegment(StreamInterface* stream, size_t length);
+
+ // StreamAdapterInterface Interface
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual bool SetPosition(size_t position);
+ virtual bool GetPosition(size_t* position) const;
+ virtual bool GetSize(size_t* size) const;
+ virtual bool GetAvailable(size_t* size) const;
+
+ private:
+ size_t start_, pos_, length_;
+ DISALLOW_EVIL_CONSTRUCTORS(StreamSegment);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// NullStream gives errors on read, and silently discards all written data.
+///////////////////////////////////////////////////////////////////////////////
+
+class NullStream : public StreamInterface {
+ public:
+ NullStream();
+ virtual ~NullStream();
+
+ // StreamInterface Interface
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// FileStream is a simple implementation of a StreamInterface, which does not
+// support asynchronous notification.
+///////////////////////////////////////////////////////////////////////////////
+
+class FileStream : public StreamInterface {
+ public:
+ FileStream();
+ virtual ~FileStream();
+
+ // The semantics of filename and mode are the same as stdio's fopen
+ virtual bool Open(const std::string& filename, const char* mode);
+ virtual bool OpenShare(const std::string& filename, const char* mode,
+ int shflag);
+
+ // By default, reads and writes are buffered for efficiency. Disabling
+ // buffering causes writes to block until the bytes on disk are updated.
+ virtual bool DisableBuffering();
+
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+ virtual bool SetPosition(size_t position);
+ virtual bool GetPosition(size_t* position) const;
+ virtual bool GetSize(size_t* size) const;
+ virtual bool GetAvailable(size_t* size) const;
+ virtual bool ReserveSize(size_t size);
+
+ bool Flush();
+
+#if defined(POSIX)
+ // Tries to aquire an exclusive lock on the file.
+ // Use OpenShare(...) on win32 to get similar functionality.
+ bool TryLock();
+ bool Unlock();
+#endif
+
+ // Note: Deprecated in favor of Filesystem::GetFileSize().
+ static bool GetSize(const std::string& filename, size_t* size);
+
+ protected:
+ virtual void DoClose();
+
+ FILE* file_;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(FileStream);
+};
+
+#ifdef POSIX
+// A FileStream that is actually not a file, but the output or input of a
+// sub-command. See "man 3 popen" for documentation of the underlying OS popen()
+// function.
+class POpenStream : public FileStream {
+ public:
+ POpenStream() : wait_status_(-1) {}
+ virtual ~POpenStream();
+
+ virtual bool Open(const std::string& subcommand, const char* mode);
+ // Same as Open(). shflag is ignored.
+ virtual bool OpenShare(const std::string& subcommand, const char* mode,
+ int shflag);
+
+ // Returns the wait status from the last Close() of an Open()'ed stream, or
+ // -1 if no Open()+Close() has been done on this object. Meaning of the number
+ // is documented in "man 2 wait".
+ int GetWaitStatus() const { return wait_status_; }
+
+ protected:
+ virtual void DoClose();
+
+ private:
+ int wait_status_;
+};
+#endif // POSIX
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryStream is a simple implementation of a StreamInterface over in-memory
+// data. Data is read and written at the current seek position. Reads return
+// end-of-stream when they reach the end of data. Writes actually extend the
+// end of data mark.
+///////////////////////////////////////////////////////////////////////////////
+
+class MemoryStreamBase : public StreamInterface {
+ public:
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t bytes, size_t* bytes_read,
+ int* error);
+ virtual StreamResult Write(const void* buffer, size_t bytes,
+ size_t* bytes_written, int* error);
+ virtual void Close();
+ virtual bool SetPosition(size_t position);
+ virtual bool GetPosition(size_t* position) const;
+ virtual bool GetSize(size_t* size) const;
+ virtual bool GetAvailable(size_t* size) const;
+ virtual bool ReserveSize(size_t size);
+
+ char* GetBuffer() { return buffer_; }
+ const char* GetBuffer() const { return buffer_; }
+
+ protected:
+ MemoryStreamBase();
+
+ virtual StreamResult DoReserve(size_t size, int* error);
+
+ // Invariant: 0 <= seek_position <= data_length_ <= buffer_length_
+ char* buffer_;
+ size_t buffer_length_;
+ size_t data_length_;
+ size_t seek_position_;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(MemoryStreamBase);
+};
+
+// MemoryStream dynamically resizes to accomodate written data.
+
+class MemoryStream : public MemoryStreamBase {
+ public:
+ MemoryStream();
+ MemoryStream(const char* data); // Calls SetData(data, strlen(data))
+ MemoryStream(const void* data, size_t length); // Calls SetData(data, length)
+ virtual ~MemoryStream();
+
+ void SetData(const void* data, size_t length);
+
+ protected:
+ virtual StreamResult DoReserve(size_t size, int* error);
+};
+
+// ExternalMemoryStream adapts an external memory buffer, so writes which would
+// extend past the end of the buffer will return end-of-stream.
+
+class ExternalMemoryStream : public MemoryStreamBase {
+ public:
+ ExternalMemoryStream();
+ ExternalMemoryStream(void* data, size_t length);
+
+ void SetData(void* data, size_t length);
+};
+
+// FifoBuffer allows for efficient, thread-safe buffering of data between
+// writer and reader. As the data can wrap around the end of the buffer,
+// MemoryStreamBase can't help us here.
+
+class FifoBuffer : public StreamInterface {
+ public:
+ // Creates a FIFO buffer with the specified capacity.
+ explicit FifoBuffer(size_t length);
+ virtual ~FifoBuffer();
+ // Gets the amount of data currently readable from the buffer.
+ bool GetBuffered(size_t* data_len) const;
+ // Resizes the buffer to the specified capacity. Fails if data_length_ > size
+ bool SetCapacity(size_t length);
+
+ // StreamInterface methods
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t bytes,
+ size_t* bytes_read, int* error);
+ virtual StreamResult Write(const void* buffer, size_t bytes,
+ size_t* bytes_written, int* error);
+ virtual void Close();
+ virtual const void* GetReadData(size_t* data_len);
+ virtual void ConsumeReadData(size_t used);
+ virtual void* GetWriteBuffer(size_t *buf_len);
+ virtual void ConsumeWriteBuffer(size_t used);
+
+ private:
+ StreamState state_; // keeps the opened/closed state of the stream
+ scoped_array<char> buffer_; // the allocated buffer
+ size_t buffer_length_; // size of the allocated buffer
+ size_t data_length_; // amount of readable data in the buffer
+ size_t read_position_; // offset to the readable data
+ Thread* owner_; // stream callbacks are dispatched on this thread
+ mutable CriticalSection crit_; // object lock
+ DISALLOW_EVIL_CONSTRUCTORS(FifoBuffer);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class LoggingAdapter : public StreamAdapterInterface {
+public:
+ LoggingAdapter(StreamInterface* stream, LoggingSeverity level,
+ const std::string& label, bool hex_mode = false);
+
+ void set_label(const std::string& label);
+
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+
+ protected:
+ virtual void OnEvent(StreamInterface* stream, int events, int err);
+
+ private:
+ LoggingSeverity level_;
+ std::string label_;
+ bool hex_mode_;
+ LogMultilineState lms_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LoggingAdapter);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream - Reads/Writes to an external std::string
+///////////////////////////////////////////////////////////////////////////////
+
+class StringStream : public StreamInterface {
+public:
+ StringStream(std::string& str);
+ StringStream(const std::string& str);
+
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+ virtual bool SetPosition(size_t position);
+ virtual bool GetPosition(size_t* position) const;
+ virtual bool GetSize(size_t* size) const;
+ virtual bool GetAvailable(size_t* size) const;
+ virtual bool ReserveSize(size_t size);
+
+private:
+ std::string& str_;
+ size_t read_pos_;
+ bool read_only_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamReference - A reference counting stream adapter
+///////////////////////////////////////////////////////////////////////////////
+
+// Keep in mind that the streams and adapters defined in this file are
+// not thread-safe, so this has limited uses.
+
+// A StreamRefCount holds the reference count and a pointer to the
+// wrapped stream. It deletes the wrapped stream when there are no
+// more references. We can then have multiple StreamReference
+// instances pointing to one StreamRefCount, all wrapping the same
+// stream.
+
+class StreamReference : public StreamAdapterInterface {
+ class StreamRefCount;
+ public:
+ // Constructor for the first reference to a stream
+ // Note: get more references through NewReference(). Use this
+ // constructor only once on a given stream.
+ explicit StreamReference(StreamInterface* stream)
+ : StreamAdapterInterface(stream, false) {
+ // owner set to false so the destructor does not free the stream.
+ stream_ref_count_ = new StreamRefCount(stream);
+ }
+ StreamInterface* GetStream() { return stream(); }
+ StreamInterface* NewReference() {
+ stream_ref_count_->AddReference();
+ return new StreamReference(stream_ref_count_, stream());
+ }
+ virtual ~StreamReference() {
+ stream_ref_count_->Release();
+ }
+
+ private:
+ class StreamRefCount {
+ public:
+ explicit StreamRefCount(StreamInterface* stream)
+ : stream_(stream), ref_count_(1) {
+ }
+ void AddReference() {
+ CritScope lock(&cs_);
+ ++ref_count_;
+ }
+ void Release() {
+ int ref_count;
+ { // Atomic ops would have been a better fit here.
+ CritScope lock(&cs_);
+ ref_count = --ref_count_;
+ }
+ if (ref_count == 0) {
+ delete stream_;
+ delete this;
+ }
+ }
+ private:
+ StreamInterface* stream_;
+ int ref_count_;
+ CriticalSection cs_;
+ DISALLOW_EVIL_CONSTRUCTORS(StreamRefCount);
+ };
+
+ // Constructor for adding references
+ explicit StreamReference(StreamRefCount* stream_ref_count,
+ StreamInterface* stream)
+ : StreamAdapterInterface(stream, false),
+ stream_ref_count_(stream_ref_count) {
+ }
+ StreamRefCount* stream_ref_count_;
+ DISALLOW_EVIL_CONSTRUCTORS(StreamReference);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Flow attempts to move bytes from source to sink via buffer of size
+// buffer_len. The function returns SR_SUCCESS when source reaches
+// end-of-stream (returns SR_EOS), and all the data has been written successful
+// to sink. Alternately, if source returns SR_BLOCK or SR_ERROR, or if sink
+// returns SR_BLOCK, SR_ERROR, or SR_EOS, then the function immediately returns
+// with the unexpected StreamResult value.
+// data_len is the length of the valid data in buffer. in case of error
+// this is the data that read from source but can't move to destination.
+// as a pass in parameter, it indicates data in buffer that should move to sink
+StreamResult Flow(StreamInterface* source,
+ char* buffer, size_t buffer_len,
+ StreamInterface* sink, size_t* data_len = NULL);
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_STREAM_H__
diff --git a/third_party/libjingle/source/talk/base/stringdigest.cc b/third_party/libjingle/source/talk/base/stringdigest.cc
new file mode 100644
index 0000000..1f98124
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/stringdigest.cc
@@ -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.
+ */
+
+
+#include "talk/base/md5.h"
+#include "talk/base/stringdigest.h"
+#include "talk/base/stringencode.h"
+
+namespace talk_base {
+
+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;
+ for (int i=0; i<16; ++i) {
+ hex_digest += hex_encode(digest[i] >> 4);
+ hex_digest += hex_encode(digest[i] & 0xf);
+ }
+ return hex_digest;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/stringdigest.h b/third_party/libjingle/source/talk/base/stringdigest.h
new file mode 100644
index 0000000..d75d845
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/stringdigest.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 TALK_BASE_STRINGDIGEST_H__
+#define TALK_BASE_STRINGDIGEST_H__
+
+#include <string>
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Message Digest Utilities
+//////////////////////////////////////////////////////////////////////
+
+// Compute the MD5 message digest of data, and return it in
+std::string MD5(const std::string& data);
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_STRINGDIGEST_H__
diff --git a/third_party/libjingle/source/talk/base/stringencode.cc b/third_party/libjingle/source/talk/base/stringencode.cc
new file mode 100644
index 0000000..d588514
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/stringencode.cc
@@ -0,0 +1,579 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/stringencode.h"
+
+#include <cstdio>
+#include <cstdlib>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/stringutils.h"
+
+namespace talk_base {
+
+/////////////////////////////////////////////////////////////////////////////
+// String Encoding Utilities
+/////////////////////////////////////////////////////////////////////////////
+
+static const char HEX[] = "0123456789abcdef";
+
+char hex_encode(unsigned char val) {
+ ASSERT(val < 16);
+ return (val < 16) ? HEX[val] : '!';
+}
+
+bool hex_decode(char ch, unsigned char* val) {
+ if ((ch >= '0') && (ch <= '9')) {
+ *val = ch - '0';
+ } else if ((ch >= 'A') && (ch <= 'Z')) {
+ *val = (ch - 'A') + 10;
+ } else if ((ch >= 'a') && (ch <= 'z')) {
+ *val = (ch - 'a') + 10;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+size_t escape(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ const char * illegal, char escape) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ char ch = source[srcpos++];
+ if ((ch == escape) || ::strchr(illegal, ch)) {
+ if (bufpos + 2 >= buflen)
+ break;
+ buffer[bufpos++] = escape;
+ }
+ buffer[bufpos++] = ch;
+ }
+
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t unescape(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ char escape) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ char ch = source[srcpos++];
+ if ((ch == escape) && (srcpos < srclen)) {
+ ch = source[srcpos++];
+ }
+ buffer[bufpos++] = ch;
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ const char * illegal, char escape) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ char ch = source[srcpos++];
+ if ((ch != escape) && !::strchr(illegal, ch)) {
+ buffer[bufpos++] = ch;
+ } else if (bufpos + 3 >= buflen) {
+ break;
+ } else {
+ buffer[bufpos+0] = escape;
+ buffer[bufpos+1] = hex_encode((static_cast<unsigned char>(ch) >> 4) & 0xF);
+ buffer[bufpos+2] = hex_encode((static_cast<unsigned char>(ch) ) & 0xF);
+ bufpos += 3;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ char escape) {
+ if (buflen <= 0)
+ return 0;
+
+ unsigned char h1, h2;
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ char ch = source[srcpos++];
+ if ((ch == escape)
+ && (srcpos + 1 < srclen)
+ && hex_decode(source[srcpos], &h1)
+ && hex_decode(source[srcpos+1], &h2)) {
+ buffer[bufpos++] = (h1 << 4) | h2;
+ srcpos += 2;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+const char* unsafe_filename_characters() {
+ // It might be better to have a single specification which is the union of
+ // all operating systems, unless one system is overly restrictive.
+#ifdef WIN32
+ return "\\/:*?\"<>|";
+#else // !WIN32
+ // TODO
+ ASSERT(false);
+ return "";
+#endif // !WIN23
+}
+
+const unsigned char URL_UNSAFE = 0x1; // 0-33 "#$%&+,/:;<=>?@[\]^`{|} 127
+const unsigned char XML_UNSAFE = 0x2; // "&'<>
+const unsigned char HTML_UNSAFE = 0x2; // "&'<>
+
+// ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 6 5 7 8 9 : ; < = > ?
+//@ 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 { | } ~
+
+const unsigned char ASCII_CLASS[128] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,0,3,1,1,1,3,2,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,3,1,3,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,
+};
+
+size_t url_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ if (NULL == buffer)
+ return srclen * 3 + 1;
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos++];
+ if ((ch < 128) && (ASCII_CLASS[ch] & URL_UNSAFE)) {
+ if (bufpos + 3 >= buflen) {
+ break;
+ }
+ buffer[bufpos+0] = '%';
+ buffer[bufpos+1] = hex_encode((ch >> 4) & 0xF);
+ buffer[bufpos+2] = hex_encode((ch ) & 0xF);
+ bufpos += 3;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t url_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ if (NULL == buffer)
+ return srclen + 1;
+ if (buflen <= 0)
+ return 0;
+
+ unsigned char h1, h2;
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos++];
+ if (ch == '+') {
+ buffer[bufpos++] = ' ';
+ } else if ((ch == '%')
+ && (srcpos + 1 < srclen)
+ && hex_decode(source[srcpos], &h1)
+ && hex_decode(source[srcpos+1], &h2))
+ {
+ buffer[bufpos++] = (h1 << 4) | h2;
+ srcpos += 2;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) {
+ const unsigned char* s = reinterpret_cast<const unsigned char*>(source);
+ if ((s[0] & 0x80) == 0x00) { // Check s[0] == 0xxxxxxx
+ *value = s[0];
+ return 1;
+ }
+ if ((srclen < 2) || ((s[1] & 0xC0) != 0x80)) { // Check s[1] != 10xxxxxx
+ return 0;
+ }
+ // Accumulate the trailer byte values in value16, and combine it with the
+ // relevant bits from s[0], once we've determined the sequence length.
+ unsigned long value16 = (s[1] & 0x3F);
+ if ((s[0] & 0xE0) == 0xC0) { // Check s[0] == 110xxxxx
+ *value = ((s[0] & 0x1F) << 6) | value16;
+ return 2;
+ }
+ if ((srclen < 3) || ((s[2] & 0xC0) != 0x80)) { // Check s[2] != 10xxxxxx
+ return 0;
+ }
+ value16 = (value16 << 6) | (s[2] & 0x3F);
+ if ((s[0] & 0xF0) == 0xE0) { // Check s[0] == 1110xxxx
+ *value = ((s[0] & 0x0F) << 12) | value16;
+ return 3;
+ }
+ if ((srclen < 4) || ((s[3] & 0xC0) != 0x80)) { // Check s[3] != 10xxxxxx
+ return 0;
+ }
+ value16 = (value16 << 6) | (s[3] & 0x3F);
+ if ((s[0] & 0xF8) == 0xF0) { // Check s[0] == 11110xxx
+ *value = ((s[0] & 0x07) << 18) | value16;
+ return 4;
+ }
+ return 0;
+}
+
+size_t utf8_encode(char* buffer, size_t buflen, unsigned long value) {
+ if ((value <= 0x7F) && (buflen >= 1)) {
+ buffer[0] = static_cast<unsigned char>(value);
+ return 1;
+ }
+ if ((value <= 0x7FF) && (buflen >= 2)) {
+ buffer[0] = 0xC0 | static_cast<unsigned char>(value >> 6);
+ buffer[1] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+ return 2;
+ }
+ if ((value <= 0xFFFF) && (buflen >= 3)) {
+ buffer[0] = 0xE0 | static_cast<unsigned char>(value >> 12);
+ buffer[1] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
+ buffer[2] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+ return 3;
+ }
+ if ((value <= 0x1FFFFF) && (buflen >= 4)) {
+ buffer[0] = 0xF0 | static_cast<unsigned char>(value >> 18);
+ buffer[1] = 0x80 | static_cast<unsigned char>((value >> 12) & 0x3F);
+ buffer[2] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
+ buffer[3] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+ return 4;
+ }
+ return 0;
+}
+
+size_t html_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos];
+ if (ch < 128) {
+ srcpos += 1;
+ if (ASCII_CLASS[ch] & HTML_UNSAFE) {
+ const char * escseq = 0;
+ size_t esclen = 0;
+ switch (ch) {
+ case '<': escseq = "&lt;"; esclen = 4; break;
+ case '>': escseq = "&gt;"; esclen = 4; break;
+ case '\'': escseq = "&#39;"; esclen = 5; break;
+ case '\"': escseq = "&quot;"; esclen = 6; break;
+ case '&': escseq = "&amp;"; esclen = 5; break;
+ default: ASSERT(false);
+ }
+ if (bufpos + esclen >= buflen) {
+ break;
+ }
+ memcpy(buffer + bufpos, escseq, esclen);
+ bufpos += esclen;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ } else {
+ // Largest value is 0x1FFFFF => &#2097151; (10 characters)
+ char escseq[11];
+ unsigned long val;
+ if (size_t vallen = utf8_decode(&source[srcpos], srclen - srcpos, &val)) {
+ srcpos += vallen;
+ } else {
+ // Not a valid utf8 sequence, just use the raw character.
+ val = static_cast<unsigned char>(source[srcpos++]);
+ }
+ size_t esclen = sprintfn(escseq, ARRAY_SIZE(escseq), "&#%lu;", val);
+ if (bufpos + esclen >= buflen) {
+ break;
+ }
+ memcpy(buffer + bufpos, escseq, esclen);
+ bufpos += esclen;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t html_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ return xml_decode(buffer, buflen, source, srclen);
+}
+
+size_t xml_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos++];
+ if ((ch < 128) && (ASCII_CLASS[ch] & XML_UNSAFE)) {
+ const char * escseq = 0;
+ size_t esclen = 0;
+ switch (ch) {
+ case '<': escseq = "&lt;"; esclen = 4; break;
+ case '>': escseq = "&gt;"; esclen = 4; break;
+ case '\'': escseq = "&apos;"; esclen = 6; break;
+ case '\"': escseq = "&quot;"; esclen = 6; break;
+ case '&': escseq = "&amp;"; esclen = 5; break;
+ default: ASSERT(false);
+ }
+ if (bufpos + esclen >= buflen) {
+ break;
+ }
+ memcpy(buffer + bufpos, escseq, esclen);
+ bufpos += esclen;
+ } else {
+ buffer[bufpos++] = ch;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t xml_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+ unsigned char ch = source[srcpos++];
+ if (ch != '&') {
+ buffer[bufpos++] = ch;
+ } else if ((srcpos + 2 < srclen)
+ && (memcmp(source + srcpos, "lt;", 3) == 0)) {
+ buffer[bufpos++] = '<';
+ srcpos += 3;
+ } else if ((srcpos + 2 < srclen)
+ && (memcmp(source + srcpos, "gt;", 3) == 0)) {
+ buffer[bufpos++] = '>';
+ srcpos += 3;
+ } else if ((srcpos + 4 < srclen)
+ && (memcmp(source + srcpos, "apos;", 5) == 0)) {
+ buffer[bufpos++] = '\'';
+ srcpos += 5;
+ } else if ((srcpos + 4 < srclen)
+ && (memcmp(source + srcpos, "quot;", 5) == 0)) {
+ buffer[bufpos++] = '\"';
+ srcpos += 5;
+ } else if ((srcpos + 3 < srclen)
+ && (memcmp(source + srcpos, "amp;", 4) == 0)) {
+ buffer[bufpos++] = '&';
+ srcpos += 4;
+ } else if ((srcpos < srclen) && (source[srcpos] == '#')) {
+ int int_base = 10;
+ if ((srcpos + 1 < srclen) && (source[srcpos+1] == 'x')) {
+ int_base = 16;
+ srcpos += 1;
+ }
+ char * ptr;
+ // TODO: Fix hack (ptr may go past end of data)
+ unsigned long val = strtoul(source + srcpos + 1, &ptr, int_base);
+ if ((static_cast<size_t>(ptr - source) < srclen) && (*ptr == ';')) {
+ srcpos = ptr - source + 1;
+ } else {
+ // Not a valid escape sequence.
+ break;
+ }
+ if (size_t esclen = utf8_encode(buffer + bufpos, buflen - bufpos, val)) {
+ bufpos += esclen;
+ } else {
+ // Not enough room to encode the character, or illegal character
+ break;
+ }
+ } else {
+ // Unrecognized escape sequence.
+ break;
+ }
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+std::string hex_encode(const char * source, size_t srclen) {
+ const size_t kBufferSize = srclen * 2 + 1;
+ char* buffer = STACK_ARRAY(char, kBufferSize);
+ size_t length = hex_encode(buffer, kBufferSize, source, srclen);
+ return std::string(buffer, length);
+}
+
+size_t hex_encode(char * buffer, size_t buflen,
+ const char * csource, size_t srclen) {
+ ASSERT(NULL != buffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ const unsigned char * bsource =
+ reinterpret_cast<const unsigned char *>(csource);
+
+ size_t srcpos = 0, bufpos = 0;
+ srclen = _min(srclen, (buflen - 1) / 2);
+ while (srcpos < srclen) {
+ unsigned char ch = bsource[srcpos++];
+ buffer[bufpos ] = hex_encode((ch >> 4) & 0xF);
+ buffer[bufpos+1] = hex_encode((ch ) & 0xF);
+ bufpos += 2;
+ }
+ buffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t hex_decode(char * cbuffer, size_t buflen,
+ const char * source, size_t srclen) {
+ ASSERT(NULL != cbuffer); // TODO: estimate output size
+ if (buflen <= 0)
+ return 0;
+
+ unsigned char * bbuffer = reinterpret_cast<unsigned char *>(cbuffer);
+
+ unsigned char h1, h2;
+ size_t srcpos = 0, bufpos = 0;
+ while ((srcpos + 1 < srclen)
+ && (bufpos + 1 < buflen)
+ && hex_decode(source[srcpos], &h1)
+ && hex_decode(source[srcpos+1], &h2))
+ {
+ bbuffer[bufpos++] = (h1 << 4) | h2;
+ srcpos += 2;
+ }
+ bbuffer[bufpos] = '\0';
+ return bufpos;
+}
+
+size_t transform(std::string& value, size_t maxlen, const std::string& source,
+ Transform t) {
+ char* buffer = STACK_ARRAY(char, maxlen + 1);
+ size_t length = t(buffer, maxlen + 1, source.data(), source.length());
+ value.assign(buffer, length);
+ return length;
+}
+
+std::string s_transform(const std::string& source, Transform t) {
+ // Ask transformation function to approximate the destination size (returns upper bound)
+ size_t maxlen = t(NULL, 0, source.data(), source.length());
+ char * buffer = STACK_ARRAY(char, maxlen);
+ size_t len = t(buffer, maxlen, source.data(), source.length());
+ std::string result(buffer, len);
+ return result;
+}
+
+size_t split(const std::string& source, char delimiter,
+ std::vector<std::string>* fields)
+{
+ ASSERT(NULL != fields);
+ fields->clear();
+ size_t last = 0;
+ for (size_t i=0; i<source.length(); ++i) {
+ if (source[i] == delimiter) {
+ fields->push_back(source.substr(last, i - last));
+ last = i+1;
+ }
+ }
+ fields->push_back(source.substr(last, source.length() - last));
+ return fields->size();
+}
+
+std::string split_one(const std::string& source, char delimiter, int* index) {
+ std::string substring;
+ size_t start = source.find_first_not_of(delimiter, *index);
+ size_t end = source.find_first_of(delimiter, start);
+ if (start != std::string::npos) {
+ if (end == std::string::npos) {
+ substring = source.substr(start);
+ *index = source.length();
+ } else {
+ substring = source.substr(start, end - start);
+ *index = source.find_first_not_of(delimiter, end);
+ }
+ }
+ return substring;
+}
+
+char make_char_safe_for_filename(char c) {
+ if (c < 32)
+ return '_';
+
+ switch (c) {
+ case '<':
+ case '>':
+ case ':':
+ case '"':
+ case '/':
+ case '\\':
+ case '|':
+ case '*':
+ case '?':
+ return '_';
+
+ default:
+ return c;
+ }
+}
+
+/*
+void sprintf(std::string& value, size_t maxlen, const char * format, ...) {
+ char * buffer = STACK_ARRAY(char, maxlen + 1);
+ va_list args;
+ va_start(args, format);
+ value.assign(buffer, vsprintfn(buffer, maxlen + 1, format, args));
+ va_end(args);
+}
+*/
+
+/////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/stringencode.h b/third_party/libjingle/source/talk/base/stringencode.h
new file mode 100644
index 0000000..143b220
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/stringencode.h
@@ -0,0 +1,183 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_STRINGENCODE_H__
+#define TALK_BASE_STRINGENCODE_H__
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// String Encoding Utilities
+//////////////////////////////////////////////////////////////////////
+
+// Convert an unsigned value from 0 to 15 to the hex character equivalent...
+char hex_encode(unsigned char val);
+// ...and vice-versa.
+bool hex_decode(char ch, unsigned char* val);
+
+// Convert an unsigned value to it's utf8 representation. Returns the length
+// of the encoded string, or 0 if the encoding is longer than buflen - 1.
+size_t utf8_encode(char* buffer, size_t buflen, unsigned long value);
+// Decode the utf8 encoded value pointed to by source. Returns the number of
+// bytes used by the encoding, or 0 if the encoding is invalid.
+size_t utf8_decode(const char* source, size_t srclen, unsigned long* value);
+
+// Escaping prefixes illegal characters with the escape character. Compact, but
+// illegal characters still appear in the string.
+size_t escape(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ const char * illegal, char escape);
+// Note: in-place unescaping (buffer == source) is allowed.
+size_t unescape(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ char escape);
+
+// Encoding replaces illegal characters with the escape character and 2 hex
+// chars, so it's a little less compact than escape, but completely removes
+// illegal characters. note that hex digits should not be used as illegal
+// characters.
+size_t encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ const char * illegal, char escape);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen,
+ char escape);
+
+// Returns a list of characters that may be unsafe for use in the name of a
+// file, suitable for passing to the 'illegal' member of escape or encode.
+const char* unsafe_filename_characters();
+
+// url_encode is an encode operation with a predefined set of illegal characters
+// and escape character (for use in URLs, obviously).
+size_t url_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t url_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+
+// html_encode prevents data embedded in html from containing markup.
+size_t html_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t html_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+
+// xml_encode makes data suitable for inside xml attributes and values.
+size_t xml_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t xml_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+
+// hex_encode shows the hex representation of binary data in ascii.
+size_t hex_encode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+size_t hex_decode(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+// helper funtion for hex_encode
+std::string hex_encode(const char * source, size_t srclen);
+
+// Apply any suitable string transform (including the ones above) to an STL
+// string. Stack-allocated temporary space is used for the transformation,
+// so value and source may refer to the same string.
+typedef size_t (*Transform)(char * buffer, size_t buflen,
+ const char * source, size_t srclen);
+size_t transform(std::string& value, size_t maxlen, const std::string& source,
+ Transform t);
+
+// Return the result of applying transform t to source.
+std::string s_transform(const std::string& source, Transform t);
+
+// Convenience wrappers
+inline std::string s_url_encode(const std::string& source) {
+ return s_transform(source, url_encode);
+}
+inline std::string s_url_decode(const std::string& source) {
+ return s_transform(source, url_decode);
+}
+
+// Splits the source string into multiple fields separated by delimiter.
+size_t split(const std::string& source, char delimiter,
+ std::vector<std::string>* fields);
+
+// Returns the first part of a string separated by delimiter.
+// Index indicates the location to start parsing in the string and
+// is increased to the start of the next substring.
+std::string split_one(const std::string& source, char delimiter, int* index);
+
+// Safe sprintf to std::string
+//void sprintf(std::string& value, size_t maxlen, const char * format, ...)
+// PRINTF_FORMAT(3);
+
+// Convert arbitrary values to/from a string.
+
+template <class T>
+static bool ToString(const T &t, std::string* s) {
+ std::ostringstream oss;
+ oss << t;
+ *s = oss.str();
+ return !oss.fail();
+}
+
+template <class T>
+static bool FromString(const std::string& s, T* t) {
+ std::istringstream iss(s);
+ iss >> *t;
+ return !iss.fail();
+}
+
+// Inline versions of the string conversion routines.
+
+template<typename T>
+static inline std::string ToString(T val) {
+ std::string str; ToString(val, &str); return str;
+}
+
+template<typename T>
+static inline T FromString(const std::string& str) {
+ T val; FromString(str, &val); return val;
+}
+
+template<typename T>
+static inline T FromString(const T& defaultValue, const std::string& str) {
+ T val(defaultValue); FromString(str, &val); return val;
+}
+
+// simple function to strip out characters which shouldn't be
+// used in filenames
+char make_char_safe_for_filename(char c);
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_STRINGENCODE_H__
diff --git a/third_party/libjingle/source/talk/base/stringutils.cc b/third_party/libjingle/source/talk/base/stringutils.cc
new file mode 100644
index 0000000..44dc7c8
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/stringutils.cc
@@ -0,0 +1,132 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/stringutils.h"
+#include "talk/base/common.h"
+
+namespace talk_base {
+
+bool memory_check(const void* memory, int c, size_t count) {
+ const char* char_memory = static_cast<const char*>(memory);
+ char char_c = static_cast<char>(c);
+ for (size_t i=0; i<count; ++i) {
+ if (char_memory[i] != char_c) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool string_match(const char* target, const char* pattern) {
+ while (*pattern) {
+ if (*pattern == '*') {
+ if (!*++pattern) {
+ return true;
+ }
+ while (*target) {
+ if ((toupper(*pattern) == toupper(*target))
+ && string_match(target + 1, pattern + 1)) {
+ return true;
+ }
+ ++target;
+ }
+ return false;
+ } else {
+ if (toupper(*pattern) != toupper(*target)) {
+ return false;
+ }
+ ++target;
+ ++pattern;
+ }
+ }
+ return !*target;
+}
+
+#ifdef WIN32
+int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n,
+ CharacterTransformation transformation) {
+ wchar_t c1, c2;
+ while (true) {
+ if (n-- == 0) return 0;
+ c1 = transformation(*s1);
+ // Double check that characters are not UTF-8
+ ASSERT(static_cast<unsigned char>(*s2) < 128);
+ // Note: *s2 gets implicitly promoted to wchar_t
+ c2 = transformation(*s2);
+ if (c1 != c2) return (c1 < c2) ? -1 : 1;
+ if (!c1) return 0;
+ ++s1;
+ ++s2;
+ }
+}
+
+size_t asccpyn(wchar_t* buffer, size_t buflen,
+ const char* source, size_t srclen) {
+ if (buflen <= 0)
+ return 0;
+
+ if (srclen == SIZE_UNKNOWN) {
+ srclen = strlenn(source, buflen - 1);
+ } else if (srclen >= buflen) {
+ srclen = buflen - 1;
+ }
+#if _DEBUG
+ // Double check that characters are not UTF-8
+ for (size_t pos = 0; pos < srclen; ++pos)
+ ASSERT(static_cast<unsigned char>(source[pos]) < 128);
+#endif // _DEBUG
+ std::copy(source, source + srclen, buffer);
+ buffer[srclen] = 0;
+ return srclen;
+}
+
+#endif // WIN32
+
+void replace_substrs(const char *search,
+ size_t search_len,
+ const char *replace,
+ size_t replace_len,
+ std::string *s) {
+ size_t pos = 0;
+ while ((pos = s->find(search, pos, search_len)) != std::string::npos) {
+ s->replace(pos, search_len, replace, replace_len);
+ pos += replace_len;
+ }
+}
+
+bool starts_with(const char *s1, const char *s2) {
+ while (*s2 != '\0') {
+ if (*s1 != *s2) {
+ return false;
+ }
+ s1++;
+ s2++;
+ }
+ return true;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/stringutils.h b/third_party/libjingle/source/talk/base/stringutils.h
new file mode 100644
index 0000000..686a73e
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/stringutils.h
@@ -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.
+ */
+
+#ifndef TALK_BASE_STRINGUTILS_H__
+#define TALK_BASE_STRINGUTILS_H__
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#ifdef WIN32
+#include <malloc.h>
+#include <wchar.h>
+#define alloca _alloca
+#endif // WIN32
+#ifdef POSIX
+#include <alloca.h>
+#endif // POSIX
+
+#include <cstring>
+#include <string>
+
+#include "talk/base/basictypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Generic string/memory utilities
+///////////////////////////////////////////////////////////////////////////////
+
+#define STACK_ARRAY(TYPE, LEN) static_cast<TYPE*>(::alloca((LEN)*sizeof(TYPE)))
+
+namespace talk_base {
+
+// Complement to memset. Verifies memory consists of count bytes of value c.
+bool memory_check(const void* memory, int c, size_t count);
+
+// Determines whether the simple wildcard pattern matches target.
+// Alpha characters in pattern match case-insensitively.
+// Asterisks in pattern match 0 or more characters.
+// Ex: string_match("www.TEST.GOOGLE.COM", "www.*.com") -> true
+bool string_match(const char* target, const char* pattern);
+
+} // namespace talk_base
+
+///////////////////////////////////////////////////////////////////////////////
+// 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
+//
+// It's not clear if we will ever use wchar_t strings on unix. In theory,
+// all strings should be Utf8 all the time, except when interfacing with Win32
+// APIs that require Utf16.
+///////////////////////////////////////////////////////////////////////////////
+
+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 const wchar_t* strstr(const wchar_t* haystack, const wchar_t* needle) {
+ return wcsstr(haystack, needle);
+}
+#ifndef vsnprintf
+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);
+}
+#endif // !vsnprintf
+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 talk_base {
+
+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;
+}
+
+// Safe versions of strncpy, strncat, snprintf and vsnprintf that always
+// null-terminate.
+
+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;
+}
+
+template<class CTYPE>
+size_t strcatn(CTYPE* buffer, size_t buflen,
+ const CTYPE* source, size_t srclen = SIZE_UNKNOWN) {
+ if (buflen <= 0)
+ return 0;
+
+ size_t bufpos = strlenn(buffer, buflen - 1);
+ return bufpos + strcpyn(buffer + bufpos, buflen - bufpos, source, srclen);
+}
+
+// Some compilers (clang specifically) require vsprintfn be defined before
+// sprintfn.
+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;
+}
+
+template<class CTYPE>
+size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...);
+/* This works to get GCC to notice printf argument mismatches, but then complains of missing implementation of sprintfn<char>
+template<>
+size_t sprintfn(char* buffer, size_t buflen, const char* format, ...)
+GCC_ATTR(format(printf,3,4));
+*/
+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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// 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
+
+// Replaces all occurrences of "search" with "replace".
+void replace_substrs(const char *search,
+ size_t search_len,
+ const char *replace,
+ size_t replace_len,
+ std::string *s);
+
+// True iff s1 starts with s2.
+bool starts_with(const char *s1, const char *s2);
+
+} // namespace talk_base
+
+#endif // TALK_BASE_STRINGUTILS_H__
diff --git a/third_party/libjingle/source/talk/base/task.cc b/third_party/libjingle/source/talk/base/task.cc
new file mode 100644
index 0000000..dfd29f7
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/task.cc
@@ -0,0 +1,296 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/task.h"
+#include "talk/base/common.h"
+#include "talk/base/taskrunner.h"
+
+namespace talk_base {
+
+int32 Task::unique_id_seed_ = 0;
+
+Task::Task(TaskParent *parent)
+ : TaskParent(this, parent),
+ state_(STATE_INIT),
+ blocked_(false),
+ done_(false),
+ aborted_(false),
+ busy_(false),
+ error_(false),
+ start_time_(0),
+ timeout_time_(0),
+ timeout_seconds_(0),
+ timeout_suspended_(false) {
+ unique_id_ = unique_id_seed_++;
+
+ // sanity check that we didn't roll-over our id seed
+ assert(unique_id_ < unique_id_seed_);
+}
+
+Task::~Task() {
+ // Is this task being deleted in the correct manner?
+ ASSERT(!done_ || GetRunner()->is_ok_to_delete(this));
+ ASSERT(state_ == STATE_INIT || done_);
+ ASSERT(state_ == STATE_INIT || blocked_);
+
+ // If the task is being deleted without being done, it
+ // means that it hasn't been removed from its parent.
+ // This happens if a task is deleted outside of TaskRunner.
+ if (!done_) {
+ Stop();
+ }
+}
+
+int64 Task::CurrentTime() {
+ return GetRunner()->CurrentTime();
+}
+
+int64 Task::ElapsedTime() {
+ return CurrentTime() - start_time_;
+}
+
+void Task::Start() {
+ if (state_ != STATE_INIT)
+ return;
+ // Set the start time before starting the task. Otherwise if the task
+ // finishes quickly and deletes the Task object, setting start_time_
+ // will crash.
+ start_time_ = CurrentTime();
+ GetRunner()->StartTask(this);
+}
+
+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();
+#ifdef _DEBUG
+ // verify that stop removed this from its parent
+ assert(!parent()->IsChildTask(this));
+#endif
+ 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;
+ // Let the timeout continue
+ } else {
+ state_ = new_state;
+ blocked_ = false;
+ ResetTimeout();
+ }
+
+ 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();
+#if _DEBUG
+ // verify that stop removed this from its parent
+ assert(!parent()->IsChildTask(this));
+#endif
+ blocked_ = true;
+ }
+}
+
+void Task::Abort(bool nowake) {
+ // Why only check for done_ (instead of "aborted_ || done_")?
+ //
+ // If aborted_ && !done_, it means the logic for aborting still
+ // needs to be executed (because busy_ must have been true when
+ // Abort() was previously called).
+ if (done_)
+ return;
+ aborted_ = true;
+ if (!busy_) {
+ done_ = true;
+ blocked_ = true;
+ error_ = true;
+
+ // "done_" is set before calling "Stop()" to ensure that this code
+ // doesn't execute more than once (recursively) for the same task.
+ Stop();
+#ifdef _DEBUG
+ // verify that stop removed this from its parent
+ assert(!parent()->IsChildTask(this));
+#endif
+ if (!nowake) {
+ // WakeTasks to self-delete.
+ // Don't call Wake() because it is a no-op after "done_" is set.
+ // Even if Wake() did run, it clears "blocked_" which isn't desireable.
+ GetRunner()->WakeTasks();
+ }
+ }
+}
+
+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) {
+ int newstate = STATE_ERROR;
+
+ if (TimedOut()) {
+ ClearTimeout();
+ newstate = OnTimeout();
+ SignalTimeout();
+ } else {
+ switch (state) {
+ case STATE_INIT:
+ newstate = STATE_START;
+ break;
+ case STATE_START:
+ newstate = ProcessStart();
+ break;
+ case STATE_RESPONSE:
+ newstate = ProcessResponse();
+ break;
+ case STATE_DONE:
+ case STATE_ERROR:
+ newstate = STATE_BLOCKED;
+ break;
+ }
+ }
+
+ return newstate;
+}
+
+void Task::Stop() {
+ // No need to wake because we're either awake or in abort
+ TaskParent::OnStopped(this);
+}
+
+void Task::set_timeout_seconds(const int timeout_seconds) {
+ timeout_seconds_ = timeout_seconds;
+ ResetTimeout();
+}
+
+bool Task::TimedOut() {
+ return timeout_seconds_ &&
+ timeout_time_ &&
+ CurrentTime() >= timeout_time_;
+}
+
+void Task::ResetTimeout() {
+ int64 previous_timeout_time = timeout_time_;
+ bool timeout_allowed = (state_ != STATE_INIT)
+ && (state_ != STATE_DONE)
+ && (state_ != STATE_ERROR);
+ if (timeout_seconds_ && timeout_allowed && !timeout_suspended_)
+ timeout_time_ = CurrentTime() +
+ (timeout_seconds_ * kSecToMsec * kMsecTo100ns);
+ else
+ timeout_time_ = 0;
+
+ GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
+}
+
+void Task::ClearTimeout() {
+ int64 previous_timeout_time = timeout_time_;
+ timeout_time_ = 0;
+ GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
+}
+
+void Task::SuspendTimeout() {
+ if (!timeout_suspended_) {
+ timeout_suspended_ = true;
+ ResetTimeout();
+ }
+}
+
+void Task::ResumeTimeout() {
+ if (timeout_suspended_) {
+ timeout_suspended_ = false;
+ ResetTimeout();
+ }
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/task.h b/third_party/libjingle/source/talk/base/task.h
new file mode 100644
index 0000000..10e6f5c
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/task.h
@@ -0,0 +1,194 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_TASK_H__
+#define TALK_BASE_TASK_H__
+
+#include <string>
+#include "talk/base/basictypes.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/taskparent.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 sometime 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 information, 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.
+//
+// Also notice that Task has some primitive built-in timeout functionality.
+//
+// A timeout is defined as "the task stays in STATE_BLOCKED longer than
+// timeout_seconds_."
+//
+// Descendant classes can override this behavior by calling the
+// various protected methods to change the timeout behavior. For
+// instance, a descendand might call SuspendTimeout() when it knows
+// that it isn't waiting for anything that might timeout, but isn't
+// yet in the STATE_DONE state.
+//
+
+namespace talk_base {
+
+// Executes a sequence of steps
+class Task : public TaskParent {
+ public:
+ Task(TaskParent *parent);
+ virtual ~Task();
+
+ int32 unique_id() { return unique_id_; }
+
+ 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_; }
+ int64 ElapsedTime();
+
+ // Called from outside to stop task without any more callbacks
+ void Abort(bool nowake = false);
+
+ bool TimedOut();
+
+ int64 timeout_time() const { return timeout_time_; }
+ int timeout_seconds() const { return timeout_seconds_; }
+ void set_timeout_seconds(int timeout_seconds);
+
+ sigslot::signal0<> SignalTimeout;
+
+ // Called inside the task to signal that the task may be unblocked
+ void Wake();
+
+ 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 to advise that the task should wake and signal an error
+ void Error();
+
+ int64 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; }
+
+ void ResetTimeout();
+ void ClearTimeout();
+
+ void SuspendTimeout();
+ void ResumeTimeout();
+
+ protected:
+ virtual int OnTimeout() {
+ // by default, we are finished after timing out
+ return STATE_DONE;
+ }
+
+ private:
+ void Done();
+
+ int state_;
+ bool blocked_;
+ bool done_;
+ bool aborted_;
+ bool busy_;
+ bool error_;
+ int64 start_time_;
+ int64 timeout_time_;
+ int timeout_seconds_;
+ bool timeout_suspended_;
+ int32 unique_id_;
+
+ static int32 unique_id_seed_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_TASK_H__
diff --git a/third_party/libjingle/source/talk/base/taskparent.cc b/third_party/libjingle/source/talk/base/taskparent.cc
new file mode 100644
index 0000000..0d8f5de
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/taskparent.cc
@@ -0,0 +1,112 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/base/taskparent.h"
+
+#include "talk/base/task.h"
+#include "talk/base/taskrunner.h"
+
+namespace talk_base {
+
+TaskParent::TaskParent(Task* derived_instance, TaskParent *parent)
+ : parent_(parent) {
+ assert(derived_instance);
+ assert(parent);
+ runner_ = parent->GetRunner();
+ parent_->AddChild(derived_instance);
+ Initialize();
+}
+
+TaskParent::TaskParent(TaskRunner *derived_instance)
+ : parent_(NULL),
+ runner_(derived_instance) {
+ assert(derived_instance);
+ Initialize();
+}
+
+// Does common initialization of member variables
+void TaskParent::Initialize() {
+ children_.reset(new ChildSet());
+ child_error_ = false;
+}
+
+void TaskParent::AddChild(Task *child) {
+ children_->insert(child);
+}
+
+#ifdef _DEBUG
+bool TaskParent::IsChildTask(Task *task) {
+ assert(task);
+ return task->parent_ == this && children_->find(task) != children_->end();
+}
+#endif
+
+bool TaskParent::AllChildrenDone() {
+ for (ChildSet::iterator it = children_->begin();
+ it != children_->end();
+ ++it) {
+ if (!(*it)->IsDone())
+ return false;
+ }
+ return true;
+}
+
+bool TaskParent::AnyChildError() {
+ return child_error_;
+}
+
+void TaskParent::AbortAllChildren() {
+ if (children_->size() > 0) {
+#ifdef _DEBUG
+ runner_->IncrementAbortCount();
+#endif
+
+ ChildSet copy = *children_;
+ for (ChildSet::iterator it = copy.begin(); it != copy.end(); ++it) {
+ (*it)->Abort(true); // Note we do not wake
+ }
+
+#ifdef _DEBUG
+ runner_->DecrementAbortCount();
+#endif
+ }
+}
+
+void TaskParent::OnStopped(Task *task) {
+ AbortAllChildren();
+ parent_->OnChildStopped(task);
+}
+
+void TaskParent::OnChildStopped(Task *child) {
+ if (child->HasError())
+ child_error_ = true;
+ children_->erase(child);
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/taskparent.h b/third_party/libjingle/source/talk/base/taskparent.h
new file mode 100644
index 0000000..a6c5795
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/taskparent.h
@@ -0,0 +1,89 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_TASKPARENT_H__
+#define TALK_BASE_TASKPARENT_H__
+
+#include <set>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/scoped_ptr.h"
+
+namespace talk_base {
+
+class Task;
+class TaskRunner;
+
+class TaskParent {
+ public:
+ TaskParent(Task *derived_instance, TaskParent *parent);
+ explicit TaskParent(TaskRunner *derived_instance);
+ virtual ~TaskParent() { }
+
+ TaskParent *GetParent() { return parent_; }
+ TaskRunner *GetRunner() { return runner_; }
+
+ // Retrieves a parent that corresponds to the given "code". The code
+ // should be defined in a unique manner for the given subtree. This
+ // method will crash (when the parent_ is NULL) if there is no corresponding
+ // parent.
+ //
+ // Example use:
+ // XmppClient* client =
+ // static_cast<XmppClient*>(parent->GetParent(XMPP_CLIENT_TASK_CODE));
+ virtual TaskParent *GetParent(int code) { return parent_->GetParent(code); }
+
+ bool AllChildrenDone();
+ bool AnyChildError();
+#ifdef _DEBUG
+ bool IsChildTask(Task *task);
+#endif
+
+ protected:
+ void OnStopped(Task *task);
+ void AbortAllChildren();
+ TaskParent *parent() {
+ return parent_;
+ }
+
+ private:
+ void Initialize();
+ void OnChildStopped(Task *child);
+ void AddChild(Task *child);
+
+ TaskParent *parent_;
+ TaskRunner *runner_;
+ bool child_error_;
+ typedef std::set<Task *> ChildSet;
+ scoped_ptr<ChildSet> children_;
+ DISALLOW_EVIL_CONSTRUCTORS(TaskParent);
+};
+
+
+} // namespace talk_base
+
+#endif // TALK_BASE_TASKPARENT_H__
diff --git a/third_party/libjingle/source/talk/base/taskrunner.cc b/third_party/libjingle/source/talk/base/taskrunner.cc
new file mode 100644
index 0000000..bf5ed7d
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/taskrunner.cc
@@ -0,0 +1,241 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/base/taskrunner.h"
+
+#include "talk/base/common.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/task.h"
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+TaskRunner::TaskRunner()
+ : TaskParent(this),
+ next_timeout_task_(NULL),
+ tasks_running_(false)
+#ifdef _DEBUG
+ , abort_count_(0),
+ deleting_task_(NULL)
+#endif
+{
+}
+
+TaskRunner::~TaskRunner() {
+ // this kills and deletes children silently!
+ AbortAllChildren();
+ InternalRunTasks(true);
+}
+
+void TaskRunner::StartTask(Task * task) {
+ tasks_.push_back(task);
+
+ // the task we just started could be about to timeout --
+ // make sure our "next timeout task" is correct
+ UpdateTaskTimeout(task, 0);
+
+ WakeTasks();
+}
+
+void TaskRunner::RunTasks() {
+ InternalRunTasks(false);
+}
+
+void TaskRunner::InternalRunTasks(bool in_destructor) {
+ // This shouldn't run while an abort is happening.
+ // If that occurs, then tasks may be deleted in this method,
+ // but pointers to them will still be in the
+ // "ChildSet copy" in TaskParent::AbortAllChildren.
+ // Subsequent use of those task may cause data corruption or crashes.
+ ASSERT(!abort_count_);
+ // Running continues until all tasks are Blocked (ok for a small # of tasks)
+ if (tasks_running_) {
+ return; // don't reenter
+ }
+
+ tasks_running_ = true;
+
+ int64 previous_timeout_time = next_task_timeout();
+
+ 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
+ bool need_timeout_recalc = false;
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ if (tasks_[i]->IsDone()) {
+ Task* task = tasks_[i];
+ if (next_timeout_task_ &&
+ task->unique_id() == next_timeout_task_->unique_id()) {
+ next_timeout_task_ = NULL;
+ need_timeout_recalc = true;
+ }
+
+#ifdef _DEBUG
+ deleting_task_ = task;
+#endif
+ delete task;
+#ifdef _DEBUG
+ deleting_task_ = NULL;
+#endif
+ tasks_[i] = NULL;
+ }
+ }
+ // Finally, remove nulls
+ std::vector<Task *>::iterator it;
+ it = std::remove(tasks_.begin(),
+ tasks_.end(),
+ reinterpret_cast<Task *>(NULL));
+
+ tasks_.erase(it, tasks_.end());
+
+ if (need_timeout_recalc)
+ RecalcNextTimeout(NULL);
+
+ // Make sure that adjustments are done to account
+ // for any timeout changes (but don't call this
+ // while being destroyed since it calls a pure virtual function).
+ if (!in_destructor)
+ CheckForTimeoutChange(previous_timeout_time);
+
+ tasks_running_ = false;
+}
+
+void TaskRunner::PollTasks() {
+ // see if our "next potentially timed-out task" has indeed timed out.
+ // If it has, wake it up, then queue up the next task in line
+ // Repeat while we have new timed-out tasks.
+ // TODO(juberti): We need to guard against WakeTasks not updating
+ // next_timeout_task_. Maybe also add documentation in the header file once
+ // we understand this code better.
+ Task* old_timeout_task = NULL;
+ while (next_timeout_task_ &&
+ old_timeout_task != next_timeout_task_ &&
+ next_timeout_task_->TimedOut()) {
+ old_timeout_task = next_timeout_task_;
+ next_timeout_task_->Wake();
+ WakeTasks();
+ }
+}
+
+int64 TaskRunner::next_task_timeout() const {
+ if (next_timeout_task_) {
+ return next_timeout_task_->timeout_time();
+ }
+ return 0;
+}
+
+// this function gets called frequently -- when each task changes
+// state to something other than DONE, ERROR or BLOCKED, it calls
+// ResetTimeout(), which will call this function to make sure that
+// the next timeout-able task hasn't changed. The logic in this function
+// prevents RecalcNextTimeout() from getting called in most cases,
+// effectively making the task scheduler O-1 instead of O-N
+
+void TaskRunner::UpdateTaskTimeout(Task* task,
+ int64 previous_task_timeout_time) {
+ ASSERT(task != NULL);
+ int64 previous_timeout_time = next_task_timeout();
+ bool task_is_timeout_task = next_timeout_task_ != NULL &&
+ task->unique_id() == next_timeout_task_->unique_id();
+ if (task_is_timeout_task) {
+ previous_timeout_time = previous_task_timeout_time;
+ }
+
+ // if the relevant task has a timeout, then
+ // check to see if it's closer than the current
+ // "about to timeout" task
+ if (task->timeout_time()) {
+ if (next_timeout_task_ == NULL ||
+ (task->timeout_time() <= next_timeout_task_->timeout_time())) {
+ next_timeout_task_ = task;
+ }
+ } else if (task_is_timeout_task) {
+ // otherwise, if the task doesn't have a timeout,
+ // and it used to be our "about to timeout" task,
+ // walk through all the tasks looking for the real
+ // "about to timeout" task
+ RecalcNextTimeout(task);
+ }
+
+ // Note when task_running_, then the running routine
+ // (TaskRunner::InternalRunTasks) is responsible for calling
+ // CheckForTimeoutChange.
+ if (!tasks_running_) {
+ CheckForTimeoutChange(previous_timeout_time);
+ }
+}
+
+void TaskRunner::RecalcNextTimeout(Task *exclude_task) {
+ // walk through all the tasks looking for the one
+ // which satisfies the following:
+ // it's not finished already
+ // we're not excluding it
+ // it has the closest timeout time
+
+ int64 next_timeout_time = 0;
+ next_timeout_task_ = NULL;
+
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ Task *task = tasks_[i];
+ // if the task isn't complete, and it actually has a timeout time
+ if (!task->IsDone() && (task->timeout_time() > 0))
+ // if it doesn't match our "exclude" task
+ if (exclude_task == NULL ||
+ exclude_task->unique_id() != task->unique_id())
+ // if its timeout time is sooner than our current timeout time
+ if (next_timeout_time == 0 ||
+ task->timeout_time() <= next_timeout_time) {
+ // set this task as our next-to-timeout
+ next_timeout_time = task->timeout_time();
+ next_timeout_task_ = task;
+ }
+ }
+}
+
+void TaskRunner::CheckForTimeoutChange(int64 previous_timeout_time) {
+ int64 next_timeout = next_task_timeout();
+ bool timeout_change = (previous_timeout_time == 0 && next_timeout != 0) ||
+ next_timeout < previous_timeout_time ||
+ (previous_timeout_time <= CurrentTime() &&
+ previous_timeout_time != next_timeout);
+ if (timeout_change) {
+ OnTimeoutChange();
+ }
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/taskrunner.h b/third_party/libjingle/source/talk/base/taskrunner.h
new file mode 100644
index 0000000..f34a609
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/taskrunner.h
@@ -0,0 +1,117 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_TASKRUNNER_H__
+#define TALK_BASE_TASKRUNNER_H__
+
+#include <vector>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/taskparent.h"
+
+namespace talk_base {
+class Task;
+
+const int64 kSecToMsec = 1000;
+const int64 kMsecTo100ns = 10000;
+const int64 kSecTo100ns = kSecToMsec * kMsecTo100ns;
+
+class TaskRunner : public TaskParent, public sigslot::has_slots<> {
+ public:
+ TaskRunner();
+ virtual ~TaskRunner();
+
+ virtual void WakeTasks() = 0;
+
+ // Returns the current time in 100ns units. It is used for
+ // determining timeouts. The origin is not important, only
+ // the units and that rollover while the computer is running.
+ //
+ // On Windows, GetSystemTimeAsFileTime is the typical implementation.
+ virtual int64 CurrentTime() = 0 ;
+
+ void StartTask(Task *task);
+ void RunTasks();
+ void PollTasks();
+
+ void UpdateTaskTimeout(Task *task, int64 previous_task_timeout_time);
+
+#ifdef _DEBUG
+ bool is_ok_to_delete(Task* task) {
+ return task == deleting_task_;
+ }
+
+ void IncrementAbortCount() {
+ ++abort_count_;
+ }
+
+ void DecrementAbortCount() {
+ --abort_count_;
+ }
+#endif
+
+ // Returns the next absolute time when a task times out
+ // OR "0" if there is no next timeout.
+ int64 next_task_timeout() const;
+
+ protected:
+ // The primary usage of this method is to know if
+ // a callback timer needs to be set-up or adjusted.
+ // This method will be called
+ // * when the next_task_timeout() becomes a smaller value OR
+ // * when next_task_timeout() has changed values and the previous
+ // value is in the past.
+ //
+ // If the next_task_timeout moves to the future, this method will *not*
+ // get called (because it subclass should check next_task_timeout()
+ // when its timer goes off up to see if it needs to set-up a new timer).
+ //
+ // Note that this maybe called conservatively. In that it may be
+ // called when no time change has happened.
+ virtual void OnTimeoutChange() {
+ // by default, do nothing.
+ }
+
+ private:
+ void InternalRunTasks(bool in_destructor);
+ void CheckForTimeoutChange(int64 previous_timeout_time);
+
+ std::vector<Task *> tasks_;
+ Task *next_timeout_task_;
+ bool tasks_running_;
+#ifdef _DEBUG
+ int abort_count_;
+ Task* deleting_task_;
+#endif
+
+ void RecalcNextTimeout(Task *exclude_task);
+};
+
+} // namespace talk_base
+
+#endif // TASK_BASE_TASKRUNNER_H__
diff --git a/third_party/libjingle/source/talk/base/thread.cc b/third_party/libjingle/source/talk/base/thread.cc
new file mode 100644
index 0000000..c4bebdc
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/thread.cc
@@ -0,0 +1,495 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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
+#include <time.h>
+#endif
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#include "talk/base/time.h"
+
+#if defined(OSX_USE_COCOA)
+#ifndef OSX
+#error OSX_USE_COCOA is defined but not OSX
+#endif
+#include "talk/base/maccocoathreadhelper.h"
+#include "talk/base/scoped_autorelease_pool.h"
+#endif
+
+#define MSDEV_SET_THREAD_NAME 0x406D1388
+
+namespace talk_base {
+
+ThreadManager g_thmgr;
+
+#ifdef POSIX
+pthread_key_t ThreadManager::key_;
+
+ThreadManager::ThreadManager() {
+ pthread_key_create(&key_, NULL);
+ main_thread_ = WrapCurrentThread();
+#if defined(OSX_USE_COCOA)
+ InitCocoaMultiThreading();
+#endif
+}
+
+ThreadManager::~ThreadManager() {
+#ifdef OSX_USE_COCOA
+ // This is called during exit, at which point apparently no NSAutoreleasePools
+ // are available; but we might still need them to do cleanup (or we get the
+ // "no autoreleasepool in place, just leaking" warning when exiting).
+ ScopedAutoreleasePool pool;
+#endif
+ UnwrapCurrentThread();
+ // Unwrap deletes main_thread_ automatically.
+ pthread_key_delete(key_);
+
+}
+
+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_ = WrapCurrentThread();
+}
+
+ThreadManager::~ThreadManager() {
+ UnwrapCurrentThread();
+ TlsFree(key_);
+}
+
+Thread *ThreadManager::CurrentThread() {
+ return (Thread *)TlsGetValue(key_);
+}
+
+void ThreadManager::SetCurrent(Thread *thread) {
+ TlsSetValue(key_, thread);
+}
+#endif
+
+// static
+Thread *ThreadManager::WrapCurrentThread() {
+ Thread* result = CurrentThread();
+ if (NULL == result) {
+ result = new Thread();
+#if defined(WIN32)
+ // We explicitly ask for no rights other than synchronization.
+ // This gives us the best chance of succeeding.
+ result->thread_ = OpenThread(SYNCHRONIZE, FALSE, GetCurrentThreadId());
+ if (!result->thread_)
+ LOG_GLE(LS_ERROR) << "Unable to get handle to thread.";
+#elif defined(POSIX)
+ result->thread_ = pthread_self();
+#endif
+ result->owned_ = false;
+ result->started_ = true;
+ SetCurrent(result);
+ }
+
+ return result;
+}
+
+// static
+void ThreadManager::UnwrapCurrentThread() {
+ Thread* t = CurrentThread();
+ if (t && !(t->IsOwned())) {
+ // Clears the platform-specific thread-specific storage.
+ SetCurrent(NULL);
+#ifdef WIN32
+ if (!CloseHandle(t->thread_)) {
+ LOG_GLE(LS_ERROR) << "When unwrapping thread, failed to close handle.";
+ }
+#endif
+ t->started_ = false;
+ delete t;
+ }
+}
+
+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());
+}
+
+void ThreadManager::StopAllThreads_() {
+ // TODO: In order to properly implement, Threads need to be ref-counted.
+ CritScope cs(&g_thmgr.crit_);
+ for (size_t i=0; i<g_thmgr.threads_.size(); ++i) {
+ g_thmgr.threads_[i]->Stop();
+ }
+}
+
+struct ThreadInit {
+ Thread* thread;
+ Runnable* runnable;
+};
+
+Thread::Thread(SocketServer* ss)
+ : MessageQueue(ss),
+ priority_(PRIORITY_NORMAL),
+ started_(false),
+ has_sends_(false),
+#if defined(WIN32)
+ thread_(NULL),
+#endif
+ owned_(true) {
+ g_thmgr.Add(this);
+}
+
+Thread::~Thread() {
+ Stop();
+ if (active_)
+ Clear(NULL);
+ g_thmgr.Remove(this);
+}
+
+bool Thread::SleepMs(int milliseconds) {
+#ifdef WIN32
+ ::Sleep(milliseconds);
+ return true;
+#else
+ // POSIX has both a usleep() and a nanosleep(), but the former is deprecated,
+ // so we use nanosleep() even though it has greater precision than necessary.
+ struct timespec ts;
+ ts.tv_sec = milliseconds / 1000;
+ ts.tv_nsec = (milliseconds % 1000) * 1000000;
+ int ret = nanosleep(&ts, NULL);
+ if (ret != 0) {
+ LOG_ERR(LS_WARNING) << "nanosleep() returning early";
+ return false;
+ }
+ return true;
+#endif
+}
+
+bool Thread::Start(Runnable* runnable) {
+ assert(owned_);
+ if (!owned_) return false;
+ assert(!started_);
+ if (started_) return false;
+
+ ThreadInit* init = new ThreadInit;
+ init->thread = this;
+ init->runnable = runnable;
+#if defined(WIN32)
+ DWORD flags = 0;
+ if (priority_ != PRIORITY_NORMAL) {
+ flags = CREATE_SUSPENDED;
+ }
+ thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreRun, init, flags,
+ NULL);
+ if (thread_) {
+ if (priority_ != PRIORITY_NORMAL) {
+ if (priority_ == PRIORITY_HIGH) {
+ ::SetThreadPriority(thread_, THREAD_PRIORITY_HIGHEST);
+ } else if (priority_ == PRIORITY_ABOVE_NORMAL) {
+ ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL);
+ } else if (priority_ == PRIORITY_IDLE) {
+ ::SetThreadPriority(thread_, THREAD_PRIORITY_IDLE);
+ }
+ ::ResumeThread(thread_);
+ }
+ } else {
+ return false;
+ }
+#elif defined(POSIX)
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ if (priority_ != PRIORITY_NORMAL) {
+ if (priority_ == PRIORITY_IDLE) {
+ // There is no POSIX-standard way to set a below-normal priority for an
+ // individual thread (only whole process), so let's not support it.
+ LOG(LS_WARNING) << "PRIORITY_IDLE not supported";
+ } else {
+ // Set real-time round-robin policy.
+ if (pthread_attr_setschedpolicy(&attr, SCHED_RR) != 0) {
+ LOG(LS_ERROR) << "pthread_attr_setschedpolicy";
+ }
+ struct sched_param param;
+ if (pthread_attr_getschedparam(&attr, &param) != 0) {
+ LOG(LS_ERROR) << "pthread_attr_getschedparam";
+ } else {
+ // The numbers here are arbitrary.
+ if (priority_ == PRIORITY_HIGH) {
+ param.sched_priority = 6; // 6 = HIGH
+ } else {
+ ASSERT(priority_ == PRIORITY_ABOVE_NORMAL);
+ param.sched_priority = 4; // 4 = ABOVE_NORMAL
+ }
+ if (pthread_attr_setschedparam(&attr, &param) != 0) {
+ LOG(LS_ERROR) << "pthread_attr_setschedparam";
+ }
+ }
+ }
+ }
+ int error_code = pthread_create(&thread_, &attr, PreRun, init);
+ if (0 != error_code) {
+ LOG(LS_ERROR) << "Unable to create pthread, error " << error_code;
+ return false;
+ }
+#endif
+ started_ = true;
+ return true;
+}
+
+void Thread::Join() {
+ if (started_) {
+ ASSERT(!IsCurrent());
+#if defined(WIN32)
+ WaitForSingleObject(thread_, INFINITE);
+ CloseHandle(thread_);
+ thread_ = NULL;
+#elif defined(POSIX)
+ void *pv;
+ pthread_join(thread_, &pv);
+#endif
+ started_ = false;
+ }
+}
+
+#ifdef WIN32
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType;
+ LPCSTR szName;
+ DWORD dwThreadID;
+ DWORD dwFlags;
+} THREADNAME_INFO;
+
+void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
+{
+ THREADNAME_INFO info;
+ {
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+ }
+ __try
+ {
+ RaiseException(MSDEV_SET_THREAD_NAME, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+}
+#endif // WIN32
+
+void* Thread::PreRun(void* pv) {
+ ThreadInit* init = static_cast<ThreadInit*>(pv);
+ ThreadManager::SetCurrent(init->thread);
+#if defined(WIN32) && defined(_DEBUG)
+ char buf[256];
+ _snprintf(buf, sizeof(buf), "Thread 0x%.8x", init->thread);
+ SetThreadName(GetCurrentThreadId(), buf);
+#endif
+#ifdef OSX_USE_COCOA
+ // Make sure the new thread has an autoreleasepool
+ ScopedAutoreleasePool pool;
+#endif
+ if (init->runnable) {
+ init->runnable->Run(init->thread);
+ } else {
+ init->thread->Run();
+ }
+ delete init;
+ return NULL;
+}
+
+void Thread::Run() {
+ ProcessMessages(kForever);
+}
+
+bool Thread::IsOwned() {
+ return owned_;
+}
+
+void Thread::Stop() {
+ MessageQueue::Quit();
+ Join();
+}
+
+void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
+ if (fStop_)
+ return;
+
+ // 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
+
+ bool ready = false;
+ {
+ CritScope cs(&crit_);
+ EnsureActive();
+ _SendMessage smsg;
+ smsg.thread = current_thread;
+ smsg.msg = msg;
+ smsg.ready = &ready;
+ sendlist_.push_back(smsg);
+ has_sends_ = true;
+ }
+
+ // Wait for a reply
+
+ ss_->WakeUp();
+
+ bool waited = false;
+ while (!ready) {
+ current_thread->ReceiveSends();
+ current_thread->socketserver()->Wait(kForever, false);
+ waited = true;
+ }
+
+ // Our Wait loop above may have consumed some WakeUp events for this
+ // MessageQueue, that weren't relevant to this Send. Losing these WakeUps can
+ // cause problems for some SocketServers.
+ //
+ // Concrete example:
+ // Win32SocketServer on thread A calls Send on thread B. While processing the
+ // message, thread B Posts a message to A. We consume the wakeup for that
+ // Post while waiting for the Send to complete, which means that when we exit
+ // this loop, we need to issue another WakeUp, or else the Posted message
+ // won't be processed in a timely manner.
+
+ if (waited) {
+ current_thread->socketserver()->WakeUp();
+ }
+}
+
+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,
+ MessageList* removed) {
+ 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 (smsg.msg.Match(phandler, id)) {
+ if (removed) {
+ removed->push_back(smsg.msg);
+ } else {
+ delete smsg.msg.pdata;
+ }
+ iter = sendlist_.erase(iter);
+ *smsg.ready = true;
+ smsg.thread->socketserver()->WakeUp();
+ continue;
+ }
+ ++iter;
+ }
+
+ MessageQueue::Clear(phandler, id, removed);
+}
+
+bool Thread::ProcessMessages(int cmsLoop) {
+ uint32 msEnd = (kForever == cmsLoop) ? 0 : TimeAfter(cmsLoop);
+ int cmsNext = cmsLoop;
+
+ while (true) {
+ Message msg;
+ if (!Get(&msg, cmsNext))
+ return !IsQuitting();
+ Dispatch(&msg);
+
+ if (cmsLoop != kForever) {
+ cmsNext = TimeUntil(msEnd);
+ if (cmsNext < 0)
+ return true;
+ }
+ }
+}
+
+AutoThread::AutoThread(SocketServer* ss) : Thread(ss) {
+ if (!ThreadManager::CurrentThread()) {
+ ThreadManager::SetCurrent(this);
+ }
+}
+
+AutoThread::~AutoThread() {
+ if (ThreadManager::CurrentThread() == this) {
+ ThreadManager::SetCurrent(NULL);
+ }
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/thread.h b/third_party/libjingle/source/talk/base/thread.h
new file mode 100644
index 0000000..ff6f15b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/thread.h
@@ -0,0 +1,229 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_THREAD_H_
+#define TALK_BASE_THREAD_H_
+
+#include <algorithm>
+#include <list>
+#include <vector>
+
+#ifdef POSIX
+#include <pthread.h>
+#endif
+
+#include "talk/base/messagequeue.h"
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+namespace talk_base {
+
+class Thread;
+
+class ThreadManager {
+public:
+ ThreadManager();
+ ~ThreadManager();
+
+ static Thread *CurrentThread();
+ static void SetCurrent(Thread *thread);
+ void Add(Thread *thread);
+ void Remove(Thread *thread);
+
+ // Returns a thread object with its thread_ ivar set
+ // to whatever the OS uses to represent the thread.
+ // If there already *is* a Thread object corresponding to this thread,
+ // this method will return that. Otherwise it creates a new Thread
+ // object whose wrapped() method will return true, and whose
+ // handle will, on Win32, be opened with only synchronization privileges -
+ // if you need more privilegs, rather than changing this method, please
+ // write additional code to adjust the privileges, or call a different
+ // factory method of your own devising, because this one gets used in
+ // unexpected contexts (like inside browser plugins) and it would be a
+ // shame to break it. It is also conceivable on Win32 that we won't even
+ // be able to get synchronization privileges, in which case the result
+ // will have a NULL handle.
+ static Thread *WrapCurrentThread();
+ static void UnwrapCurrentThread();
+
+ static void StopAllThreads_(); // Experimental
+
+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;
+};
+
+enum ThreadPriority {
+ PRIORITY_HIGH,
+ PRIORITY_ABOVE_NORMAL,
+ PRIORITY_NORMAL,
+ PRIORITY_IDLE,
+};
+
+class Runnable {
+ public:
+ virtual ~Runnable() {}
+ virtual void Run(Thread* thread) = 0;
+};
+
+class Thread : public MessageQueue {
+public:
+ Thread(SocketServer* ss = NULL);
+ virtual ~Thread();
+
+ static inline Thread* Current() {
+ return ThreadManager::CurrentThread();
+ }
+
+ // Sleeps the calling thread for the specified number of milliseconds, during
+ // which time no processing is performed. Returns false if sleeping was
+ // interrupted by a signal (POSIX only).
+ static bool SleepMs(int millis);
+
+ inline bool IsCurrent() const {
+ return (ThreadManager::CurrentThread() == this);
+ }
+
+ void SetPriority(ThreadPriority priority) {
+ priority_ = priority;
+ }
+
+ bool started() const { return started_; }
+
+ bool Start(Runnable* runnable = NULL);
+
+ // Never call Stop on the current thread. Instead use the inherited Quit
+ // function which will exit the base MessageQueue without terminating the
+ // underlying OS thread.
+ virtual void Stop();
+
+ // By default, Thread::Run() calls ProcessMessages(kForever). To do other
+ // work, override Run(). To receive and dispatch messages, call
+ // ProcessMessages occasionally.
+ virtual void Run();
+
+ virtual void Send(MessageHandler *phandler, uint32 id = 0,
+ MessageData *pdata = NULL);
+
+ // From MessageQueue
+ virtual void Clear(MessageHandler *phandler, uint32 id = MQID_ANY,
+ MessageList* removed = NULL);
+ virtual void ReceiveSends();
+
+ // ProcessMessages will process I/O and dispatch messages until:
+ // 1) cms milliseconds have elapsed (returns true)
+ // 2) Stop() is called (returns false)
+ bool ProcessMessages(int cms);
+
+ // Returns true if this is a thread that we created using the standard
+ // constructor, false if it was created by a call to
+ // ThreadManager::WrapCurrentThread(). The main thread of an application
+ // is generally not owned, since the OS representation of the thread
+ // obviously exists before we can get to it.
+ // You cannot call Start on non-owned threads.
+ bool IsOwned();
+
+#ifdef WIN32
+ HANDLE GetHandle() {
+ return thread_;
+ }
+#elif POSIX
+ pthread_t GetPThread() {
+ return thread_;
+ }
+#endif
+
+private:
+ static void *PreRun(void *pv);
+ // Blocks the calling thread until this thread has terminated.
+ void Join();
+
+ std::list<_SendMessage> sendlist_;
+ ThreadPriority priority_;
+ bool started_;
+ bool has_sends_;
+
+#ifdef POSIX
+ pthread_t thread_;
+#endif
+
+#ifdef WIN32
+ HANDLE thread_;
+#endif
+
+ bool owned_;
+
+ 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();
+};
+
+// Provides an easy way to install/uninstall a socketserver on a thread.
+class SocketServerScope {
+ public:
+ explicit SocketServerScope(SocketServer* ss) {
+ old_ss_ = Thread::Current()->socketserver();
+ Thread::Current()->set_socketserver(ss);
+ }
+ ~SocketServerScope() {
+ Thread::Current()->set_socketserver(old_ss_);
+ }
+ private:
+ SocketServer* old_ss_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_THREAD_H_
diff --git a/third_party/libjingle/source/talk/base/time.cc b/third_party/libjingle/source/talk/base/time.cc
new file mode 100644
index 0000000..f5fa6db
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/time.cc
@@ -0,0 +1,125 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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
+#include <sys/time.h>
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include "talk/base/common.h"
+#include "talk/base/time.h"
+
+#define EFFICIENT_IMPLEMENTATION 1
+
+namespace talk_base {
+
+const uint32 LAST = 0xFFFFFFFF;
+const uint32 HALF = 0x80000000;
+
+#ifdef POSIX
+uint32 Time() {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+#endif
+
+#ifdef WIN32
+uint32 Time() {
+ return GetTickCount();
+}
+#endif
+
+uint32 StartTime() {
+ // Close to program execution time
+ static const uint32 g_start = Time();
+ return g_start;
+}
+
+// Make sure someone calls it so that it gets initialized
+static uint32 ignore = StartTime();
+
+uint32 TimeAfter(int32 elapsed) {
+ ASSERT(elapsed >= 0);
+ ASSERT(static_cast<uint32>(elapsed) < HALF);
+ return Time() + elapsed;
+}
+
+bool TimeIsBetween(uint32 earlier, uint32 middle, uint32 later) {
+ if (earlier <= later) {
+ return ((earlier <= middle) && (middle <= later));
+ } else {
+ return !((later < middle) && (middle < earlier));
+ }
+}
+
+bool TimeIsLaterOrEqual(uint32 earlier, uint32 later) {
+#if EFFICIENT_IMPLEMENTATION
+ int32 diff = later - earlier;
+ return (diff >= 0 && static_cast<uint32>(diff) < HALF);
+#else
+ const bool later_or_equal = TimeIsBetween(earlier, later, earlier + HALF);
+ return later_or_equal;
+#endif
+}
+
+bool TimeIsLater(uint32 earlier, uint32 later) {
+#if EFFICIENT_IMPLEMENTATION
+ int32 diff = later - earlier;
+ return (diff > 0 && static_cast<uint32>(diff) < HALF);
+#else
+ const bool earlier_or_equal = TimeIsBetween(later, earlier, later + HALF);
+ return !earlier_or_equal;
+#endif
+}
+
+int32 TimeDiff(uint32 later, uint32 earlier) {
+#if EFFICIENT_IMPLEMENTATION
+ return later - earlier;
+#else
+ const bool later_or_equal = TimeIsBetween(earlier, later, earlier + HALF);
+ if (later_or_equal) {
+ 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);
+ }
+ }
+#endif
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/time.h b/third_party/libjingle/source/talk/base/time.h
new file mode 100644
index 0000000..eca0e4e
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/time.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 TALK_BASE_TIME_H__
+#define TALK_BASE_TIME_H__
+
+#ifndef WIN32
+#include_next <time.h>
+#endif
+
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+typedef uint32 TimeStamp;
+
+// Returns the current time in milliseconds.
+uint32 Time();
+
+// Approximate time when the program started.
+uint32 StartTime();
+
+// Returns a future timestamp, 'elapsed' milliseconds from now.
+uint32 TimeAfter(int32 elapsed);
+
+// Comparisons between time values, which can wrap around.
+bool TimeIsBetween(uint32 earlier, uint32 middle, uint32 later); // Inclusive
+bool TimeIsLaterOrEqual(uint32 earlier, uint32 later); // Inclusive
+bool TimeIsLater(uint32 earlier, uint32 later); // Exclusive
+
+// Returns the later of two timestamps.
+inline uint32 TimeMax(uint32 ts1, uint32 ts2) {
+ return TimeIsLaterOrEqual(ts1, ts2) ? ts2 : ts1;
+}
+
+// Returns the earlier of two timestamps.
+inline uint32 TimeMin(uint32 ts1, uint32 ts2) {
+ return TimeIsLaterOrEqual(ts1, ts2) ? ts1 : ts2;
+}
+
+// Number of milliseconds that would elapse between 'earlier' and 'later'
+// timestamps. The value is negative if 'later' occurs before 'earlier'.
+int32 TimeDiff(uint32 later, uint32 earlier);
+
+// The number of milliseconds that have elapsed since 'earlier'.
+inline int32 TimeSince(uint32 earlier) {
+ return TimeDiff(Time(), earlier);
+}
+
+// The number of milliseconds that will elapse between now and 'later'.
+inline int32 TimeUntil(uint32 later) {
+ return TimeDiff(later, Time());
+}
+
+} // namespace talk_base
+
+#endif // TALK_BASE_TIME_H__
diff --git a/third_party/libjingle/source/talk/base/unixfilesystem.cc b/third_party/libjingle/source/talk/base/unixfilesystem.cc
new file mode 100644
index 0000000..dc29a35
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/unixfilesystem.cc
@@ -0,0 +1,517 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/unixfilesystem.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef OSX
+#include <Carbon/Carbon.h>
+#include <IOKit/IOCFBundle.h>
+#include <sys/statvfs.h>
+#include "talk/base/macutils.h"
+#endif // OSX
+
+#if defined(POSIX) && !defined(OSX)
+#include <sys/types.h>
+#ifdef ANDROID
+#include <sys/statfs.h>
+#else
+#include <sys/statvfs.h>
+#endif // ANDROID
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#endif // POSIX && !OSX
+
+#ifdef LINUX
+#include <ctype.h>
+#include <algorithm>
+#endif
+
+#include "talk/base/fileutils.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringutils.h"
+
+#ifdef ANDROID
+namespace {
+// Android does not have a concept of a single temp dir shared by all
+// because resource are scarse on a phone. Instead each app gets some
+// space on the sdcard under a path that is given at runtime by the
+// system.
+// The disk allocation feature is still work in progress so currently
+// we return a hardcoded a path on the sdcard. In the future, we
+// should do a JNI call to get that info from the context.
+// TODO: Replace hardcoded path with a query to the Context
+// object to get the equivalents of '/tmp' and '~/.'
+
+// @return the folder for libjingle. Some extra path (typically
+// Google/<app name>) will be added.
+const char* GetAndroidAppDataFolder() {
+ return "/sdcard";
+}
+
+// @return the tmp folder to be used. Some extra path will be added to
+// that base folder.
+const char* GetAndroidTempFolder() {
+ return "/sdcard";
+}
+
+} // anonymous namespace
+#endif
+
+namespace talk_base {
+
+std::string UnixFilesystem::app_temp_path_;
+
+bool UnixFilesystem::CreateFolder(const Pathname &path) {
+ std::string pathname(path.pathname());
+ int len = pathname.length();
+ if ((len == 0) || (pathname[len - 1] != '/'))
+ return false;
+
+ struct stat st;
+ int res = ::stat(pathname.c_str(), &st);
+ if (res == 0) {
+ // Something exists at this location, check if it is a directory
+ return S_ISDIR(st.st_mode) != 0;
+ } else if (errno != ENOENT) {
+ // Unexpected error
+ return false;
+ }
+
+ // Directory doesn't exist, look up one directory level
+ do {
+ --len;
+ } while ((len > 0) && (pathname[len - 1] != '/'));
+
+ if (!CreateFolder(Pathname(pathname.substr(0, len)))) {
+ return false;
+ }
+
+ LOG(LS_INFO) << "Creating folder: " << pathname;
+ return (0 == ::mkdir(pathname.c_str(), 0755));
+}
+
+FileStream *UnixFilesystem::OpenFile(const Pathname &filename,
+ const std::string &mode) {
+ FileStream *fs = new FileStream();
+ if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str())) {
+ delete fs;
+ fs = NULL;
+ }
+ return fs;
+}
+
+bool UnixFilesystem::DeleteFile(const Pathname &filename) {
+ LOG(LS_INFO) << "Deleting file:" << filename.pathname();
+
+ if (!IsFile(filename)) {
+ ASSERT(IsFile(filename));
+ return false;
+ }
+ return ::unlink(filename.pathname().c_str()) == 0;
+}
+
+bool UnixFilesystem::DeleteEmptyFolder(const Pathname &folder) {
+ LOG(LS_INFO) << "Deleting folder" << folder.pathname();
+
+ if (!IsFolder(folder)) {
+ ASSERT(IsFolder(folder));
+ return false;
+ }
+ std::string no_slash(folder.pathname(), 0, folder.pathname().length()-1);
+ return ::rmdir(no_slash.c_str()) == 0;
+}
+
+bool UnixFilesystem::GetTemporaryFolder(Pathname &pathname, bool create,
+ const std::string *append) {
+#ifdef OSX
+ FSRef fr;
+ if (0 != FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType,
+ kCreateFolder, &fr))
+ return false;
+ unsigned char buffer[NAME_MAX+1];
+ if (0 != FSRefMakePath(&fr, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ pathname.SetPathname(reinterpret_cast<char*>(buffer), "");
+#elif defined(ANDROID)
+ pathname.SetPathname(GetAndroidTempFolder(), "");
+#else // !OSX && !ANDROID
+ if (const char* tmpdir = getenv("TMPDIR")) {
+ pathname.SetPathname(tmpdir, "");
+ } else if (const char* tmp = getenv("TMP")) {
+ pathname.SetPathname(tmp, "");
+ } else {
+#ifdef P_tmpdir
+ pathname.SetPathname(P_tmpdir, "");
+#else // !P_tmpdir
+ pathname.SetPathname("/tmp/", "");
+#endif // !P_tmpdir
+ }
+#endif // !OSX && !ANDROID
+ if (append) {
+ ASSERT(!append->empty());
+ pathname.AppendFolder(*append);
+ }
+ return !create || CreateFolder(pathname);
+}
+
+std::string UnixFilesystem::TempFilename(const Pathname &dir,
+ const std::string &prefix) {
+ int len = dir.pathname().size() + prefix.size() + 2 + 6;
+ char *tempname = new char[len];
+
+ snprintf(tempname, len, "%s/%sXXXXXX", dir.pathname().c_str(),
+ prefix.c_str());
+ int fd = ::mkstemp(tempname);
+ if (fd != -1)
+ ::close(fd);
+ std::string ret(tempname);
+ delete[] tempname;
+
+ return ret;
+}
+
+bool UnixFilesystem::MoveFile(const Pathname &old_path,
+ const Pathname &new_path) {
+ if (!IsFile(old_path)) {
+ ASSERT(IsFile(old_path));
+ return false;
+ }
+ LOG(LS_VERBOSE) << "Moving " << old_path.pathname()
+ << " to " << new_path.pathname();
+ if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) {
+ if (errno != EXDEV)
+ return false;
+ if (!CopyFile(old_path, new_path))
+ return false;
+ if (!DeleteFile(old_path))
+ return false;
+ }
+ return true;
+}
+
+bool UnixFilesystem::MoveFolder(const Pathname &old_path,
+ const Pathname &new_path) {
+ if (!IsFolder(old_path)) {
+ ASSERT(IsFolder(old_path));
+ return false;
+ }
+ LOG(LS_VERBOSE) << "Moving " << old_path.pathname()
+ << " to " << new_path.pathname();
+ if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) {
+ if (errno != EXDEV)
+ return false;
+ if (!CopyFolder(old_path, new_path))
+ return false;
+ if (!DeleteFolderAndContents(old_path))
+ return false;
+ }
+ return true;
+}
+
+bool UnixFilesystem::IsFolder(const Pathname &path) {
+ struct stat st;
+ if (stat(path.pathname().c_str(), &st) < 0)
+ return false;
+ return S_ISDIR(st.st_mode);
+}
+
+bool UnixFilesystem::CopyFile(const Pathname &old_path,
+ const Pathname &new_path) {
+ LOG(LS_VERBOSE) << "Copying " << old_path.pathname()
+ << " to " << new_path.pathname();
+ char buf[256];
+ size_t len;
+
+ StreamInterface *source = OpenFile(old_path, "rb");
+ if (!source)
+ return false;
+
+ StreamInterface *dest = OpenFile(new_path, "wb");
+ if (!dest) {
+ delete source;
+ return false;
+ }
+
+ while (source->Read(buf, sizeof(buf), &len, NULL) == SR_SUCCESS)
+ dest->Write(buf, len, NULL, NULL);
+
+ delete source;
+ delete dest;
+ return true;
+}
+
+bool UnixFilesystem::IsTemporaryPath(const Pathname& pathname) {
+ const char* const kTempPrefixes[] = {
+#ifdef ANDROID
+ GetAndroidTempFolder()
+#else
+ "/tmp/", "/var/tmp/",
+#ifdef OSX
+ "/private/tmp/", "/private/var/tmp/", "/private/var/folders/",
+#endif // OSX
+#endif // ANDROID
+ };
+ for (size_t i = 0; i < ARRAY_SIZE(kTempPrefixes); ++i) {
+ if (0 == strncmp(pathname.pathname().c_str(), kTempPrefixes[i],
+ strlen(kTempPrefixes[i])))
+ return true;
+ }
+ return false;
+}
+
+bool UnixFilesystem::IsFile(const Pathname& pathname) {
+ struct stat st;
+ int res = ::stat(pathname.pathname().c_str(), &st);
+ // Treat symlinks, named pipes, etc. all as files.
+ return res == 0 && !S_ISDIR(st.st_mode);
+}
+
+bool UnixFilesystem::IsAbsent(const Pathname& pathname) {
+ struct stat st;
+ int res = ::stat(pathname.pathname().c_str(), &st);
+ // Note: we specifically maintain ENOTDIR as an error, because that implies
+ // that you could not call CreateFolder(pathname).
+ return res != 0 && ENOENT == errno;
+}
+
+bool UnixFilesystem::GetFileSize(const Pathname& pathname, size_t *size) {
+ struct stat st;
+ if (::stat(pathname.pathname().c_str(), &st) != 0)
+ return false;
+ *size = st.st_size;
+ return true;
+}
+
+bool UnixFilesystem::GetFileTime(const Pathname& path, FileTimeType which,
+ time_t* time) {
+ struct stat st;
+ if (::stat(path.pathname().c_str(), &st) != 0)
+ return false;
+ switch (which) {
+ case FTT_CREATED:
+ *time = st.st_ctime;
+ break;
+ case FTT_MODIFIED:
+ *time = st.st_mtime;
+ break;
+ case FTT_ACCESSED:
+ *time = st.st_atime;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool UnixFilesystem::GetAppPathname(Pathname* path) {
+#ifdef OSX
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ CFDictionaryRef procinfo = ProcessInformationCopyDictionary(&psn,
+ kProcessDictionaryIncludeAllInformationMask);
+ if (NULL == procinfo)
+ return false;
+ CFStringRef cfpath = (CFStringRef) CFDictionaryGetValue(procinfo,
+ kIOBundleExecutableKey);
+ std::string path8;
+ bool success = ToUtf8(cfpath, &path8);
+ CFRelease(procinfo);
+ if (success)
+ path->SetPathname(path8);
+ return success;
+#else // OSX
+ char buffer[NAME_MAX+1];
+ size_t len = readlink("/proc/self/exe", buffer, ARRAY_SIZE(buffer) - 1);
+ if (len <= 0)
+ return false;
+ buffer[len] = '\0';
+ path->SetPathname(buffer);
+ return true;
+#endif // OSX
+}
+
+bool UnixFilesystem::GetAppDataFolder(Pathname* path, bool per_user) {
+ ASSERT(!organization_name_.empty());
+ ASSERT(!application_name_.empty());
+
+ // First get the base directory for app data.
+#ifdef OSX
+ if (per_user) {
+ // Use ~/Library/Application Support/<orgname>/<appname>/
+ FSRef fr;
+ if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType,
+ kCreateFolder, &fr))
+ return false;
+ unsigned char buffer[NAME_MAX+1];
+ if (0 != FSRefMakePath(&fr, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ path->SetPathname(reinterpret_cast<char*>(buffer), "");
+ } else {
+ // TODO
+ return false;
+ }
+#elif defined(ANDROID) // && !OSX
+ // TODO: Check if the new disk allocation mechanism works
+ // per-user and we don't have the per_user distinction.
+ path->SetPathname(GetAndroidAppDataFolder(), "");
+#elif defined(LINUX) // && !OSX && !defined(ANDROID)
+ if (per_user) {
+ // We follow the recommendations in
+ // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ // It specifies separate directories for data and config files, but
+ // GetAppDataFolder() does not distinguish. We just return the config dir
+ // path.
+ const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
+ if (xdg_config_home) {
+ path->SetPathname(xdg_config_home, "");
+ } else {
+ // XDG says to default to $HOME/.config. We also support falling back to
+ // other synonyms for HOME if for some reason it is not defined.
+ const char* homedir;
+ if (const char* home = getenv("HOME")) {
+ homedir = home;
+ } else if (const char* dotdir = getenv("DOTDIR")) {
+ homedir = dotdir;
+ } else if (passwd* pw = getpwuid(geteuid())) {
+ homedir = pw->pw_dir;
+ } else {
+ return false;
+ }
+ path->SetPathname(homedir, "");
+ path->AppendFolder(".config");
+ }
+ } else {
+ // XDG does not define a standard directory for writable global data. Let's
+ // just use this.
+ path->SetPathname("/var/cache/", "");
+ }
+#endif // !OSX && !defined(ANDROID) && !defined(LINUX)
+
+ // Now add on a sub-path for our app.
+#if defined(OSX) || defined(ANDROID)
+ path->AppendFolder(organization_name_);
+ path->AppendFolder(application_name_);
+#elif defined(LINUX)
+ // XDG says to use a single directory level, so we concatenate the org and app
+ // name with a hyphen. We also do the Linuxy thing and convert to all
+ // lowercase with no spaces.
+ std::string subdir(organization_name_);
+ subdir.append("-");
+ subdir.append(application_name_);
+ replace_substrs(" ", 1, "", 0, &subdir);
+ std::transform(subdir.begin(), subdir.end(), subdir.begin(), ::tolower);
+ path->AppendFolder(subdir);
+#endif
+ return CreateFolder(*path);
+}
+
+bool UnixFilesystem::GetAppTempFolder(Pathname* path) {
+ ASSERT(!application_name_.empty());
+ // TODO: Consider whether we are worried about thread safety.
+ if (!app_temp_path_.empty()) {
+ path->SetPathname(app_temp_path_);
+ return true;
+ }
+
+ // Create a random directory as /tmp/<appname>-<pid>-<timestamp>
+ char buffer[128];
+ sprintfn(buffer, ARRAY_SIZE(buffer), "-%d-%d",
+ static_cast<int>(getpid()),
+ static_cast<int>(time(0)));
+ std::string folder(application_name_);
+ folder.append(buffer);
+ if (!GetTemporaryFolder(*path, true, &folder))
+ return false;
+
+ app_temp_path_ = path->pathname();
+ // TODO: atexit(DeleteFolderAndContents(app_temp_path_));
+ return true;
+}
+
+bool UnixFilesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) {
+ ASSERT(NULL != freebytes);
+ // TODO: Consider making relative paths absolute using cwd.
+ // TODO: When popping off a symlink, push back on the components of the
+ // symlink, so we don't jump out of the target disk inadvertently.
+ Pathname existing_path(path.folder(), "");
+ while (!existing_path.folder().empty() && IsAbsent(existing_path)) {
+ existing_path.SetFolder(existing_path.parent_folder());
+ }
+#ifdef ANDROID
+ struct statfs fs;
+ memset(&fs, 0, sizeof(fs));
+ if (0 != statfs(existing_path.pathname().c_str(), &fs))
+ return false;
+#else
+ struct statvfs vfs;
+ memset(&vfs, 0, sizeof(vfs));
+ if (0 != statvfs(existing_path.pathname().c_str(), &vfs))
+ return false;
+#endif // ANDROID
+#ifdef LINUX
+ *freebytes = static_cast<int64>(vfs.f_bsize) * vfs.f_bavail;
+#elif defined(OSX)
+ *freebytes = static_cast<int64>(vfs.f_frsize) * vfs.f_bavail;
+#elif defined(ANDROID)
+ *freebytes = static_cast<int64>(fs.f_bsize) * fs.f_bavail;
+#endif
+
+ return true;
+}
+
+Pathname UnixFilesystem::GetCurrentDirectory() {
+ Pathname cwd;
+#if defined(LINUX) || defined(OSX)
+ // Both Linux and Mac supported malloc()'ing the string themselves, although
+ // that is not required by POSIX.
+ char *path = getcwd(NULL, 0);
+#elif defined(ANDROID)
+ // Android requires the buffer to be allocated before getcwd is called.
+ char buffer[PATH_MAX];
+ char *path = getcwd(buffer, PATH_MAX);
+#else
+#error GetCurrentDirectory() not implemented on this platform
+#endif
+
+ if (!path) {
+ LOG_ERR(LS_ERROR) << "getcwd() failed";
+ return cwd; // returns empty pathname
+ }
+ cwd.SetFolder(std::string(path));
+
+#if defined(LINUX) || defined(OSX)
+ free(path);
+#endif
+ return cwd;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/unixfilesystem.h b/third_party/libjingle/source/talk/base/unixfilesystem.h
new file mode 100644
index 0000000..1b686be
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/unixfilesystem.h
@@ -0,0 +1,109 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_UNIXFILESYSTEM_H__
+#define _TALK_BASE_UNIXFILESYSTEM_H__
+
+#include "fileutils.h"
+
+namespace talk_base {
+
+class UnixFilesystem : public FilesystemInterface {
+ public:
+
+ // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise,
+ // returns NULL.
+ virtual FileStream *OpenFile(const Pathname &filename,
+ const std::string &mode);
+
+ // This will attempt to delete the file located at filename.
+ // It will fail with VERIY if you pass it a non-existant file, or a directory.
+ virtual bool DeleteFile(const Pathname &filename);
+
+ // This will attempt to delete the folder located at 'folder'
+ // It ASSERTs and returns false if you pass it a non-existant folder or a plain file.
+ virtual bool DeleteEmptyFolder(const Pathname &folder);
+
+ // Creates a directory. This will call itself recursively to create /foo/bar even if
+ // /foo does not exist.
+ // Returns TRUE if function succeeds
+ virtual bool CreateFolder(const Pathname &pathname);
+
+ // This moves a file from old_path to new_path, where "file" can be a plain file
+ // or directory, which will be moved recursively.
+ // Returns true if function succeeds.
+ virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path);
+ virtual bool MoveFolder(const Pathname &old_path, const Pathname &new_path);
+
+ // This copies a file from old_path to _new_path where "file" can be a plain file
+ // or directory, which will be copied recursively.
+ // Returns true if function succeeds
+ virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path);
+
+ // Returns true if a pathname is a directory
+ virtual bool IsFolder(const Pathname& pathname);
+
+ // Returns true if pathname represents a temporary location on the system.
+ virtual bool IsTemporaryPath(const Pathname& pathname);
+
+ // Returns true of pathname represents an existing file
+ virtual bool IsFile(const Pathname& pathname);
+
+ // Returns true if pathname refers to no filesystem object, every parent
+ // directory either exists, or is also absent.
+ virtual bool IsAbsent(const Pathname& pathname);
+
+ virtual std::string TempFilename(const Pathname &dir, const std::string &prefix);
+
+ // A folder appropriate for storing temporary files (Contents are
+ // automatically deleted when the program exists)
+ virtual bool GetTemporaryFolder(Pathname &path, bool create,
+ const std::string *append);
+
+ virtual bool GetFileSize(const Pathname& path, size_t* size);
+ virtual bool GetFileTime(const Pathname& path, FileTimeType which,
+ time_t* time);
+
+ // Returns the path to the running application.
+ virtual bool GetAppPathname(Pathname* path);
+
+ virtual bool GetAppDataFolder(Pathname* path, bool per_user);
+
+ // Get a temporary folder that is unique to the current user and application.
+ virtual bool GetAppTempFolder(Pathname* path);
+
+ virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes);
+
+ // Returns the absolute path of the current directory.
+ virtual Pathname GetCurrentDirectory();
+ private:
+ static std::string app_temp_path_;
+};
+
+} // namespace talk_base
+
+#endif // _UNIXFILESYSTEM_H__
diff --git a/third_party/libjingle/source/talk/base/urlencode.cc b/third_party/libjingle/source/talk/base/urlencode.cc
new file mode 100644
index 0000000..6dd51e1
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/urlencode.cc
@@ -0,0 +1,196 @@
+/*
+ * libjingle
+ * Copyright 2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/urlencode.h"
+
+#include "talk/base/common.h"
+#include "talk/base/stringutils.h"
+
+static int HexPairValue(const char * code) {
+ int value = 0;
+ const char * pch = code;
+ for (;;) {
+ int digit = *pch++;
+ if (digit >= '0' && digit <= '9') {
+ value += digit - '0';
+ }
+ else if (digit >= 'A' && digit <= 'F') {
+ value += digit - 'A' + 10;
+ }
+ else if (digit >= 'a' && digit <= 'f') {
+ value += digit - 'a' + 10;
+ }
+ else {
+ return -1;
+ }
+ if (pch == code + 2)
+ return value;
+ value <<= 4;
+ }
+}
+
+int InternalUrlDecode(const char *source, char *dest,
+ bool encode_space_as_plus) {
+ char * start = dest;
+
+ while (*source) {
+ switch (*source) {
+ case '+':
+ if (encode_space_as_plus) {
+ *(dest++) = ' ';
+ } else {
+ *dest++ = *source;
+ }
+ break;
+ case '%':
+ if (source[1] && source[2]) {
+ int value = HexPairValue(source + 1);
+ if (value >= 0) {
+ *(dest++) = value;
+ source += 2;
+ }
+ else {
+ *dest++ = '?';
+ }
+ }
+ else {
+ *dest++ = '?';
+ }
+ break;
+ default:
+ *dest++ = *source;
+ }
+ source++;
+ }
+
+ *dest = 0;
+ return dest - start;
+}
+
+int UrlDecode(const char *source, char *dest) {
+ return InternalUrlDecode(source, dest, true);
+}
+
+int UrlDecodeWithoutEncodingSpaceAsPlus(const char *source, char *dest) {
+ return InternalUrlDecode(source, dest, false);
+}
+
+bool IsValidUrlChar(char ch, bool unsafe_only) {
+ if (unsafe_only) {
+ return !(ch <= ' ' || strchr("\\\"^&`<>[]{}", ch));
+ } else {
+ return isalnum(ch) || strchr("-_.!~*'()", ch);
+ }
+}
+
+int InternalUrlEncode(const char *source, char *dest, unsigned int max,
+ bool encode_space_as_plus, bool unsafe_only) {
+ static const char *digits = "0123456789ABCDEF";
+ if (max == 0) {
+ return 0;
+ }
+
+ char *start = dest;
+ while (static_cast<unsigned>(dest - start) < max && *source) {
+ unsigned char ch = static_cast<unsigned char>(*source);
+ if (*source == ' ' && encode_space_as_plus && !unsafe_only) {
+ *dest++ = '+';
+ } else if (IsValidUrlChar(ch, unsafe_only)) {
+ *dest++ = *source;
+ } else {
+ if (static_cast<unsigned>(dest - start) + 4 > max) {
+ break;
+ }
+ *dest++ = '%';
+ *dest++ = digits[(ch >> 4) & 0x0F];
+ *dest++ = digits[ ch & 0x0F];
+ }
+ source++;
+ }
+ ASSERT(static_cast<unsigned int>(dest - start) < max);
+ *dest = 0;
+
+ return dest - start;
+}
+
+int UrlEncode(const char *source, char *dest, unsigned max) {
+ return InternalUrlEncode(source, dest, max, true, false);
+}
+
+int UrlEncodeWithoutEncodingSpaceAsPlus(const char *source, char *dest,
+ unsigned max) {
+ return InternalUrlEncode(source, dest, max, false, false);
+}
+
+int UrlEncodeOnlyUnsafeChars(const char *source, char *dest, unsigned max) {
+ return InternalUrlEncode(source, dest, max, false, true);
+}
+
+std::string
+InternalUrlDecodeString(const std::string & encoded,
+ bool encode_space_as_plus) {
+ size_t needed_length = encoded.length() + 1;
+ char* buf = STACK_ARRAY(char, needed_length);
+ InternalUrlDecode(encoded.c_str(), buf, encode_space_as_plus);
+ return buf;
+}
+
+std::string
+UrlDecodeString(const std::string & encoded) {
+ return InternalUrlDecodeString(encoded, true);
+}
+
+std::string
+UrlDecodeStringWithoutEncodingSpaceAsPlus(const std::string & encoded) {
+ return InternalUrlDecodeString(encoded, false);
+}
+
+std::string
+InternalUrlEncodeString(const std::string & decoded,
+ bool encode_space_as_plus,
+ bool unsafe_only) {
+ size_t needed_length = decoded.length() * 3 + 1;
+ char* buf = STACK_ARRAY(char, needed_length);
+ InternalUrlEncode(decoded.c_str(), buf, needed_length,
+ encode_space_as_plus, unsafe_only);
+ return buf;
+}
+
+std::string
+UrlEncodeString(const std::string & decoded) {
+ return InternalUrlEncodeString(decoded, true, false);
+}
+
+std::string
+UrlEncodeStringWithoutEncodingSpaceAsPlus(const std::string & decoded) {
+ return InternalUrlEncodeString(decoded, false, false);
+}
+
+std::string
+UrlEncodeStringForOnlyUnsafeChars(const std::string & decoded) {
+ return InternalUrlEncodeString(decoded, false, true);
+}
diff --git a/third_party/libjingle/source/talk/base/urlencode.h b/third_party/libjingle/source/talk/base/urlencode.h
new file mode 100644
index 0000000..05165e8
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/urlencode.h
@@ -0,0 +1,60 @@
+/*
+ * libjingle
+ * Copyright 2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _URLENCODE_H_
+#define _URLENCODE_H_
+
+#include <string>
+
+// Decode all encoded characters. Also decode + as space.
+int UrlDecode(const char *source, char *dest);
+
+// Decode all encoded characters.
+int UrlDecodeWithoutEncodingSpaceAsPlus(const char *source, char *dest);
+
+// Encode all characters except alphas, numbers, and -_.!~*'()
+// Also encode space as +.
+int UrlEncode(const char *source, char *dest, unsigned max);
+
+// Encode all characters except alphas, numbers, and -_.!~*'()
+int UrlEncodeWithoutEncodingSpaceAsPlus(const char *source, char *dest,
+ unsigned max);
+
+// Encode only unsafe chars, including \ "^&`<>[]{}
+// Also encode space as %20, instead of +
+int UrlEncodeOnlyUnsafeChars(const char *source, char *dest, unsigned max);
+
+std::string UrlDecodeString(const std::string & encoded);
+std::string UrlDecodeStringWithoutEncodingSpaceAsPlus(
+ const std::string & encoded);
+std::string UrlEncodeString(const std::string & decoded);
+std::string UrlEncodeStringWithoutEncodingSpaceAsPlus(
+ const std::string & decoded);
+std::string UrlEncodeStringForOnlyUnsafeChars(const std::string & decoded);
+
+#endif
+
diff --git a/third_party/libjingle/source/talk/base/win32.cc b/third_party/libjingle/source/talk/base/win32.cc
new file mode 100644
index 0000000..9a816c4
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32.cc
@@ -0,0 +1,183 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/win32.h"
+#include <algorithm>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+
+namespace talk_base {
+
+//
+// Unix time is in seconds relative to 1/1/1970. So we compute the windows
+// FILETIME of that time/date, then we add/subtract in appropriate units to
+// convert to/from unix time.
+// The units of FILETIME are 100ns intervals, so by multiplying by or dividing
+// by 10000000, we can convert to/from seconds.
+//
+// FileTime = UnixTime*10000000 + FileTime(1970)
+// UnixTime = (FileTime-FileTime(1970))/10000000
+//
+
+void FileTimeToUnixTime(const FILETIME& ft, time_t* ut) {
+ ASSERT(NULL != ut);
+
+ // FILETIME has an earlier date base than time_t (1/1/1970), so subtract off
+ // the difference.
+ SYSTEMTIME base_st;
+ memset(&base_st, 0, sizeof(base_st));
+ base_st.wDay = 1;
+ base_st.wMonth = 1;
+ base_st.wYear = 1970;
+
+ FILETIME base_ft;
+ SystemTimeToFileTime(&base_st, &base_ft);
+
+ ULARGE_INTEGER base_ul, current_ul;
+ memcpy(&base_ul, &base_ft, sizeof(FILETIME));
+ memcpy(&current_ul, &ft, sizeof(FILETIME));
+
+ // Divide by big number to convert to seconds, then subtract out the 1970
+ // base date value.
+ const ULONGLONG RATIO = 10000000;
+ *ut = static_cast<time_t>((current_ul.QuadPart - base_ul.QuadPart) / RATIO);
+}
+
+void UnixTimeToFileTime(const time_t& ut, FILETIME* ft) {
+ ASSERT(NULL != ft);
+
+ // FILETIME has an earlier date base than time_t (1/1/1970), so add in
+ // the difference.
+ SYSTEMTIME base_st;
+ memset(&base_st, 0, sizeof(base_st));
+ base_st.wDay = 1;
+ base_st.wMonth = 1;
+ base_st.wYear = 1970;
+
+ FILETIME base_ft;
+ SystemTimeToFileTime(&base_st, &base_ft);
+
+ ULARGE_INTEGER base_ul;
+ memcpy(&base_ul, &base_ft, sizeof(FILETIME));
+
+ // Multiply by big number to convert to 100ns units, then add in the 1970
+ // base date value.
+ const ULONGLONG RATIO = 10000000;
+ ULARGE_INTEGER current_ul;
+ current_ul.QuadPart = base_ul.QuadPart + static_cast<int64>(ut) * RATIO;
+ memcpy(ft, &current_ul, sizeof(FILETIME));
+}
+
+bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename) {
+ // TODO: Integrate into fileutils.h
+ // TODO: Handle wide and non-wide cases via TCHAR?
+ // TODO: Skip \\?\ processing if the length is not > MAX_PATH?
+ // TODO: Write unittests
+
+ // Convert to Utf16
+ int wlen = ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.length() + 1,
+ NULL, 0);
+ if (0 == wlen) {
+ return false;
+ }
+ wchar_t* wfilename = STACK_ARRAY(wchar_t, wlen);
+ if (0 == ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.length() + 1,
+ wfilename, wlen)) {
+ return false;
+ }
+ // Replace forward slashes with backslashes
+ std::replace(wfilename, wfilename + wlen, L'/', L'\\');
+ // Convert to complete filename
+ DWORD full_len = ::GetFullPathName(wfilename, 0, NULL, NULL);
+ if (0 == full_len) {
+ return false;
+ }
+ wchar_t* filepart = NULL;
+ wchar_t* full_filename = STACK_ARRAY(wchar_t, full_len + 6);
+ wchar_t* start = full_filename + 6;
+ if (0 == ::GetFullPathName(wfilename, full_len, start, &filepart)) {
+ return false;
+ }
+ // Add long-path prefix
+ const wchar_t kLongPathPrefix[] = L"\\\\?\\UNC";
+ if ((start[0] != L'\\') || (start[1] != L'\\')) {
+ // Non-unc path: <pathname>
+ // Becomes: \\?\<pathname>
+ start -= 4;
+ ASSERT(start >= full_filename);
+ memcpy(start, kLongPathPrefix, 4 * sizeof(wchar_t));
+ } else if (start[2] != L'?') {
+ // Unc path: \\<server>\<pathname>
+ // Becomes: \\?\UNC\<server>\<pathname>
+ start -= 6;
+ ASSERT(start >= full_filename);
+ memcpy(start, kLongPathPrefix, 7 * sizeof(wchar_t));
+ } else {
+ // Already in long-path form.
+ }
+ filename->assign(start);
+ return true;
+}
+
+bool GetOsVersion(int* major, int* minor, int* build) {
+ OSVERSIONINFO info = {0};
+ info.dwOSVersionInfoSize = sizeof(info);
+ if (GetVersionEx(&info)) {
+ if (major) *major = info.dwMajorVersion;
+ if (minor) *minor = info.dwMinorVersion;
+ if (build) *build = info.dwBuildNumber;
+ return true;
+ }
+ return false;
+}
+
+bool GetCurrentProcessIntegrityLevel(int* level) {
+ bool ret = false;
+ HANDLE process = GetCurrentProcess(), token;
+ if (OpenProcessToken(process, TOKEN_QUERY | TOKEN_QUERY_SOURCE, &token)) {
+ DWORD size;
+ if (!GetTokenInformation(token, TokenIntegrityLevel, NULL, 0, &size) &&
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+
+ char* buf = STACK_ARRAY(char, size);
+ TOKEN_MANDATORY_LABEL* til =
+ reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buf);
+ if (GetTokenInformation(token, TokenIntegrityLevel, til, size, &size)) {
+
+ DWORD count = *GetSidSubAuthorityCount(til->Label.Sid);
+ *level = *GetSidSubAuthority(til->Label.Sid, count - 1);
+ ret = true;
+ }
+ }
+ CloseHandle(token);
+ }
+ return ret;
+}
+
+} // namespace talk_base
+
diff --git a/third_party/libjingle/source/talk/base/win32.h b/third_party/libjingle/source/talk/base/win32.h
new file mode 100644
index 0000000..6274b0e
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32.h
@@ -0,0 +1,130 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_WIN32_H_
+#define TALK_BASE_WIN32_H_
+
+#ifdef WIN32
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <winsock2.h>
+#include <windows.h>
+
+#ifndef SECURITY_MANDATORY_LABEL_AUTHORITY
+// Add defines that we use if we are compiling against older sdks
+#define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L)
+#define TokenIntegrityLevel static_cast<TOKEN_INFORMATION_CLASS>(0x19)
+typedef struct _TOKEN_MANDATORY_LABEL {
+ SID_AND_ATTRIBUTES Label;
+} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL;
+#endif // SECURITY_MANDATORY_LABEL_AUTHORITY
+
+#undef SetPort
+
+#include <string>
+
+#include "talk/base/stringutils.h"
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline std::wstring ToUtf16(const char* utf8, size_t len) {
+ int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0);
+ wchar_t* ws = STACK_ARRAY(wchar_t, len16);
+ ::MultiByteToWideChar(CP_UTF8, 0, utf8, len, ws, len16);
+ return std::wstring(ws, len16);
+}
+
+inline std::wstring ToUtf16(const std::string& str) {
+ return ToUtf16(str.data(), str.length());
+}
+
+inline std::string ToUtf8(const wchar_t* wide, size_t len) {
+ int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, len, NULL, 0, NULL, NULL);
+ char* ns = STACK_ARRAY(char, len8);
+ ::WideCharToMultiByte(CP_UTF8, 0, wide, len, ns, len8, NULL, NULL);
+ return std::string(ns, len8);
+}
+
+inline std::string ToUtf8(const std::wstring& wstr) {
+ return ToUtf8(wstr.data(), wstr.length());
+}
+
+// Convert FILETIME to time_t
+void FileTimeToUnixTime(const FILETIME& ft, time_t* ut);
+
+// Convert time_t to FILETIME
+void UnixTimeToFileTime(const time_t& ut, FILETIME * ft);
+
+// Convert a Utf8 path representation to a non-length-limited Unicode pathname.
+bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename);
+
+// Convert a FILETIME to a UInt64
+inline uint64 ToUInt64(const FILETIME& ft) {
+ ULARGE_INTEGER r = {ft.dwLowDateTime, ft.dwHighDateTime};
+ return r.QuadPart;
+}
+
+enum WindowsMajorVersions {
+ kWindows2000 = 5,
+ kWindowsVista = 6,
+};
+bool GetOsVersion(int* major, int* minor, int* build);
+
+inline bool IsWindowsVistaOrLater() {
+ int major;
+ return (GetOsVersion(&major, NULL, NULL) && major >= kWindowsVista);
+}
+
+inline bool IsWindowsXpOrLater() {
+ int major, minor;
+ return (GetOsVersion(&major, &minor, NULL) &&
+ (major >= kWindowsVista ||
+ (major == kWindows2000 && minor >= 1)));
+}
+
+// Determine the current integrity level of the process.
+bool GetCurrentProcessIntegrityLevel(int* level);
+
+inline bool IsCurrentProcessLowIntegrity() {
+ int level;
+ return (GetCurrentProcessIntegrityLevel(&level) &&
+ level < SECURITY_MANDATORY_MEDIUM_RID);
+}
+
+bool AdjustCurrentProcessPrivilege(const TCHAR* privilege, bool to_enable);
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // WIN32
+#endif // TALK_BASE_WIN32_H_
diff --git a/third_party/libjingle/source/talk/base/win32filesystem.cc b/third_party/libjingle/source/talk/base/win32filesystem.cc
new file mode 100644
index 0000000..aeebc86
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32filesystem.cc
@@ -0,0 +1,369 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/win32filesystem.h"
+
+#include "talk/base/win32.h"
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+
+#include "talk/base/fileutils.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/stream.h"
+#include "talk/base/stringutils.h"
+
+// In several places in this file, we test the integrity level of the process
+// before calling GetLongPathName. We do this because calling GetLongPathName
+// when running under protected mode IE (a low integrity process) can result in
+// a virtualized path being returned, which is wrong if you only plan to read.
+// TODO(juberti): Waiting to hear back from IE team on whether this is the
+// best approach; IEIsProtectedModeProcess is another possible solution.
+
+namespace talk_base {
+
+bool Win32Filesystem::CreateFolder(const Pathname &pathname) {
+ if (pathname.pathname().empty() || !pathname.filename().empty())
+ return false;
+
+ std::wstring path16;
+ if (!Utf8ToWindowsFilename(pathname.pathname(), &path16))
+ return false;
+
+ DWORD res = ::GetFileAttributes(path16.c_str());
+ if (res != INVALID_FILE_ATTRIBUTES) {
+ // Something exists at this location, check if it is a directory
+ return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0);
+ } else if ((GetLastError() != ERROR_FILE_NOT_FOUND)
+ && (GetLastError() != ERROR_PATH_NOT_FOUND)) {
+ // Unexpected error
+ return false;
+ }
+
+ // Directory doesn't exist, look up one directory level
+ if (!pathname.parent_folder().empty()) {
+ Pathname parent(pathname);
+ parent.SetFolder(pathname.parent_folder());
+ if (!CreateFolder(parent)) {
+ return false;
+ }
+ }
+
+ return (::CreateDirectory(path16.c_str(), NULL) != 0);
+}
+
+FileStream *Win32Filesystem::OpenFile(const Pathname &filename,
+ const std::string &mode) {
+ FileStream *fs = new FileStream();
+ if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str())) {
+ delete fs;
+ fs = NULL;
+ }
+ return fs;
+}
+
+bool Win32Filesystem::DeleteFile(const Pathname &filename) {
+ LOG(LS_INFO) << "Deleting file " << filename.pathname();
+ if (!IsFile(filename)) {
+ ASSERT(IsFile(filename));
+ return false;
+ }
+ return ::DeleteFile(ToUtf16(filename.pathname()).c_str()) != 0;
+}
+
+bool Win32Filesystem::DeleteEmptyFolder(const Pathname &folder) {
+ LOG(LS_INFO) << "Deleting folder " << folder.pathname();
+
+ std::string no_slash(folder.pathname(), 0, folder.pathname().length()-1);
+ return ::RemoveDirectory(ToUtf16(no_slash).c_str()) != 0;
+}
+
+bool Win32Filesystem::GetTemporaryFolder(Pathname &pathname, bool create,
+ const std::string *append) {
+ wchar_t buffer[MAX_PATH + 1];
+ if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
+ return false;
+ if (!IsCurrentProcessLowIntegrity() &&
+ !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ size_t len = strlen(buffer);
+ if ((len > 0) && (buffer[len-1] != '\\')) {
+ len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, L"\\");
+ }
+ if (len >= ARRAY_SIZE(buffer) - 1)
+ return false;
+ pathname.clear();
+ pathname.SetFolder(ToUtf8(buffer));
+ if (append != NULL) {
+ ASSERT(!append->empty());
+ pathname.AppendFolder(*append);
+ }
+ return !create || CreateFolder(pathname);
+}
+
+std::string Win32Filesystem::TempFilename(const Pathname &dir,
+ const std::string &prefix) {
+ wchar_t filename[MAX_PATH];
+ if (::GetTempFileName(ToUtf16(dir.pathname()).c_str(),
+ ToUtf16(prefix).c_str(), 0, filename) != 0)
+ return ToUtf8(filename);
+ ASSERT(false);
+ return "";
+}
+
+bool Win32Filesystem::MoveFile(const Pathname &old_path,
+ const Pathname &new_path) {
+ if (!IsFile(old_path)) {
+ ASSERT(IsFile(old_path));
+ return false;
+ }
+ LOG(LS_INFO) << "Moving " << old_path.pathname()
+ << " to " << new_path.pathname();
+ return ::MoveFile(ToUtf16(old_path.pathname()).c_str(),
+ ToUtf16(new_path.pathname()).c_str()) != 0;
+}
+
+bool Win32Filesystem::MoveFolder(const Pathname &old_path,
+ const Pathname &new_path) {
+ if (!IsFolder(old_path)) {
+ ASSERT(IsFolder(old_path));
+ return false;
+ }
+ LOG(LS_INFO) << "Moving " << old_path.pathname()
+ << " to " << new_path.pathname();
+ if (::MoveFile(ToUtf16(old_path.pathname()).c_str(),
+ ToUtf16(new_path.pathname()).c_str()) == 0) {
+ if (::GetLastError() != ERROR_NOT_SAME_DEVICE) {
+ LOG_GLE(LS_ERROR) << "Failed to move file";
+ return false;
+ }
+ if (!CopyFolder(old_path, new_path))
+ return false;
+ if (!DeleteFolderAndContents(old_path))
+ return false;
+ }
+ return true;
+}
+
+bool Win32Filesystem::IsFolder(const Pathname &path) {
+ WIN32_FILE_ATTRIBUTE_DATA data = {0};
+ if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
+ GetFileExInfoStandard, &data))
+ return false;
+ return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
+ FILE_ATTRIBUTE_DIRECTORY;
+}
+
+bool Win32Filesystem::IsFile(const Pathname &path) {
+ WIN32_FILE_ATTRIBUTE_DATA data = {0};
+ if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
+ GetFileExInfoStandard, &data))
+ return false;
+ return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
+}
+
+bool Win32Filesystem::IsAbsent(const Pathname& path) {
+ WIN32_FILE_ATTRIBUTE_DATA data = {0};
+ if (0 != ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
+ GetFileExInfoStandard, &data))
+ return false;
+ DWORD err = ::GetLastError();
+ return (ERROR_FILE_NOT_FOUND == err || ERROR_PATH_NOT_FOUND == err);
+}
+
+bool Win32Filesystem::CopyFile(const Pathname &old_path,
+ const Pathname &new_path) {
+ return ::CopyFile(ToUtf16(old_path.pathname()).c_str(),
+ ToUtf16(new_path.pathname()).c_str(), TRUE) != 0;
+}
+
+bool Win32Filesystem::IsTemporaryPath(const Pathname& pathname) {
+ TCHAR buffer[MAX_PATH + 1];
+ if (!::GetTempPath(ARRAY_SIZE(buffer), buffer))
+ return false;
+ if (!IsCurrentProcessLowIntegrity() &&
+ !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ return (::strnicmp(ToUtf16(pathname.pathname()).c_str(),
+ buffer, strlen(buffer)) == 0);
+}
+
+bool Win32Filesystem::GetFileSize(const Pathname &pathname, size_t *size) {
+ WIN32_FILE_ATTRIBUTE_DATA data = {0};
+ if (::GetFileAttributesEx(ToUtf16(pathname.pathname()).c_str(),
+ GetFileExInfoStandard, &data) == 0)
+ return false;
+ *size = data.nFileSizeLow;
+ return true;
+}
+
+bool Win32Filesystem::GetFileTime(const Pathname& path, FileTimeType which,
+ time_t* time) {
+ WIN32_FILE_ATTRIBUTE_DATA data = {0};
+ if (::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
+ GetFileExInfoStandard, &data) == 0)
+ return false;
+ switch (which) {
+ case FTT_CREATED:
+ FileTimeToUnixTime(data.ftCreationTime, time);
+ break;
+ case FTT_MODIFIED:
+ FileTimeToUnixTime(data.ftLastWriteTime, time);
+ break;
+ case FTT_ACCESSED:
+ FileTimeToUnixTime(data.ftLastAccessTime, time);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool Win32Filesystem::GetAppPathname(Pathname* path) {
+ TCHAR buffer[MAX_PATH + 1];
+ if (0 == ::GetModuleFileName(NULL, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ path->SetPathname(ToUtf8(buffer));
+ return true;
+}
+
+bool Win32Filesystem::GetAppDataFolder(Pathname* path, bool per_user) {
+ ASSERT(!organization_name_.empty());
+ ASSERT(!application_name_.empty());
+ TCHAR buffer[MAX_PATH + 1];
+ int csidl = per_user ? CSIDL_LOCAL_APPDATA : CSIDL_COMMON_APPDATA;
+ if (!::SHGetSpecialFolderPath(NULL, buffer, csidl, TRUE))
+ return false;
+ if (!IsCurrentProcessLowIntegrity() &&
+ !::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer)))
+ return false;
+ size_t len = strcatn(buffer, ARRAY_SIZE(buffer), __T("\\"));
+ len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ ToUtf16(organization_name_).c_str());
+ if ((len > 0) && (buffer[len-1] != __T('\\'))) {
+ len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, __T("\\"));
+ }
+ len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len,
+ ToUtf16(application_name_).c_str());
+ if ((len > 0) && (buffer[len-1] != __T('\\'))) {
+ len += strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, __T("\\"));
+ }
+ if (len >= ARRAY_SIZE(buffer) - 1)
+ return false;
+ path->clear();
+ path->SetFolder(ToUtf8(buffer));
+ return CreateFolder(*path);
+}
+
+bool Win32Filesystem::GetAppTempFolder(Pathname* path) {
+ if (!GetAppPathname(path))
+ return false;
+ std::string filename(path->filename());
+ return GetTemporaryFolder(*path, true, &filename);
+}
+
+bool Win32Filesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) {
+ if (!freebytes) {
+ return false;
+ }
+ char drive[4];
+ std::wstring drive16;
+ const wchar_t* target_drive = NULL;
+ if (path.GetDrive(drive, sizeof(drive))) {
+ drive16 = ToUtf16(drive);
+ target_drive = drive16.c_str();
+ } else if (path.folder().substr(0, 2) == "\\\\") {
+ // UNC path, fail.
+ // TODO: Handle UNC paths.
+ return false;
+ } else {
+ // The path is probably relative. GetDriveType and GetDiskFreeSpaceEx
+ // use the current drive if NULL is passed as the drive name.
+ // TODO: Add method to Pathname to determine if the path is relative.
+ // TODO: Add method to Pathname to convert a path to absolute.
+ }
+ UINT driveType = ::GetDriveType(target_drive);
+ if ( (driveType & DRIVE_REMOTE) || (driveType & DRIVE_UNKNOWN) ) {
+ LOG(LS_VERBOSE) << " remove or unknown drive " << drive;
+ return false;
+ }
+
+ int64 totalNumberOfBytes; // receives the number of bytes on disk
+ int64 totalNumberOfFreeBytes; // receives the free bytes on disk
+ // make sure things won't change in 64 bit machine
+ // TODO replace with compile time assert
+ ASSERT(sizeof(ULARGE_INTEGER) == sizeof(uint64)); //NOLINT
+ if (::GetDiskFreeSpaceEx(target_drive,
+ (PULARGE_INTEGER)freebytes,
+ (PULARGE_INTEGER)&totalNumberOfBytes,
+ (PULARGE_INTEGER)&totalNumberOfFreeBytes)) {
+ return true;
+ } else {
+ LOG(LS_VERBOSE) << " GetDiskFreeSpaceEx returns error ";
+ return false;
+ }
+}
+
+Pathname Win32Filesystem::GetCurrentDirectory() {
+ Pathname cwd;
+ int path_len = 0;
+ scoped_array<wchar_t> path;
+ do {
+ int needed = ::GetCurrentDirectory(path_len, path.get());
+ if (needed == 0) {
+ // Error.
+ LOG_GLE(LS_ERROR) << "::GetCurrentDirectory() failed";
+ return cwd; // returns empty pathname
+ }
+ if (needed <= path_len) {
+ // It wrote successfully.
+ break;
+ }
+ // Else need to re-alloc for "needed".
+ path.reset(new wchar_t[needed]);
+ path_len = needed;
+ } while (true);
+ cwd.SetFolder(ToUtf8(path.get()));
+ return cwd;
+}
+
+// TODO: Consider overriding DeleteFolderAndContents for speed and potentially
+// better OS integration (recycle bin?)
+/*
+ std::wstring temp_path16 = ToUtf16(temp_path.pathname());
+ temp_path16.append(1, '*');
+ temp_path16.append(1, '\0');
+
+ SHFILEOPSTRUCT file_op = { 0 };
+ file_op.wFunc = FO_DELETE;
+ file_op.pFrom = temp_path16.c_str();
+ file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
+ return (0 == SHFileOperation(&file_op));
+*/
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/win32filesystem.h b/third_party/libjingle/source/talk/base/win32filesystem.h
new file mode 100644
index 0000000..ef1b28f
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32filesystem.h
@@ -0,0 +1,114 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_WIN32FILESYSTEM_H__
+#define _TALK_BASE_WIN32FILESYSTEM_H__
+
+#include "fileutils.h"
+
+namespace talk_base {
+
+class Win32Filesystem : public FilesystemInterface {
+ public:
+ // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise,
+ // returns NULL.
+ virtual FileStream *OpenFile(const Pathname &filename,
+ const std::string &mode);
+
+ // This will attempt to delete the path located at filename.
+ // If the path points to a folder, it will fail with VERIFY
+ virtual bool DeleteFile(const Pathname &filename);
+
+ // This will attempt to delete an empty folder. If the path does not point to
+ // a folder, it fails with VERIFY. If the folder is not empty, it fails normally
+ virtual bool DeleteEmptyFolder(const Pathname &folder);
+
+ // Creates a directory. This will call itself recursively to create /foo/bar even if
+ // /foo does not exist.
+ // Returns TRUE if function succeeds
+ virtual bool CreateFolder(const Pathname &pathname);
+
+ // This moves a file from old_path to new_path. If the new path is on a
+ // different volume than the old, it will attempt to copy and then delete
+ // the folder
+ // Returns true if the file is successfully moved
+ virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path);
+
+ // Moves a folder from old_path to new_path. If the new path is on a different
+ // volume from the old, it will attempt to Copy and then Delete the folder
+ // Returns true if the folder is successfully moved
+ virtual bool MoveFolder(const Pathname &old_path, const Pathname &new_path);
+
+ // This copies a file from old_path to _new_path
+ // Returns true if function succeeds
+ virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path);
+
+ // Returns true if a pathname is a directory
+ virtual bool IsFolder(const Pathname& pathname);
+
+ // Returns true if a file exists at path
+ virtual bool IsFile(const Pathname &path);
+
+ // Returns true if pathname refers to no filesystem object, every parent
+ // directory either exists, or is also absent.
+ virtual bool IsAbsent(const Pathname& pathname);
+
+ // Returns true if pathname represents a temporary location on the system.
+ virtual bool IsTemporaryPath(const Pathname& pathname);
+
+ // All of the following functions set pathname and return true if successful.
+ // Returned paths always include a trailing backslash.
+ // If create is true, the path will be recursively created.
+ // If append is non-NULL, it will be appended (and possibly created).
+
+ virtual std::string TempFilename(const Pathname &dir, const std::string &prefix);
+
+ virtual bool GetFileSize(const Pathname& path, size_t* size);
+ virtual bool GetFileTime(const Pathname& path, FileTimeType which,
+ time_t* time);
+
+ // A folder appropriate for storing temporary files (Contents are
+ // automatically deleted when the program exists)
+ virtual bool GetTemporaryFolder(Pathname &path, bool create,
+ const std::string *append);
+
+ // Returns the path to the running application.
+ virtual bool GetAppPathname(Pathname* path);
+
+ virtual bool GetAppDataFolder(Pathname* path, bool per_user);
+
+ // Get a temporary folder that is unique to the current user and application.
+ virtual bool GetAppTempFolder(Pathname* path);
+
+ virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes);
+
+ virtual Pathname GetCurrentDirectory();
+};
+
+} // namespace talk_base
+
+#endif // _WIN32FILESYSTEM_H__
diff --git a/third_party/libjingle/source/talk/base/win32securityerrors.cc b/third_party/libjingle/source/talk/base/win32securityerrors.cc
new file mode 100644
index 0000000..50f4f66
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32securityerrors.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/win32.h"
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern const ConstantLabel SECURITY_ERRORS[];
+
+const ConstantLabel SECURITY_ERRORS[] = {
+ KLABEL(SEC_I_COMPLETE_AND_CONTINUE),
+ KLABEL(SEC_I_COMPLETE_NEEDED),
+ KLABEL(SEC_I_CONTEXT_EXPIRED),
+ KLABEL(SEC_I_CONTINUE_NEEDED),
+ KLABEL(SEC_I_INCOMPLETE_CREDENTIALS),
+ KLABEL(SEC_I_RENEGOTIATE),
+ KLABEL(SEC_E_CERT_EXPIRED),
+ KLABEL(SEC_E_INCOMPLETE_MESSAGE),
+ KLABEL(SEC_E_INSUFFICIENT_MEMORY),
+ KLABEL(SEC_E_INTERNAL_ERROR),
+ KLABEL(SEC_E_INVALID_HANDLE),
+ KLABEL(SEC_E_INVALID_TOKEN),
+ KLABEL(SEC_E_LOGON_DENIED),
+ KLABEL(SEC_E_NO_AUTHENTICATING_AUTHORITY),
+ KLABEL(SEC_E_NO_CREDENTIALS),
+ KLABEL(SEC_E_NOT_OWNER),
+ KLABEL(SEC_E_OK),
+ KLABEL(SEC_E_SECPKG_NOT_FOUND),
+ KLABEL(SEC_E_TARGET_UNKNOWN),
+ KLABEL(SEC_E_UNKNOWN_CREDENTIALS),
+ KLABEL(SEC_E_UNSUPPORTED_FUNCTION),
+ KLABEL(SEC_E_UNTRUSTED_ROOT),
+ KLABEL(SEC_E_WRONG_PRINCIPAL),
+ LASTLABEL
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/win32socketinit.cc b/third_party/libjingle/source/talk/base/win32socketinit.cc
new file mode 100644
index 0000000..f6ac666
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32socketinit.cc
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/win32socketinit.h"
+
+#include "talk/base/win32.h"
+
+namespace talk_base {
+
+// Please don't remove this function.
+void EnsureWinsockInit() {
+ // The default implementation uses a global initializer, so WSAStartup
+ // happens at module load time. Thus we don't need to do anything here.
+ // The hook is provided so that a client that statically links with
+ // libjingle can override it, to provide its own initialization.
+}
+
+#ifdef WIN32
+class WinsockInitializer {
+ public:
+ WinsockInitializer() {
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD(1, 0);
+ err_ = WSAStartup(wVersionRequested, &wsaData);
+ }
+ ~WinsockInitializer() {
+ if (!err_)
+ WSACleanup();
+ }
+ int error() {
+ return err_;
+ }
+ private:
+ int err_;
+};
+WinsockInitializer g_winsockinit;
+#endif
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/win32socketinit.h b/third_party/libjingle/source/talk/base/win32socketinit.h
new file mode 100644
index 0000000..f56b7ff
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32socketinit.h
@@ -0,0 +1,37 @@
+/*
+ * libjingle
+ * Copyright 2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_WIN32SOCKETINIT_H_
+#define TALK_BASE_WIN32SOCKETINIT_H_
+
+namespace talk_base {
+
+void EnsureWinsockInit();
+
+} // namespace talk_base
+
+#endif // TALK_BASE_WIN32SOCKETINIT_H_
diff --git a/third_party/libjingle/source/talk/base/win32socketserver.cc b/third_party/libjingle/source/talk/base/win32socketserver.cc
new file mode 100644
index 0000000..6a5307b
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32socketserver.cc
@@ -0,0 +1,811 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/win32socketserver.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/winping.h"
+#include "talk/base/win32window.h"
+#include <ws2tcpip.h> // NOLINT
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+///////////////////////////////////////////////////////////////////////////////
+
+// TODO(juberti): Move this to a common place where PhysicalSocketServer can
+// share it.
+// Standard MTUs
+static 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
+};
+
+static const uint32 IP_HEADER_SIZE = 20;
+static const uint32 ICMP_HEADER_SIZE = 8;
+
+// TODO(juberti): Enable for production builds also? Use FormatMessage?
+#ifdef _DEBUG
+LPCSTR WSAErrorToString(int error, LPCSTR *description_result) {
+ LPCSTR string = "Unspecified";
+ LPCSTR description = "Unspecified description";
+ switch (error) {
+ case ERROR_SUCCESS:
+ string = "SUCCESS";
+ description = "Operation succeeded";
+ break;
+ case WSAEWOULDBLOCK:
+ string = "WSAEWOULDBLOCK";
+ description = "Using a non-blocking socket, will notify later";
+ break;
+ case WSAEACCES:
+ string = "WSAEACCES";
+ description = "Access denied, or sharing violation";
+ break;
+ case WSAEADDRNOTAVAIL:
+ string = "WSAEADDRNOTAVAIL";
+ description = "Address is not valid in this context";
+ break;
+ case WSAENETDOWN:
+ string = "WSAENETDOWN";
+ description = "Network is down";
+ break;
+ case WSAENETUNREACH:
+ string = "WSAENETUNREACH";
+ description = "Network is up, but unreachable";
+ break;
+ case WSAENETRESET:
+ string = "WSANETRESET";
+ description = "Connection has been reset due to keep-alive activity";
+ break;
+ case WSAECONNABORTED:
+ string = "WSAECONNABORTED";
+ description = "Aborted by host";
+ break;
+ case WSAECONNRESET:
+ string = "WSAECONNRESET";
+ description = "Connection reset by host";
+ break;
+ case WSAETIMEDOUT:
+ string = "WSAETIMEDOUT";
+ description = "Timed out, host failed to respond";
+ break;
+ case WSAECONNREFUSED:
+ string = "WSAECONNREFUSED";
+ description = "Host actively refused connection";
+ break;
+ case WSAEHOSTDOWN:
+ string = "WSAEHOSTDOWN";
+ description = "Host is down";
+ break;
+ case WSAEHOSTUNREACH:
+ string = "WSAEHOSTUNREACH";
+ description = "Host is unreachable";
+ break;
+ case WSAHOST_NOT_FOUND:
+ string = "WSAHOST_NOT_FOUND";
+ description = "No such host is known";
+ break;
+ }
+ if (description_result) {
+ *description_result = description;
+ }
+ return string;
+}
+
+void ReportWSAError(LPCSTR context, int error, const SocketAddress& address) {
+ LPCSTR description_string;
+ LPCSTR error_string = WSAErrorToString(error, &description_string);
+ LOG(LS_INFO) << context << " = " << error
+ << " (" << error_string << ":" << description_string << ") ["
+ << address.ToString() << "]";
+}
+#else
+void ReportWSAError(LPCSTR context, int error, const SocketAddress& address) {}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Win32Socket::EventSink
+/////////////////////////////////////////////////////////////////////////////
+
+#define WM_SOCKETNOTIFY (WM_USER + 50)
+#define WM_DNSNOTIFY (WM_USER + 51)
+
+struct Win32Socket::DnsLookup {
+ HANDLE handle;
+ uint16 port;
+ char buffer[MAXGETHOSTSTRUCT];
+};
+
+class Win32Socket::EventSink : public Win32Window {
+ public:
+ explicit EventSink(Win32Socket * parent) : parent_(parent) { }
+
+ void Dispose();
+
+ virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result);
+ virtual void OnFinalMessage(HWND hWnd);
+
+ private:
+ bool OnSocketNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result);
+ bool OnDnsNotify(WPARAM wParam, LPARAM lParam, LRESULT& result);
+
+ Win32Socket * parent_;
+};
+
+void Win32Socket::EventSink::Dispose() {
+ parent_ = NULL;
+ if (::IsWindow(handle())) {
+ ::DestroyWindow(handle());
+ } else {
+ delete this;
+ }
+}
+
+bool Win32Socket::EventSink::OnMessage(UINT uMsg, WPARAM wParam,
+ LPARAM lParam, LRESULT& result) {
+ switch (uMsg) {
+ case WM_SOCKETNOTIFY:
+ case WM_TIMER:
+ return OnSocketNotify(uMsg, wParam, lParam, result);
+ case WM_DNSNOTIFY:
+ return OnDnsNotify(wParam, lParam, result);
+ }
+ return false;
+}
+
+bool Win32Socket::EventSink::OnSocketNotify(UINT uMsg, WPARAM wParam,
+ LPARAM lParam, LRESULT& result) {
+ result = 0;
+
+ int wsa_event = WSAGETSELECTEVENT(lParam);
+ int wsa_error = WSAGETSELECTERROR(lParam);
+
+ // Treat connect timeouts as close notifications
+ if (uMsg == WM_TIMER) {
+ wsa_event = FD_CLOSE;
+ wsa_error = WSAETIMEDOUT;
+ }
+
+ if (parent_)
+ parent_->OnSocketNotify(static_cast<SOCKET>(wParam), wsa_event, wsa_error);
+ return true;
+}
+
+bool Win32Socket::EventSink::OnDnsNotify(WPARAM wParam, LPARAM lParam,
+ LRESULT& result) {
+ result = 0;
+
+ int error = WSAGETASYNCERROR(lParam);
+ if (parent_)
+ parent_->OnDnsNotify(reinterpret_cast<HANDLE>(wParam), error);
+ return true;
+}
+
+void Win32Socket::EventSink::OnFinalMessage(HWND hWnd) {
+ delete this;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+/////////////////////////////////////////////////////////////////////////////
+
+Win32Socket::Win32Socket()
+ : socket_(INVALID_SOCKET), error_(0), state_(CS_CLOSED), connect_time_(0),
+ closing_(false), close_error_(0), sink_(NULL), dns_(NULL) {
+}
+
+Win32Socket::~Win32Socket() {
+ Close();
+}
+
+bool Win32Socket::CreateT(int type) {
+ Close();
+ int proto = (SOCK_DGRAM == type) ? IPPROTO_UDP : IPPROTO_TCP;
+ socket_ = ::WSASocket(AF_INET, type, proto, NULL, NULL, 0);
+ if (socket_ == INVALID_SOCKET) {
+ UpdateLastError();
+ return false;
+ }
+ if ((SOCK_DGRAM == type) && !SetAsync(FD_READ | FD_WRITE)) {
+ return false;
+ }
+ return true;
+}
+
+int Win32Socket::Attach(SOCKET s) {
+ ASSERT(socket_ == INVALID_SOCKET);
+ if (socket_ != INVALID_SOCKET)
+ return SOCKET_ERROR;
+
+ ASSERT(s != INVALID_SOCKET);
+ if (s == INVALID_SOCKET)
+ return SOCKET_ERROR;
+
+ socket_ = s;
+ state_ = CS_CONNECTED;
+
+ if (!SetAsync(FD_READ | FD_WRITE | FD_CLOSE))
+ return SOCKET_ERROR;
+
+ return 0;
+}
+
+void Win32Socket::SetTimeout(int ms) {
+ if (sink_)
+ ::SetTimer(sink_->handle(), 1, ms, 0);
+}
+
+SocketAddress Win32Socket::GetLocalAddress() const {
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getsockname(socket_, reinterpret_cast<sockaddr*>(&addr),
+ &addrlen);
+ SocketAddress address;
+ if (result >= 0) {
+ ASSERT(addrlen == sizeof(addr));
+ address.FromSockAddr(addr);
+ } else {
+ LOG(LS_WARNING) << "GetLocalAddress: unable to get local addr, socket="
+ << socket_;
+ }
+ return address;
+}
+
+SocketAddress Win32Socket::GetRemoteAddress() const {
+ sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getpeername(socket_, reinterpret_cast<sockaddr*>(&addr),
+ &addrlen);
+ ASSERT(addrlen == sizeof(addr));
+ SocketAddress address;
+ if (result >= 0) {
+ ASSERT(addrlen == sizeof(addr));
+ address.FromSockAddr(addr);
+ } else {
+ LOG(LS_WARNING) << "GetRemoteAddress: unable to get remote addr, socket="
+ << socket_;
+ }
+ return address;
+}
+
+int Win32Socket::Bind(const SocketAddress& addr) {
+ ASSERT(socket_ != INVALID_SOCKET);
+ if (socket_ == INVALID_SOCKET)
+ return SOCKET_ERROR;
+
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int err = ::bind(socket_, reinterpret_cast<sockaddr*>(&saddr), sizeof(saddr));
+ UpdateLastError();
+ return err;
+}
+
+int Win32Socket::Connect(const SocketAddress& addr) {
+ if ((socket_ == INVALID_SOCKET) && !CreateT(SOCK_STREAM))
+ return SOCKET_ERROR;
+
+ if (!sink_ && !SetAsync(FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE))
+ return SOCKET_ERROR;
+
+ // If we have an IP address, connect now.
+ if (!addr.IsUnresolved()) {
+ return DoConnect(addr);
+ }
+
+ LOG_F(LS_INFO) << "async dns lookup (" << addr.IPAsString() << ")";
+ DnsLookup * dns = new DnsLookup;
+ dns->handle = WSAAsyncGetHostByName(sink_->handle(), WM_DNSNOTIFY,
+ addr.IPAsString().c_str(), dns->buffer, sizeof(dns->buffer));
+
+ if (!dns->handle) {
+ LOG_F(LS_ERROR) << "WSAAsyncGetHostByName error: " << WSAGetLastError();
+ delete dns;
+ UpdateLastError();
+ Close();
+ return SOCKET_ERROR;
+ }
+
+ dns->port = addr.port();
+ dns_ = dns;
+ state_ = CS_CONNECTING;
+ return 0;
+}
+
+int Win32Socket::DoConnect(const SocketAddress& addr) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ connect_time_ = Time();
+ int result = connect(socket_, reinterpret_cast<SOCKADDR*>(&saddr),
+ sizeof(saddr));
+ if (result != SOCKET_ERROR) {
+ state_ = CS_CONNECTED;
+ } else {
+ int code = WSAGetLastError();
+ if (code == WSAEWOULDBLOCK) {
+ state_ = CS_CONNECTING;
+ } else {
+ ReportWSAError("WSAAsync:connect", code, addr);
+ error_ = code;
+ Close();
+ return SOCKET_ERROR;
+ }
+ }
+ addr_ = addr;
+
+ return 0;
+}
+
+int Win32Socket::GetError() const {
+ return error_;
+}
+
+void Win32Socket::SetError(int error) {
+ error_ = error;
+}
+
+Socket::ConnState Win32Socket::GetState() const {
+ return state_;
+}
+
+int Win32Socket::GetOption(Option opt, int* value) {
+ int slevel;
+ int sopt;
+ if (TranslateOption(opt, &slevel, &sopt) == -1)
+ return -1;
+
+ char* p = reinterpret_cast<char*>(value);
+ int optlen = sizeof(value);
+ return ::getsockopt(socket_, slevel, sopt, p, &optlen);
+}
+
+int Win32Socket::SetOption(Option opt, int value) {
+ int slevel;
+ int sopt;
+ if (TranslateOption(opt, &slevel, &sopt) == -1)
+ return -1;
+
+ const char* p = reinterpret_cast<const char*>(&value);
+ return ::setsockopt(socket_, slevel, sopt, p, sizeof(value));
+}
+
+int Win32Socket::Send(const void *pv, size_t cb) {
+ int sent = ::send(socket_, reinterpret_cast<const char*>(pv), cb, 0);
+ UpdateLastError();
+ return sent;
+}
+
+int Win32Socket::SendTo(const void *pv, size_t cb,
+ const SocketAddress& addr) {
+ sockaddr_in saddr;
+ addr.ToSockAddr(&saddr);
+ int sent = ::sendto(socket_, reinterpret_cast<const char*>(pv), cb, 0,
+ reinterpret_cast<sockaddr*>(&saddr), sizeof(saddr));
+ UpdateLastError();
+ return sent;
+}
+
+int Win32Socket::Recv(void *pv, size_t cb) {
+ int received = ::recv(socket_, static_cast<char*>(pv), cb, 0);
+ UpdateLastError();
+ if (closing_ && received <= static_cast<int>(cb))
+ PostClosed();
+ return received;
+}
+
+int Win32Socket::RecvFrom(void *pv, size_t cb,
+ SocketAddress *paddr) {
+ sockaddr_in saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ int received = ::recvfrom(socket_, static_cast<char*>(pv), cb, 0,
+ reinterpret_cast<sockaddr*>(&saddr), &cbAddr);
+ UpdateLastError();
+ if (received != SOCKET_ERROR)
+ paddr->FromSockAddr(saddr);
+ if (closing_ && received <= static_cast<int>(cb))
+ PostClosed();
+ return received;
+}
+
+int Win32Socket::Listen(int backlog) {
+ int err = ::listen(socket_, backlog);
+ if (!SetAsync(FD_ACCEPT))
+ return SOCKET_ERROR;
+
+ UpdateLastError();
+ if (err == 0)
+ state_ = CS_CONNECTING;
+ return err;
+}
+
+Win32Socket* Win32Socket::Accept(SocketAddress *paddr) {
+ sockaddr_in saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ SOCKET s = ::accept(socket_, reinterpret_cast<sockaddr*>(&saddr), &cbAddr);
+ UpdateLastError();
+ if (s == INVALID_SOCKET)
+ return NULL;
+ if (paddr)
+ paddr->FromSockAddr(saddr);
+ Win32Socket* socket = new Win32Socket;
+ if (0 == socket->Attach(s))
+ return socket;
+ delete socket;
+ return NULL;
+}
+
+int Win32Socket::Close() {
+ int err = 0;
+ if (socket_ != INVALID_SOCKET) {
+ err = ::closesocket(socket_);
+ socket_ = INVALID_SOCKET;
+ closing_ = false;
+ close_error_ = 0;
+ UpdateLastError();
+ }
+ if (dns_) {
+ WSACancelAsyncRequest(dns_->handle);
+ delete dns_;
+ dns_ = NULL;
+ }
+ if (sink_) {
+ sink_->Dispose();
+ sink_ = NULL;
+ }
+ addr_.Clear();
+ state_ = CS_CLOSED;
+ return err;
+}
+
+int Win32Socket::EstimateMTU(uint16* mtu) {
+ SocketAddress addr = GetRemoteAddress();
+ if (addr.IsAny()) {
+ error_ = ENOTCONN;
+ return -1;
+ }
+
+ 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;
+ WinPing::PingResult result = ping.Ping(addr.ip(), size, 0, 1, false);
+ if (result == WinPing::PING_FAIL) {
+ error_ = EINVAL; // can't think of a better error ID
+ return -1;
+ }
+ if (result != WinPing::PING_TOO_LARGE) {
+ *mtu = PACKET_MAXIMUMS[level];
+ return 0;
+ }
+ }
+
+ ASSERT(false);
+ return 0;
+}
+
+bool Win32Socket::SetAsync(int events) {
+ ASSERT(NULL == sink_);
+
+ // Create window
+ sink_ = new EventSink(this);
+ sink_->Create(NULL, L"EventSink", 0, 0, 0, 0, 10, 10);
+
+ // start the async select
+ if (WSAAsyncSelect(socket_, sink_->handle(), WM_SOCKETNOTIFY, events)
+ == SOCKET_ERROR) {
+ UpdateLastError();
+ Close();
+ return false;
+ }
+
+ return true;
+}
+
+bool Win32Socket::HandleClosed(int close_error) {
+ // WM_CLOSE will be received before all data has been read, so we need to
+ // hold on to it until the read buffer has been drained.
+ char ch;
+ closing_ = true;
+ close_error_ = close_error;
+ return (::recv(socket_, &ch, 1, MSG_PEEK) <= 0);
+}
+
+void Win32Socket::PostClosed() {
+ // If we see that the buffer is indeed drained, then send the close.
+ closing_ = false;
+ ::PostMessage(sink_->handle(), WM_SOCKETNOTIFY,
+ socket_, WSAMAKESELECTREPLY(FD_CLOSE, close_error_));
+}
+
+void Win32Socket::UpdateLastError() {
+ error_ = WSAGetLastError();
+}
+
+int Win32Socket::TranslateOption(Option opt, int* slevel, int* sopt) {
+ switch (opt) {
+ case OPT_DONTFRAGMENT:
+ *slevel = IPPROTO_IP;
+ *sopt = IP_DONTFRAGMENT;
+ break;
+ case OPT_RCVBUF:
+ *slevel = SOL_SOCKET;
+ *sopt = SO_RCVBUF;
+ break;
+ case OPT_SNDBUF:
+ *slevel = SOL_SOCKET;
+ *sopt = SO_SNDBUF;
+ break;
+ case OPT_NODELAY:
+ *slevel = IPPROTO_TCP;
+ *sopt = TCP_NODELAY;
+ break;
+ default:
+ ASSERT(false);
+ return -1;
+ }
+ return 0;
+}
+
+void Win32Socket::OnSocketNotify(SOCKET socket, int event, int error) {
+ // Ignore events if we're already closed.
+ if (socket != socket_)
+ return;
+
+ error_ = error;
+ switch (event) {
+ case FD_CONNECT:
+ if (error != ERROR_SUCCESS) {
+ ReportWSAError("WSAAsync:connect notify", error, addr_);
+#ifdef _DEBUG
+ int32 duration = TimeSince(connect_time_);
+ LOG(LS_INFO) << "WSAAsync:connect error (" << duration
+ << " ms), faking close";
+#endif
+ state_ = CS_CLOSED;
+ // If you get an error connecting, close doesn't really do anything
+ // and it certainly doesn't send back any close notification, but
+ // we really only maintain a few states, so it is easiest to get
+ // back into a known state by pretending that a close happened, even
+ // though the connect event never did occur.
+ SignalCloseEvent(this, error);
+ } else {
+#ifdef _DEBUG
+ int32 duration = TimeSince(connect_time_);
+ LOG(LS_INFO) << "WSAAsync:connect (" << duration << " ms)";
+#endif
+ state_ = CS_CONNECTED;
+ SignalConnectEvent(this);
+ }
+ break;
+
+ case FD_ACCEPT:
+ case FD_READ:
+ if (error != ERROR_SUCCESS) {
+ ReportWSAError("WSAAsync:read notify", error, addr_);
+ } else {
+ SignalReadEvent(this);
+ }
+ break;
+
+ case FD_WRITE:
+ if (error != ERROR_SUCCESS) {
+ ReportWSAError("WSAAsync:write notify", error, addr_);
+ } else {
+ SignalWriteEvent(this);
+ }
+ break;
+
+ case FD_CLOSE:
+ if (HandleClosed(error)) {
+ ReportWSAError("WSAAsync:close notify", error, addr_);
+ state_ = CS_CLOSED;
+ SignalCloseEvent(this, error);
+ }
+ break;
+ }
+}
+
+void Win32Socket::OnDnsNotify(HANDLE task, int error) {
+ if (!dns_ || dns_->handle != task)
+ return;
+
+ uint32 ip = 0;
+ if (error == 0) {
+ hostent* pHost = reinterpret_cast<hostent*>(dns_->buffer);
+ uint32 net_ip = *reinterpret_cast<uint32*>(pHost->h_addr_list[0]);
+ ip = NetworkToHost32(net_ip);
+ }
+
+ LOG_F(LS_INFO) << "(" << SocketAddress::IPToString(ip)
+ << ", " << error << ")";
+
+ if (error == 0) {
+ SocketAddress address(ip, dns_->port);
+ error = DoConnect(address);
+ } else {
+ Close();
+ }
+
+ if (error) {
+ error_ = error;
+ SignalCloseEvent(this, error_);
+ } else {
+ delete dns_;
+ dns_ = NULL;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32SocketServer
+// Provides cricket base services on top of a win32 gui thread
+///////////////////////////////////////////////////////////////////////////////
+
+static UINT s_wm_wakeup_id = 0;
+const TCHAR Win32SocketServer::kWindowName[] = L"libjingle Message Window";
+
+Win32SocketServer::Win32SocketServer(MessageQueue *message_queue)
+ : message_queue_(message_queue), wnd_(this), posted_(false) {
+ if (s_wm_wakeup_id == 0)
+ s_wm_wakeup_id = RegisterWindowMessage(L"WM_WAKEUP");
+ if (!wnd_.Create(NULL, kWindowName, 0, 0, 0, 0, 0, 0)) {
+ LOG_GLE(LS_ERROR) << "Failed to create message window.";
+ }
+}
+
+Win32SocketServer::~Win32SocketServer() {
+ if (wnd_.handle() != NULL) {
+ KillTimer(wnd_.handle(), 1);
+ wnd_.Destroy();
+ }
+}
+
+Socket* Win32SocketServer::CreateSocket(int type) {
+ return CreateAsyncSocket(type);
+}
+
+AsyncSocket* Win32SocketServer::CreateAsyncSocket(int type) {
+ Win32Socket* socket = new Win32Socket;
+ if (socket->CreateT(type)) {
+ return socket;
+ }
+ delete socket;
+ return NULL;
+}
+
+void Win32SocketServer::SetMessageQueue(MessageQueue* queue) {
+ message_queue_ = queue;
+}
+
+bool Win32SocketServer::Wait(int cms, bool process_io) {
+ BOOL b;
+ if (process_io) {
+ // Spin the Win32 message pump at least once, and as long as requested.
+ // This is the Thread::ProcessMessages case.
+ uint32 start = Time();
+ MSG msg;
+ do {
+ SetTimer(wnd_.handle(), 0, cms, NULL);
+ b = GetMessage(&msg, NULL, 0, 0);
+ if (b) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ KillTimer(wnd_.handle(), 0);
+ } while (b && TimeSince(start) < cms);
+ } else if (cms != 0) {
+ // Sit and wait forever for a WakeUp. This is the Thread::Send case.
+ ASSERT(cms == -1);
+ MSG msg;
+ b = GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id);
+ {
+ CritScope scope(&cs_);
+ posted_ = false;
+ }
+ } else {
+ // No-op (cms == 0 && !process_io). This is the Pump case.
+ b = TRUE;
+ }
+ return (b != FALSE);
+}
+
+void Win32SocketServer::WakeUp() {
+ if (wnd_.handle()) {
+ // Set the "message pending" flag, if not already set.
+ {
+ CritScope scope(&cs_);
+ if (posted_)
+ return;
+ posted_ = true;
+ }
+
+ PostMessage(wnd_.handle(), s_wm_wakeup_id, 0, 0);
+ }
+}
+
+void Win32SocketServer::Pump() {
+ // Clear the "message pending" flag.
+ {
+ CritScope scope(&cs_);
+ posted_ = false;
+ }
+
+ // Dispatch all the messages that are currently in our queue. If new messages
+ // are posted during the dispatch, they will be handled in the next Pump.
+ // We use max(1, ...) to make sure we try to dispatch at least once, since
+ // this allow us to process "sent" messages, not included in the size() count.
+ Message msg;
+ for (size_t max_messages_to_process = _max<size_t>(1, message_queue_->size());
+ max_messages_to_process > 0 && message_queue_->Get(&msg, 0, false);
+ --max_messages_to_process) {
+ message_queue_->Dispatch(&msg);
+ }
+
+ // Anything remaining?
+ int delay = message_queue_->GetDelay();
+ if (delay == -1) {
+ KillTimer(wnd_.handle(), 1);
+ } else {
+ SetTimer(wnd_.handle(), 1, delay, NULL);
+ }
+}
+
+bool Win32SocketServer::MessageWindow::OnMessage(UINT wm, WPARAM wp,
+ LPARAM lp, LRESULT& lr) {
+ bool handled = false;
+ if (wm == s_wm_wakeup_id || (wm == WM_TIMER && wp == 1)) {
+ ss_->Pump();
+ lr = 0;
+ handled = true;
+ }
+ return handled;
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/win32socketserver.h b/third_party/libjingle/source/talk/base/win32socketserver.h
new file mode 100644
index 0000000..f14a197
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32socketserver.h
@@ -0,0 +1,174 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_BASE_WIN32SOCKETSERVER_H_
+#define TALK_BASE_WIN32SOCKETSERVER_H_
+
+#ifdef WIN32
+#include "talk/base/asyncsocket.h"
+#include "talk/base/criticalsection.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/socketfactory.h"
+#include "talk/base/socket.h"
+#include "talk/base/thread.h"
+#include "talk/base/win32window.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32Socket : public AsyncSocket {
+ public:
+ Win32Socket();
+ virtual ~Win32Socket();
+
+ bool CreateT(int type);
+
+ int Attach(SOCKET s);
+ void SetTimeout(int ms);
+
+ // AsyncSocket Interface
+ 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 Recv(void *pv, size_t cb);
+ virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr);
+ virtual int Listen(int backlog);
+ virtual Win32Socket *Accept(SocketAddress *paddr);
+ virtual int Close();
+ virtual int GetError() const;
+ virtual void SetError(int error);
+ virtual ConnState GetState() const;
+ virtual int EstimateMTU(uint16* mtu);
+ virtual int GetOption(Option opt, int* value);
+ virtual int SetOption(Option opt, int value);
+
+ private:
+ bool SetAsync(int events);
+ int DoConnect(const SocketAddress& addr);
+ bool HandleClosed(int close_error);
+ void PostClosed();
+ void UpdateLastError();
+ static int TranslateOption(Option opt, int* slevel, int* sopt);
+
+ void OnSocketNotify(SOCKET socket, int event, int error);
+ void OnDnsNotify(HANDLE task, int error);
+
+ SOCKET socket_;
+ int error_;
+ ConnState state_;
+ SocketAddress addr_; // address that we connected to (see DoConnect)
+ uint32 connect_time_;
+ bool closing_;
+ int close_error_;
+
+ class EventSink;
+ friend class EventSink;
+ EventSink * sink_;
+
+ struct DnsLookup;
+ DnsLookup * dns_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32SocketServer
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32SocketServer : public SocketServer {
+ public:
+ explicit Win32SocketServer(MessageQueue *message_queue);
+ virtual ~Win32SocketServer();
+
+ // SocketServer Interface
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+ virtual void SetMessageQueue(MessageQueue* queue);
+ virtual bool Wait(int cms, bool process_io);
+ virtual void WakeUp();
+
+ void Pump();
+
+ HWND handle() { return wnd_.handle(); }
+
+ private:
+ class MessageWindow : public Win32Window {
+ public:
+ explicit MessageWindow(Win32SocketServer* ss) : ss_(ss) {}
+ private:
+ virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result);
+ Win32SocketServer* ss_;
+ };
+
+ static const TCHAR kWindowName[];
+ MessageQueue *message_queue_;
+ MessageWindow wnd_;
+ CriticalSection cs_;
+ bool posted_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Thread. Automatically pumps Windows messages.
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32Thread : public Thread {
+ public:
+ Win32Thread() : ss_(this), id_(0) {
+ set_socketserver(&ss_);
+ }
+ virtual ~Win32Thread() {
+ set_socketserver(NULL);
+ }
+ virtual void Run() {
+ MSG msg;
+ id_ = GetCurrentThreadId();
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ id_ = 0;
+ }
+ virtual void Quit() {
+ PostThreadMessage(id_, WM_QUIT, 0, 0);
+ }
+ private:
+ Win32SocketServer ss_;
+ DWORD id_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // WIN32
+
+#endif // TALK_BASE_WIN32SOCKETSERVER_H_
diff --git a/third_party/libjingle/source/talk/base/win32window.cc b/third_party/libjingle/source/talk/base/win32window.cc
new file mode 100644
index 0000000..0e7761f
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32window.cc
@@ -0,0 +1,134 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/base/win32window.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Window
+///////////////////////////////////////////////////////////////////////////////
+
+static const wchar_t kWindowBaseClassName[] = L"WindowBaseClass";
+HINSTANCE Win32Window::instance_ = GetModuleHandle(NULL);
+ATOM Win32Window::window_class_ = 0;
+
+Win32Window::Win32Window() : wnd_(NULL) {
+}
+
+Win32Window::~Win32Window() {
+ ASSERT(NULL == wnd_);
+}
+
+bool Win32Window::Create(HWND parent, const wchar_t* title, DWORD style,
+ DWORD exstyle, int x, int y, int cx, int cy) {
+ if (wnd_) {
+ // Window already exists.
+ return false;
+ }
+
+ if (!window_class_) {
+ // Class not registered, register it.
+ WNDCLASSEX wcex;
+ memset(&wcex, 0, sizeof(wcex));
+ wcex.cbSize = sizeof(wcex);
+ wcex.hInstance = instance_;
+ wcex.lpfnWndProc = &Win32Window::WndProc;
+ wcex.lpszClassName = kWindowBaseClassName;
+ window_class_ = ::RegisterClassEx(&wcex);
+ if (!window_class_) {
+ LOG_GLE(LS_ERROR) << "RegisterClassEx failed";
+ return false;
+ }
+ }
+ wnd_ = ::CreateWindowEx(exstyle, kWindowBaseClassName, title, style,
+ x, y, cx, cy, parent, NULL, instance_, this);
+ return (NULL != wnd_);
+}
+
+void Win32Window::Destroy() {
+ VERIFY(::DestroyWindow(wnd_) != FALSE);
+}
+
+void Win32Window::SetInstance(HINSTANCE instance) {
+ instance_ = instance;
+}
+
+void Win32Window::Shutdown() {
+ if (window_class_) {
+ ::UnregisterClass(MAKEINTATOM(window_class_), instance_);
+ window_class_ = 0;
+ }
+}
+
+bool Win32Window::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result) {
+ switch (uMsg) {
+ case WM_CLOSE:
+ if (!OnClose()) {
+ result = 0;
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+LRESULT Win32Window::WndProc(HWND hwnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam) {
+ Win32Window* that = reinterpret_cast<Win32Window*>(
+ ::GetWindowLongPtr(hwnd, GWL_USERDATA));
+ if (!that && (WM_CREATE == uMsg)) {
+ CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
+ that = static_cast<Win32Window*>(cs->lpCreateParams);
+ that->wnd_ = hwnd;
+ ::SetWindowLongPtr(hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(that));
+ }
+ if (that) {
+ LRESULT result;
+ bool handled = that->OnMessage(uMsg, wParam, lParam, result);
+ if (WM_DESTROY == uMsg) {
+ for (HWND child = ::GetWindow(hwnd, GW_CHILD); child;
+ child = ::GetWindow(child, GW_HWNDNEXT)) {
+ LOG(LS_INFO) << "Child window: " << static_cast<void*>(child);
+ }
+ }
+ if (WM_NCDESTROY == uMsg) {
+ ::SetWindowLongPtr(hwnd, GWL_USERDATA, NULL);
+ that->wnd_ = NULL;
+ that->OnDestroyed();
+ }
+ if (handled) {
+ return result;
+ }
+ }
+ return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/win32window.h b/third_party/libjingle/source/talk/base/win32window.h
new file mode 100644
index 0000000..66a56ce
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/win32window.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 TALK_BASE_WIN32WINDOW_H_
+#define TALK_BASE_WIN32WINDOW_H_
+
+#ifdef WIN32
+
+#include "talk/base/win32.h"
+
+namespace talk_base {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Window
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32Window {
+ public:
+ Win32Window();
+ virtual ~Win32Window();
+
+ HWND handle() const { return wnd_; }
+
+ bool Create(HWND parent, const wchar_t* title, DWORD style, DWORD exstyle,
+ int x, int y, int cx, int cy);
+ void Destroy();
+
+ // Call this first if you are running inside a DLL.
+ static void SetInstance(HINSTANCE instance);
+ // Call this when your DLL unloads.
+ static void Shutdown();
+
+ protected:
+ virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result);
+
+ virtual bool OnClose() { return true; }
+ virtual void OnDestroyed() { }
+
+ private:
+ static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam);
+
+ HWND wnd_;
+ static HINSTANCE instance_;
+ static ATOM window_class_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // WIN32
+
+#endif // TALK_BASE_WIN32WINDOW_H_
diff --git a/third_party/libjingle/source/talk/base/winfirewall.cc b/third_party/libjingle/source/talk/base/winfirewall.cc
new file mode 100644
index 0000000..e87ee5a
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/winfirewall.cc
@@ -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.
+ */
+
+#include "talk/base/winfirewall.h"
+
+#include "talk/base/win32.h"
+
+#include <comdef.h>
+#include <netfw.h>
+
+#define RELEASE(lpUnk) do { \
+ if ((lpUnk) != NULL) { \
+ (lpUnk)->Release(); \
+ (lpUnk) = NULL; \
+ } \
+} while (0)
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// WinFirewall
+//////////////////////////////////////////////////////////////////////
+
+WinFirewall::WinFirewall() : mgr_(NULL), policy_(NULL), profile_(NULL) {
+}
+
+WinFirewall::~WinFirewall() {
+ Shutdown();
+}
+
+bool WinFirewall::Initialize(HRESULT* result) {
+ if (mgr_) {
+ if (result) {
+ *result = S_OK;
+ }
+ return true;
+ }
+
+ HRESULT hr = CoCreateInstance(__uuidof(NetFwMgr),
+ 0, CLSCTX_INPROC_SERVER,
+ __uuidof(INetFwMgr),
+ reinterpret_cast<void **>(&mgr_));
+ if (SUCCEEDED(hr) && (mgr_ != NULL))
+ hr = mgr_->get_LocalPolicy(&policy_);
+ if (SUCCEEDED(hr) && (policy_ != NULL))
+ hr = policy_->get_CurrentProfile(&profile_);
+
+ if (result)
+ *result = hr;
+ return SUCCEEDED(hr) && (profile_ != NULL);
+}
+
+void WinFirewall::Shutdown() {
+ RELEASE(profile_);
+ RELEASE(policy_);
+ RELEASE(mgr_);
+}
+
+bool WinFirewall::Enabled() const {
+ if (!profile_)
+ return false;
+
+ VARIANT_BOOL fwEnabled = VARIANT_FALSE;
+ profile_->get_FirewallEnabled(&fwEnabled);
+ return (fwEnabled != VARIANT_FALSE);
+}
+
+bool WinFirewall::QueryAuthorized(const char* filename, bool* authorized)
+ const {
+ return QueryAuthorizedW(ToUtf16(filename).c_str(), authorized);
+}
+
+bool WinFirewall::QueryAuthorizedW(const wchar_t* filename, bool* authorized)
+ const {
+ *authorized = false;
+ bool success = false;
+
+ if (!profile_)
+ return false;
+
+ _bstr_t bfilename = filename;
+
+ INetFwAuthorizedApplications* apps = NULL;
+ HRESULT hr = profile_->get_AuthorizedApplications(&apps);
+ if (SUCCEEDED(hr) && (apps != NULL)) {
+ INetFwAuthorizedApplication* app = NULL;
+ hr = apps->Item(bfilename, &app);
+ if (SUCCEEDED(hr) && (app != NULL)) {
+ VARIANT_BOOL fwEnabled = VARIANT_FALSE;
+ hr = app->get_Enabled(&fwEnabled);
+ app->Release();
+
+ if (SUCCEEDED(hr)) {
+ success = true;
+ *authorized = (fwEnabled != VARIANT_FALSE);
+ }
+ } else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
+ // No entry in list of authorized apps
+ success = true;
+ } else {
+ // Unexpected error
+ }
+ apps->Release();
+ }
+
+ return success;
+}
+
+bool WinFirewall::AddApplication(const char* filename,
+ const char* friendly_name,
+ bool authorized,
+ HRESULT* result) {
+ return AddApplicationW(ToUtf16(filename).c_str(),
+ ToUtf16(friendly_name).c_str(), authorized, result);
+}
+
+bool WinFirewall::AddApplicationW(const wchar_t* filename,
+ const wchar_t* friendly_name,
+ bool authorized,
+ HRESULT* result) {
+ INetFwAuthorizedApplications* apps = NULL;
+ HRESULT hr = profile_->get_AuthorizedApplications(&apps);
+ if (SUCCEEDED(hr) && (apps != NULL)) {
+ INetFwAuthorizedApplication* app = NULL;
+ hr = CoCreateInstance(__uuidof(NetFwAuthorizedApplication),
+ 0, CLSCTX_INPROC_SERVER,
+ __uuidof(INetFwAuthorizedApplication),
+ reinterpret_cast<void **>(&app));
+ if (SUCCEEDED(hr) && (app != NULL)) {
+ _bstr_t bstr = filename;
+ hr = app->put_ProcessImageFileName(bstr);
+ bstr = friendly_name;
+ if (SUCCEEDED(hr))
+ hr = app->put_Name(bstr);
+ if (SUCCEEDED(hr))
+ hr = app->put_Enabled(authorized ? VARIANT_TRUE : VARIANT_FALSE);
+ if (SUCCEEDED(hr))
+ hr = apps->Add(app);
+ app->Release();
+ }
+ apps->Release();
+ }
+ if (result)
+ *result = hr;
+ return SUCCEEDED(hr);
+}
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/winfirewall.h b/third_party/libjingle/source/talk/base/winfirewall.h
new file mode 100644
index 0000000..11d687e
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/winfirewall.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 TALK_BASE_WINFIREWALL_H_
+#define TALK_BASE_WINFIREWALL_H_
+
+#ifndef _HRESULT_DEFINED
+#define _HRESULT_DEFINED
+typedef long HRESULT; // Can't forward declare typedef, but don't need all win
+#endif // !_HRESULT_DEFINED
+
+struct INetFwMgr;
+struct INetFwPolicy;
+struct INetFwProfile;
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// WinFirewall
+//////////////////////////////////////////////////////////////////////
+
+class WinFirewall {
+ public:
+ WinFirewall();
+ ~WinFirewall();
+
+ bool Initialize(HRESULT* result);
+ void Shutdown();
+
+ bool Enabled() const;
+ bool QueryAuthorized(const char* filename, bool* authorized) const;
+ bool QueryAuthorizedW(const wchar_t* filename, bool* authorized) const;
+
+ bool AddApplication(const char* filename, const char* friendly_name,
+ bool authorized, HRESULT* result);
+ bool AddApplicationW(const wchar_t* filename, const wchar_t* friendly_name,
+ bool authorized, HRESULT* result);
+
+ private:
+ INetFwMgr* mgr_;
+ INetFwPolicy* policy_;
+ INetFwProfile* profile_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
+
+#endif // TALK_BASE_WINFIREWALL_H_
diff --git a/third_party/libjingle/source/talk/base/winping.cc b/third_party/libjingle/source/talk/base/winping.cc
new file mode 100644
index 0000000..1802e03
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/winping.cc
@@ -0,0 +1,318 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/winping.h"
+#include "talk/base/logging.h"
+#include <cassert>
+
+namespace talk_base {
+
+//////////////////////////////////////////////////////////////////////
+// Found in IPExport.h
+//////////////////////////////////////////////////////////////////////
+
+typedef struct icmp_echo_reply {
+ ULONG Address; // Replying address
+ ULONG Status; // Reply IP_STATUS
+ ULONG RoundTripTime; // RTT in milliseconds
+ USHORT DataSize; // Reply data size in bytes
+ USHORT Reserved; // Reserved for system use
+ PVOID Data; // Pointer to the reply data
+ struct ip_option_information Options; // Reply options
+} ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY;
+
+//
+// IP_STATUS codes returned from IP APIs
+//
+
+#define IP_STATUS_BASE 11000
+
+#define IP_SUCCESS 0
+#define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
+#define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
+#define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
+#define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
+#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
+#define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
+#define IP_BAD_OPTION (IP_STATUS_BASE + 7)
+#define IP_HW_ERROR (IP_STATUS_BASE + 8)
+#define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
+#define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
+#define IP_BAD_REQ (IP_STATUS_BASE + 11)
+#define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
+#define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
+#define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
+#define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
+#define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
+#define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
+#define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
+
+#define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
+#define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
+#define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
+#define IP_UNLOAD (IP_STATUS_BASE + 22)
+#define IP_ADDR_ADDED (IP_STATUS_BASE + 23)
+#define IP_MEDIA_CONNECT (IP_STATUS_BASE + 24)
+#define IP_MEDIA_DISCONNECT (IP_STATUS_BASE + 25)
+#define IP_BIND_ADAPTER (IP_STATUS_BASE + 26)
+#define IP_UNBIND_ADAPTER (IP_STATUS_BASE + 27)
+#define IP_DEVICE_DOES_NOT_EXIST (IP_STATUS_BASE + 28)
+#define IP_DUPLICATE_ADDRESS (IP_STATUS_BASE + 29)
+#define IP_INTERFACE_METRIC_CHANGE (IP_STATUS_BASE + 30)
+#define IP_RECONFIG_SECFLTR (IP_STATUS_BASE + 31)
+#define IP_NEGOTIATING_IPSEC (IP_STATUS_BASE + 32)
+#define IP_INTERFACE_WOL_CAPABILITY_CHANGE (IP_STATUS_BASE + 33)
+#define IP_DUPLICATE_IPADD (IP_STATUS_BASE + 34)
+
+#define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
+#define MAX_IP_STATUS IP_GENERAL_FAILURE
+#define IP_PENDING (IP_STATUS_BASE + 255)
+
+//
+// Values used in the IP header Flags field.
+//
+#define IP_FLAG_DF 0x2 // Don't fragment this packet.
+
+//
+// Supported IP Option Types.
+//
+// These types define the options which may be used in the OptionsData field
+// of the ip_option_information structure. See RFC 791 for a complete
+// description of each.
+//
+#define IP_OPT_EOL 0 // End of list option
+#define IP_OPT_NOP 1 // No operation
+#define IP_OPT_SECURITY 0x82 // Security option
+#define IP_OPT_LSRR 0x83 // Loose source route
+#define IP_OPT_SSRR 0x89 // Strict source route
+#define IP_OPT_RR 0x7 // Record route
+#define IP_OPT_TS 0x44 // Timestamp
+#define IP_OPT_SID 0x88 // Stream ID (obsolete)
+#define IP_OPT_ROUTER_ALERT 0x94 // Router Alert Option
+
+#define MAX_OPT_SIZE 40 // Maximum length of IP options in bytes
+
+//////////////////////////////////////////////////////////////////////
+// Global Constants and Types
+//////////////////////////////////////////////////////////////////////
+
+const char * const ICMP_DLL_NAME = "icmp.dll";
+const char * const ICMP_CREATE_FUNC = "IcmpCreateFile";
+const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle";
+const char * const ICMP_SEND_FUNC = "IcmpSendEcho";
+
+inline uint32 ReplySize(uint32 data_size) {
+ // A ping error message is 8 bytes long, so make sure we allow for at least
+ // 8 bytes of reply data.
+ return sizeof(ICMP_ECHO_REPLY) + talk_base::_max<uint32>(8, data_size);
+}
+
+//////////////////////////////////////////////////////////////////////
+// WinPing
+//////////////////////////////////////////////////////////////////////
+
+WinPing::WinPing()
+ : dll_(0), hping_(INVALID_HANDLE_VALUE), create_(0), close_(0), send_(0),
+ data_(0), dlen_(0), reply_(0), rlen_(0), valid_(false) {
+
+ dll_ = LoadLibraryA(ICMP_DLL_NAME);
+ if (!dll_) {
+ LOG(LERROR) << "LoadLibrary: " << GetLastError();
+ return;
+ }
+
+ create_ = (PIcmpCreateFile) GetProcAddress(dll_, ICMP_CREATE_FUNC);
+ close_ = (PIcmpCloseHandle) GetProcAddress(dll_, ICMP_CLOSE_FUNC);
+ send_ = (PIcmpSendEcho) GetProcAddress(dll_, ICMP_SEND_FUNC);
+ if (!create_ || !close_ || !send_) {
+ LOG(LERROR) << "GetProcAddress(ICMP_*): " << GetLastError();
+ return;
+ }
+
+ hping_ = create_();
+ if (hping_ == INVALID_HANDLE_VALUE) {
+ LOG(LERROR) << "IcmpCreateFile: " << GetLastError();
+ return;
+ }
+
+ dlen_ = 0;
+ rlen_ = ReplySize(dlen_);
+ data_ = new char[dlen_];
+ reply_ = new char[rlen_];
+
+ valid_ = true;
+}
+
+WinPing::~WinPing() {
+ if (dll_)
+ FreeLibrary(dll_);
+
+ if ((hping_ != INVALID_HANDLE_VALUE) && close_) {
+ if (!close_(hping_))
+ LOG(WARNING) << "IcmpCloseHandle: " << GetLastError();
+ }
+
+ delete[] data_;
+ delete reply_;
+}
+
+WinPing::PingResult WinPing::Ping(
+ uint32 ip, uint32 data_size, uint32 timeout, uint8 ttl,
+ bool allow_fragments) {
+
+ assert(IsValid());
+
+ IP_OPTION_INFORMATION ipopt;
+ memset(&ipopt, 0, sizeof(ipopt));
+ if (!allow_fragments)
+ ipopt.Flags |= IP_FLAG_DF;
+ ipopt.Ttl = ttl;
+
+ uint32 reply_size = ReplySize(data_size);
+
+ if (data_size > dlen_) {
+ delete [] data_;
+ dlen_ = data_size;
+ data_ = new char[dlen_];
+ memset(data_, 'z', dlen_);
+ }
+
+ if (reply_size > rlen_) {
+ delete [] reply_;
+ rlen_ = reply_size;
+ reply_ = new char[rlen_];
+ }
+
+ DWORD result = send_(hping_, talk_base::HostToNetwork32(ip),
+ data_, uint16(data_size), &ipopt,
+ reply_, reply_size, timeout);
+ if (result == 0) {
+ long error = GetLastError();
+ if (error == IP_PACKET_TOO_BIG)
+ return PING_TOO_LARGE;
+ if (error == IP_REQ_TIMED_OUT)
+ return PING_TIMEOUT;
+ LOG(LERROR) << "IcmpSendEcho(" << talk_base::SocketAddress::IPToString(ip)
+ << ", " << data_size << "): " << error;
+ return PING_FAIL;
+ }
+
+ return PING_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Microsoft Documenation
+//////////////////////////////////////////////////////////////////////
+//
+// Routine Name:
+//
+// IcmpCreateFile
+//
+// Routine Description:
+//
+// Opens a handle on which ICMP Echo Requests can be issued.
+//
+// Arguments:
+//
+// None.
+//
+// Return Value:
+//
+// An open file handle or INVALID_HANDLE_VALUE. Extended error information
+// is available by calling GetLastError().
+//
+//////////////////////////////////////////////////////////////////////
+//
+// Routine Name:
+//
+// IcmpCloseHandle
+//
+// Routine Description:
+//
+// Closes a handle opened by ICMPOpenFile.
+//
+// Arguments:
+//
+// IcmpHandle - The handle to close.
+//
+// Return Value:
+//
+// TRUE if the handle was closed successfully, otherwise FALSE. Extended
+// error information is available by calling GetLastError().
+//
+//////////////////////////////////////////////////////////////////////
+//
+// Routine Name:
+//
+// IcmpSendEcho
+//
+// Routine Description:
+//
+// Sends an ICMP Echo request and returns any replies. The
+// call returns when the timeout has expired or the reply buffer
+// is filled.
+//
+// Arguments:
+//
+// IcmpHandle - An open handle returned by ICMPCreateFile.
+//
+// DestinationAddress - The destination of the echo request.
+//
+// RequestData - A buffer containing the data to send in the
+// request.
+//
+// RequestSize - The number of bytes in the request data buffer.
+//
+// RequestOptions - Pointer to the IP header options for the request.
+// May be NULL.
+//
+// ReplyBuffer - A buffer to hold any replies to the request.
+// On return, the buffer will contain an array of
+// ICMP_ECHO_REPLY structures followed by the
+// options and data for the replies. The buffer
+// should be large enough to hold at least one
+// ICMP_ECHO_REPLY structure plus
+// MAX(RequestSize, 8) bytes of data since an ICMP
+// error message contains 8 bytes of data.
+//
+// ReplySize - The size in bytes of the reply buffer.
+//
+// Timeout - The time in milliseconds to wait for replies.
+//
+// Return Value:
+//
+// Returns the number of ICMP_ECHO_REPLY structures stored in ReplyBuffer.
+// The status of each reply is contained in the structure. If the return
+// value is zero, extended error information is available via
+// GetLastError().
+//
+//////////////////////////////////////////////////////////////////////
+
+} // namespace talk_base
diff --git a/third_party/libjingle/source/talk/base/winping.h b/third_party/libjingle/source/talk/base/winping.h
new file mode 100644
index 0000000..35d36e3
--- /dev/null
+++ b/third_party/libjingle/source/talk/base/winping.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 TALK_BASE_WINPING_H__
+#define TALK_BASE_WINPING_H__
+
+#ifdef WIN32
+
+#include "talk/base/win32.h"
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+// 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_;
+};
+
+} // namespace talk_base
+
+#endif // WIN32
+
+#endif // TALK_BASE_WINPING_H__
+
diff --git a/third_party/libjingle/source/talk/examples/call/call_main.cc b/third_party/libjingle/source/talk/examples/call/call_main.cc
new file mode 100644
index 0000000..cb42819
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/call_main.cc
@@ -0,0 +1,290 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <time.h>
+#include <iomanip>
+#include <cstdio>
+#include <cstring>
+#include <vector>
+#include "talk/base/logging.h"
+#include "talk/base/flags.h"
+#include "talk/base/ssladapter.h"
+#include "talk/base/win32socketserver.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/examples/login/xmppthread.h"
+#include "talk/examples/login/xmppauth.h"
+#include "talk/examples/login/xmpppump.h"
+#include "talk/examples/call/callclient.h"
+#include "talk/examples/call/console.h"
+
+class DebugLog : public sigslot::has_slots<> {
+ public:
+ DebugLog() :
+ debug_input_buf_(NULL), debug_input_len_(0), debug_input_alloc_(0),
+ debug_output_buf_(NULL), debug_output_len_(0), debug_output_alloc_(0),
+ censor_password_(false)
+ {}
+ char * debug_input_buf_;
+ int debug_input_len_;
+ int debug_input_alloc_;
+ char * debug_output_buf_;
+ int debug_output_len_;
+ int debug_output_alloc_;
+ bool censor_password_;
+
+ void Input(const char * data, int len) {
+ if (debug_input_len_ + len > debug_input_alloc_) {
+ char * old_buf = debug_input_buf_;
+ debug_input_alloc_ = 4096;
+ while (debug_input_alloc_ < debug_input_len_ + len) {
+ debug_input_alloc_ *= 2;
+ }
+ debug_input_buf_ = new char[debug_input_alloc_];
+ memcpy(debug_input_buf_, old_buf, debug_input_len_);
+ delete[] old_buf;
+ }
+ memcpy(debug_input_buf_ + debug_input_len_, data, len);
+ debug_input_len_ += len;
+ DebugPrint(debug_input_buf_, &debug_input_len_, false);
+ }
+
+ void Output(const char * data, int len) {
+ if (debug_output_len_ + len > debug_output_alloc_) {
+ char * old_buf = debug_output_buf_;
+ debug_output_alloc_ = 4096;
+ while (debug_output_alloc_ < debug_output_len_ + len) {
+ debug_output_alloc_ *= 2;
+ }
+ debug_output_buf_ = new char[debug_output_alloc_];
+ memcpy(debug_output_buf_, old_buf, debug_output_len_);
+ delete[] old_buf;
+ }
+ memcpy(debug_output_buf_ + debug_output_len_, data, len);
+ debug_output_len_ += len;
+ DebugPrint(debug_output_buf_, &debug_output_len_, true);
+ }
+
+ static bool IsAuthTag(const char * str, size_t len) {
+ if (str[0] == '<' && str[1] == 'a' &&
+ str[2] == 'u' &&
+ str[3] == 't' &&
+ str[4] == 'h' &&
+ str[5] <= ' ') {
+ std::string tag(str, len);
+
+ if (tag.find("mechanism") != std::string::npos)
+ return true;
+ }
+ return false;
+ }
+
+ void DebugPrint(char * buf, int * plen, bool output) {
+ int len = *plen;
+ if (len > 0) {
+ time_t tim = time(NULL);
+ struct tm * now = localtime(&tim);
+ char *time_string = asctime(now);
+ if (time_string) {
+ size_t time_len = strlen(time_string);
+ if (time_len > 0) {
+ time_string[time_len-1] = 0; // trim off terminating \n
+ }
+ }
+ LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>>>>>>>>>>" : "RECV <<<<<<<<<<<<<<<<<<<<<<<<<")
+ << " : " << time_string;
+
+ bool indent;
+ int start = 0, nest = 3;
+ for (int i = 0; i < len; i += 1) {
+ if (buf[i] == '>') {
+ if ((i > 0) && (buf[i-1] == '/')) {
+ indent = false;
+ } else if ((start + 1 < len) && (buf[start + 1] == '/')) {
+ indent = false;
+ nest -= 2;
+ } else {
+ indent = true;
+ }
+
+ // Output a tag
+ LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i + 1 - start);
+
+ if (indent)
+ nest += 2;
+
+ // Note if it's a PLAIN auth tag
+ if (IsAuthTag(buf + start, i + 1 - start)) {
+ censor_password_ = true;
+ }
+
+ // incr
+ start = i + 1;
+ }
+
+ if (buf[i] == '<' && start < i) {
+ if (censor_password_) {
+ LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##";
+ censor_password_ = false;
+ } else {
+ LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i - start);
+ }
+ start = i;
+ }
+ }
+ len = len - start;
+ memcpy(buf, buf + start, len);
+ *plen = len;
+ }
+ }
+};
+
+static DebugLog debug_log_;
+static const int DEFAULT_PORT = 5222;
+
+
+int main(int argc, char **argv) {
+ // This app has three threads. The main thread will run the XMPP client,
+ // which will print to the screen in its own thread. A second thread
+ // will get input from the console, parse it, and pass the appropriate
+ // message back to the XMPP client's thread. A third thread is used
+ // by MediaSessionClient as its worker thread.
+
+ // define options
+ DEFINE_bool(a, false, "Turn on auto accept.");
+ DEFINE_bool(d, false, "Turn on debugging.");
+ DEFINE_bool(filemedia, false, "Use File media");
+ DEFINE_bool(testserver, false, "Use test server");
+ DEFINE_int(portallocator, 0, "Filter out unwanted connection types.");
+ DEFINE_string(filterhost, NULL, "Filter out the host from all candidates.");
+ DEFINE_string(pmuc, "groupchat.google.com", "The persistant muc domain.");
+ DEFINE_string(s, "talk.google.com", "The connection server to use.");
+
+ // parse options
+ FlagList::SetFlagsFromCommandLine(&argc, argv, true);
+ bool auto_accept = FLAG_a;
+ bool debug = FLAG_d;
+ bool test_server = FLAG_testserver;
+ int32 portallocator_flags = FLAG_portallocator;
+ std::string pmuc_domain = FLAG_pmuc;
+ std::string server = FLAG_s;
+
+ // parse username and password, if present
+ buzz::Jid jid;
+ std::string username;
+ talk_base::InsecureCryptStringImpl pass;
+ if (argc > 1) {
+ username = argv[1];
+ if (argc > 2) {
+ pass.password() = argv[2];
+ }
+ }
+
+ if (debug)
+ talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE);
+
+ if (username.empty()) {
+ std::cout << "JID: ";
+ std::cin >> username;
+ }
+ if (username.find('@') == std::string::npos) {
+ username.append("@localhost");
+ }
+ jid = buzz::Jid(username);
+ if (!jid.IsValid() || jid.node() == "") {
+ printf("Invalid JID. JIDs should be in the form user@domain\n");
+ return 1;
+ }
+ if (pass.password().empty() && !test_server) {
+ Console::SetEcho(false);
+ std::cout << "Password: ";
+ std::cin >> pass.password();
+ Console::SetEcho(true);
+ std::cout << std::endl;
+ }
+
+ buzz::XmppClientSettings xcs;
+ xcs.set_user(jid.node());
+ xcs.set_resource("call");
+ xcs.set_host(jid.domain());
+ xcs.set_use_tls(!test_server);
+
+ if (test_server) {
+ pass.password() = jid.node();
+ xcs.set_allow_plain(true);
+ }
+ xcs.set_pass(talk_base::CryptString(pass));
+
+ std::string host;
+ int port;
+
+ int colon = server.find(':');
+ if (colon == -1) {
+ host = server;
+ port = DEFAULT_PORT;
+ } else {
+ host = server.substr(0, colon);
+ port = atoi(server.substr(colon + 1).c_str());
+ }
+
+ xcs.set_server(talk_base::SocketAddress(host, port));
+ printf("Logging in to %s as %s\n", server.c_str(), jid.Str().c_str());
+
+ talk_base::InitializeSSL();
+
+
+#if WIN32
+ // Need to pump messages on our main thread on Windows.
+ talk_base::Win32Thread w32_thread;
+ talk_base::ThreadManager::SetCurrent(&w32_thread);
+#endif
+ talk_base::Thread* main_thread = talk_base::Thread::Current();
+
+ XmppPump pump;
+ CallClient *client = new CallClient(pump.client());
+
+ Console *console = new Console(main_thread, client);
+ client->SetConsole(console);
+ client->SetAutoAccept(auto_accept);
+ client->SetPmucDomain(pmuc_domain);
+ client->SetPortAllocatorFlags(portallocator_flags);
+ console->Start();
+
+ if (debug) {
+ pump.client()->SignalLogInput.connect(&debug_log_, &DebugLog::Input);
+ pump.client()->SignalLogOutput.connect(&debug_log_, &DebugLog::Output);
+ }
+
+ pump.DoLogin(xcs, new XmppSocket(true), NULL);
+ main_thread->Run();
+ pump.DoDisconnect();
+
+ console->Stop();
+ delete console;
+ delete client;
+
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/examples/call/callclient.cc b/third_party/libjingle/source/talk/examples/call/callclient.cc
new file mode 100644
index 0000000..5eeb9cb
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/callclient.cc
@@ -0,0 +1,857 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/call/callclient.h"
+
+#include <string>
+
+#include "talk/xmpp/constants.h"
+#include "talk/base/helpers.h"
+#include "talk/base/thread.h"
+#include "talk/base/network.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/client/sessionmanagertask.h"
+#include "talk/session/phone/devicemanager.h"
+#include "talk/session/phone/mediaengine.h"
+#include "talk/session/phone/mediasessionclient.h"
+#include "talk/examples/call/console.h"
+#include "talk/examples/call/presencepushtask.h"
+#include "talk/examples/call/presenceouttask.h"
+#include "talk/examples/call/mucinviterecvtask.h"
+#include "talk/examples/call/mucinvitesendtask.h"
+#include "talk/examples/call/friendinvitesendtask.h"
+#include "talk/examples/call/muc.h"
+#include "talk/examples/call/voicemailjidrequester.h"
+#ifdef USE_TALK_SOUND
+#include "talk/sound/platformsoundsystemfactory.h"
+#endif
+
+#include "talk/base/logging.h"
+
+class NullRenderer : public cricket::VideoRenderer {
+ public:
+ explicit NullRenderer(const char* s) : s_(s) {}
+ private:
+ bool SetSize(int width, int height, int reserved) {
+ LOG(LS_INFO) << "Video size for " << s_ << ": " << width << "x" << height;
+ return true;
+ }
+ bool RenderFrame(const cricket::VideoFrame *frame) {
+ return true;
+ }
+ const char* s_;
+};
+
+namespace {
+
+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";
+ default: return "offline";
+ }
+}
+
+} // 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"
+" dtmf Sends a DTMF tone.\n"
+" quit Quits the application.\n"
+"";
+
+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"
+" quit Quits the application.\n"
+"";
+
+const char* CONSOLE_COMMANDS =
+"Available commands:\n"
+"\n"
+" roster Prints the online friends from your roster.\n"
+" friend user Request to add a user to your roster.\n"
+" call [jid] Initiates a call to the user[/room] with the\n"
+" given JID.\n"
+" vcall [jid] Initiates a video call to the user[/room] with\n"
+" the given JID.\n"
+" voicemail [jid] Leave a voicemail for the user with the given JID.\n"
+" join [room] Joins a multi-user-chat.\n"
+" invite user [room] Invites a friend to a multi-user-chat.\n"
+" leave [room] Leaves a multi-user-chat.\n"
+" getdevs Prints the available media devices.\n"
+" quit Quits the application.\n"
+"";
+
+void CallClient::ParseLine(const std::string& line) {
+ std::vector<std::string> words;
+ 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;
+ }
+ }
+ }
+
+ // Global commands
+ if ((words.size() == 1) && (words[0] == "quit")) {
+ Quit();
+ } else if (call_ && incoming_call_) {
+ if ((words.size() == 1) && (words[0] == "accept")) {
+ Accept();
+ } else if ((words.size() == 1) && (words[0] == "reject")) {
+ Reject();
+ } else {
+ console_->Print(RECEIVE_COMMANDS);
+ }
+ } else if (call_) {
+ if ((words.size() == 1) && (words[0] == "hangup")) {
+ // TODO(juberti): do more shutdown here, move to Terminate()
+ call_->Terminate();
+ call_ = NULL;
+ session_ = NULL;
+ console_->SetPrompt(NULL);
+ } else if ((words.size() == 1) && (words[0] == "mute")) {
+ call_->Mute(true);
+ } else if ((words.size() == 1) && (words[0] == "unmute")) {
+ call_->Mute(false);
+ } else if ((words.size() == 2) && (words[0] == "dtmf")) {
+ int ev = std::string("0123456789*#").find(words[1][0]);
+ call_->PressDTMF(ev);
+ } else {
+ console_->Print(CALL_COMMANDS);
+ }
+ } else {
+ if ((words.size() == 1) && (words[0] == "roster")) {
+ PrintRoster();
+ } else if ((words.size() == 2) && (words[0] == "friend")) {
+ InviteFriend(words[1]);
+ } else if ((words.size() >= 1) && (words[0] == "call")) {
+ MakeCallTo((words.size() >= 2) ? words[1] : "", false);
+ } else if ((words.size() >= 1) && (words[0] == "vcall")) {
+ MakeCallTo((words.size() >= 2) ? words[1] : "", true);
+ } else if ((words.size() >= 1) && (words[0] == "join")) {
+ JoinMuc((words.size() >= 2) ? words[1] : "");
+ } else if ((words.size() >= 2) && (words[0] == "invite")) {
+ InviteToMuc(words[1], (words.size() >= 3) ? words[2] : "");
+ } else if ((words.size() >= 1) && (words[0] == "leave")) {
+ LeaveMuc((words.size() >= 2) ? words[1] : "");
+ } else if ((words.size() == 1) && (words[0] == "getdevs")) {
+ GetDevices();
+ } else if ((words.size() == 2) && (words[0] == "setvol")) {
+ SetVolume(words[1]);
+ } else if ((words.size() >= 1) && (words[0] == "voicemail")) {
+ CallVoicemail((words.size() >= 2) ? words[1] : "");
+ } else {
+ console_->Print(CONSOLE_COMMANDS);
+ }
+ }
+}
+
+CallClient::CallClient(buzz::XmppClient* xmpp_client)
+ : xmpp_client_(xmpp_client), media_engine_(NULL), media_client_(NULL),
+ call_(NULL), incoming_call_(false),
+ auto_accept_(false), pmuc_domain_("groupchat.google.com"),
+ local_renderer_(NULL), remote_renderer_(NULL),
+ roster_(new RosterMap), portallocator_flags_(0)
+#ifdef USE_TALK_SOUND
+ , sound_system_factory_(NULL)
+#endif
+ {
+ xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange);
+}
+
+CallClient::~CallClient() {
+ delete media_client_;
+ 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 (Check your username and password)";
+ 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";
+ default:
+ return "Unknown error";
+ }
+}
+
+void CallClient::OnCallDestroy(cricket::Call* call) {
+ if (call == call_) {
+ if (remote_renderer_) {
+ delete remote_renderer_;
+ remote_renderer_ = NULL;
+ }
+ if (local_renderer_) {
+ delete local_renderer_;
+ local_renderer_ = NULL;
+ }
+ console_->SetPrompt(NULL);
+ console_->Print("call destroyed");
+ call_ = NULL;
+ session_ = NULL;
+ }
+}
+
+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(NULL);
+ console_->Print("logged out..." + strerror(error));
+ Quit();
+ }
+}
+
+void CallClient::InitPhone() {
+ std::string client_unique = xmpp_client_->jid().Str();
+ talk_base::InitRandom(client_unique.c_str(), client_unique.size());
+
+ worker_thread_ = new talk_base::Thread();
+ // The worker thread must be started here since initialization of
+ // the ChannelManager will generate messages that need to be
+ // dispatched by it.
+ worker_thread_->Start();
+
+ network_manager_ = new talk_base::NetworkManager();
+
+ // TODO(juberti): Decide if the relay address should be specified here.
+ talk_base::SocketAddress stun_addr("stun.l.google.com", 19302);
+ port_allocator_ =
+ new cricket::BasicPortAllocator(network_manager_, stun_addr,
+ talk_base::SocketAddress(), talk_base::SocketAddress(),
+ talk_base::SocketAddress());
+
+ if (portallocator_flags_ != 0) {
+ port_allocator_->set_flags(portallocator_flags_);
+ }
+ session_manager_ = new cricket::SessionManager(
+ port_allocator_, worker_thread_);
+ session_manager_->SignalRequestSignaling.connect(
+ this, &CallClient::OnRequestSignaling);
+ session_manager_->OnSignalingReady();
+
+ session_manager_task_ =
+ new cricket::SessionManagerTask(xmpp_client_, session_manager_);
+ session_manager_task_->EnableOutgoingMessages();
+ session_manager_task_->Start();
+
+#ifdef USE_TALK_SOUND
+ if (!sound_system_factory_) {
+ sound_system_factory_ = new cricket::PlatformSoundSystemFactory();
+ }
+#endif
+
+ if (!media_engine_) {
+ media_engine_ = cricket::MediaEngine::Create(
+#ifdef USE_TALK_SOUND
+ sound_system_factory_
+#endif
+ );
+ }
+
+ media_client_ = new cricket::MediaSessionClient(
+ xmpp_client_->jid(),
+ session_manager_,
+ media_engine_,
+ new cricket::DeviceManager(
+#ifdef USE_TALK_SOUND
+ sound_system_factory_
+#endif
+ ));
+ media_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate);
+ media_client_->SignalDevicesChange.connect(this,
+ &CallClient::OnDevicesChange);
+}
+
+void CallClient::OnRequestSignaling() {
+ session_manager_->OnSignalingReady();
+}
+
+void CallClient::OnCallCreate(cricket::Call* call) {
+ call->SignalSessionState.connect(this, &CallClient::OnSessionState);
+ if (call->video()) {
+ local_renderer_ = new NullRenderer("local");
+ remote_renderer_ = new NullRenderer("remote");
+ }
+}
+
+void CallClient::OnSessionState(cricket::Call* call,
+ cricket::BaseSession* session,
+ cricket::BaseSession::State state) {
+ if (state == cricket::Session::STATE_RECEIVEDINITIATE) {
+ buzz::Jid jid(session->remote_name());
+ console_->Printf("Incoming call from '%s'", jid.Str().c_str());
+ call_ = call;
+ session_ = session;
+ incoming_call_ = true;
+ if (auto_accept_) {
+ Accept();
+ }
+ } else 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");
+ } 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");
+ }
+}
+
+void CallClient::InitPresence() {
+ presence_push_ = new buzz::PresencePushTask(xmpp_client_, this);
+ presence_push_->SignalStatusUpdate.connect(
+ this, &CallClient::OnStatusUpdate);
+ presence_push_->SignalMucJoined.connect(this, &CallClient::OnMucJoined);
+ presence_push_->SignalMucLeft.connect(this, &CallClient::OnMucLeft);
+ presence_push_->SignalMucStatusUpdate.connect(
+ this, &CallClient::OnMucStatusUpdate);
+ presence_push_->Start();
+
+ presence_out_ = new buzz::PresenceOutTask(xmpp_client_);
+ RefreshStatus();
+ presence_out_->Start();
+
+ muc_invite_recv_ = new buzz::MucInviteRecvTask(xmpp_client_);
+ muc_invite_recv_->SignalInviteReceived.connect(this,
+ &CallClient::OnMucInviteReceived);
+ muc_invite_recv_->Start();
+
+ muc_invite_send_ = new buzz::MucInviteSendTask(xmpp_client_);
+ muc_invite_send_->Start();
+
+ friend_invite_send_ = new buzz::FriendInviteSendTask(xmpp_client_);
+ friend_invite_send_->Start();
+}
+
+void CallClient::RefreshStatus() {
+ int media_caps = media_client_->GetCapabilities();
+ my_status_.set_jid(xmpp_client_->jid());
+ my_status_.set_available(true);
+ my_status_.set_show(buzz::Status::SHOW_ONLINE);
+ my_status_.set_priority(0);
+ my_status_.set_know_capabilities(true);
+ my_status_.set_pmuc_capability(true);
+ my_status_.set_phone_capability(
+ (media_caps & cricket::MediaEngine::AUDIO_RECV) != 0);
+ my_status_.set_video_capability(
+ (media_caps & cricket::MediaEngine::VIDEO_RECV) != 0);
+ my_status_.set_camera_capability(
+ (media_caps & cricket::MediaEngine::VIDEO_SEND) != 0);
+ my_status_.set_is_google_client(true);
+ my_status_.set_version("1.0.0.67");
+ presence_out_->Send(my_status_);
+}
+
+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_->SetPrompting(false);
+ 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++;
+ }
+ console_->SetPrompting(true);
+}
+
+void CallClient::InviteFriend(const std::string& name) {
+ buzz::Jid jid(name);
+ if (!jid.IsValid() || jid.node() == "") {
+ console_->Printf("Invalid JID. JIDs should be in the form user@domain\n");
+ return;
+ }
+ // Note: for some reason the Buzz backend does not forward our presence
+ // subscription requests to the end user when that user is another call
+ // client as opposed to a Smurf user. Thus, in that scenario, you must
+ // run the friend command as the other user too to create the linkage
+ // (and you won't be notified to do so).
+ friend_invite_send_->Send(jid);
+ console_->Printf("Requesting to befriend %s.\n", name.c_str());
+}
+
+void CallClient::MakeCallTo(const std::string& name, bool video) {
+ bool found = false;
+ bool is_muc = false;
+ buzz::Jid callto_jid(name);
+ buzz::Jid found_jid;
+ if (name.length() == 0 && mucs_.size() > 0) {
+ // if no name, and in a MUC, establish audio with the MUC
+ found_jid = mucs_.begin()->first;
+ found = true;
+ is_muc = true;
+ } else if (callto_jid.resource() == "voicemail") {
+ // if the resource is /voicemail, allow that
+ found_jid = callto_jid;
+ found = true;
+ } else {
+ // otherwise, it's a friend
+ for (RosterMap::iterator iter = roster_->begin();
+ iter != roster_->end(); ++iter) {
+ if (iter->second.jid.BareEquals(callto_jid)) {
+ found = true;
+ found_jid = iter->second.jid;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (mucs_.count(callto_jid) == 1 &&
+ mucs_[callto_jid]->state() == buzz::Muc::MUC_JOINED) {
+ found = true;
+ found_jid = callto_jid;
+ is_muc = true;
+ }
+ }
+ }
+
+ if (found) {
+ console_->Printf("Found %s '%s'", is_muc ? "room" : "online friend",
+ found_jid.Str().c_str());
+ PlaceCall(found_jid, is_muc, video);
+ } else {
+ console_->Printf("Could not find online friend '%s'", name.c_str());
+ }
+}
+
+void CallClient::PlaceCall(const buzz::Jid& jid, bool is_muc, bool video) {
+ media_client_->SignalCallDestroy.connect(
+ this, &CallClient::OnCallDestroy);
+ if (!call_) {
+ call_ = media_client_->CreateCall(video, is_muc);
+ console_->SetPrompt(jid.Str().c_str());
+ session_ = call_->InitiateSession(jid);
+ if (is_muc) {
+ // If people in this room are already in a call, must add all their
+ // streams.
+ buzz::Muc::MemberMap& members = mucs_[jid]->members();
+ for (buzz::Muc::MemberMap::iterator elem = members.begin();
+ elem != members.end();
+ ++elem) {
+ AddStream(elem->second.audio_src_id(), elem->second.video_src_id());
+ }
+ }
+ }
+ media_client_->SetFocus(call_);
+ if (call_->video()) {
+ call_->SetLocalRenderer(local_renderer_);
+ // TODO(tschmelcher): Call this once for every different remote SSRC
+ // once we get to testing multiway video.
+ call_->SetVideoRenderer(session_, 0, remote_renderer_);
+ }
+}
+
+void CallClient::CallVoicemail(const std::string& name) {
+ buzz::Jid jid(name);
+ if (!jid.IsValid() || jid.node() == "") {
+ console_->Printf("Invalid JID. JIDs should be in the form user@domain\n");
+ return;
+ }
+ buzz::VoicemailJidRequester *request =
+ new buzz::VoicemailJidRequester(xmpp_client_, jid, my_status_.jid());
+ request->SignalGotVoicemailJid.connect(this,
+ &CallClient::OnFoundVoicemailJid);
+ request->SignalVoicemailJidError.connect(this,
+ &CallClient::OnVoicemailJidError);
+ request->Start();
+}
+
+void CallClient::OnFoundVoicemailJid(const buzz::Jid& to,
+ const buzz::Jid& voicemail) {
+ console_->Printf("Calling %s's voicemail.\n", to.Str().c_str());
+ PlaceCall(voicemail, false, false);
+}
+
+void CallClient::OnVoicemailJidError(const buzz::Jid& to) {
+ console_->Printf("Unable to voicemail %s.\n", to.Str().c_str());
+}
+
+void CallClient::AddStream(uint32 audio_src_id, uint32 video_src_id) {
+ if (audio_src_id || video_src_id) {
+ console_->Printf("Adding stream (%u, %u)\n", audio_src_id, video_src_id);
+ call_->AddStream(session_, audio_src_id, video_src_id);
+ }
+}
+
+void CallClient::RemoveStream(uint32 audio_src_id, uint32 video_src_id) {
+ if (audio_src_id || video_src_id) {
+ console_->Printf("Removing stream (%u, %u)\n", audio_src_id, video_src_id);
+ call_->RemoveStream(session_, audio_src_id, video_src_id);
+ }
+}
+
+void CallClient::Accept() {
+ assert(call_ && incoming_call_);
+ assert(call_->sessions().size() == 1);
+ call_->AcceptSession(call_->sessions()[0]);
+ media_client_->SetFocus(call_);
+ if (call_->video()) {
+ call_->SetLocalRenderer(local_renderer_);
+ // The client never does an accept for multiway, so this must be 1:1,
+ // so there's no SSRC.
+ call_->SetVideoRenderer(session_, 0, remote_renderer_);
+ }
+ incoming_call_ = false;
+}
+
+void CallClient::Reject() {
+ assert(call_ && incoming_call_);
+ call_->RejectSession(call_->sessions()[0]);
+ incoming_call_ = false;
+}
+
+void CallClient::Quit() {
+ talk_base::Thread::Current()->Quit();
+}
+
+void CallClient::JoinMuc(const std::string& room) {
+ buzz::Jid room_jid;
+ if (room.length() > 0) {
+ room_jid = buzz::Jid(room);
+ } else {
+ // generate a GUID of the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,
+ // for an eventual JID of private-chat-<GUID>@groupchat.google.com
+ char guid[37], guid_room[256];
+ for (size_t i = 0; i < ARRAY_SIZE(guid) - 1;) {
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
+ guid[i++] = '-';
+ } else {
+ sprintf(guid + i, "%04x", rand());
+ i += 4;
+ }
+ }
+
+ talk_base::sprintfn(guid_room, ARRAY_SIZE(guid_room),
+ "private-chat-%s@%s", guid, pmuc_domain_.c_str());
+ room_jid = buzz::Jid(guid_room);
+ }
+
+ if (!room_jid.IsValid()) {
+ console_->Printf("Unable to make valid muc endpoint for %s", room.c_str());
+ return;
+ }
+
+ MucMap::iterator elem = mucs_.find(room_jid);
+ if (elem != mucs_.end()) {
+ console_->Printf("This MUC already exists.");
+ return;
+ }
+
+ buzz::Muc* muc = new buzz::Muc(room_jid, xmpp_client_->jid().node());
+ mucs_[room_jid] = muc;
+ presence_out_->SendDirected(muc->local_jid(), my_status_);
+}
+
+void CallClient::OnMucInviteReceived(const buzz::Jid& inviter,
+ const buzz::Jid& room,
+ const std::vector<buzz::AvailableMediaEntry>& avail) {
+
+ console_->Printf("Invited to join %s by %s.\n", room.Str().c_str(),
+ inviter.Str().c_str());
+ console_->Printf("Available media:\n");
+ if (avail.size() > 0) {
+ for (std::vector<buzz::AvailableMediaEntry>::const_iterator i =
+ avail.begin();
+ i != avail.end();
+ ++i) {
+ console_->Printf(" %s, %s\n",
+ buzz::AvailableMediaEntry::TypeAsString(i->type),
+ buzz::AvailableMediaEntry::StatusAsString(i->status));
+ }
+ } else {
+ console_->Printf(" None\n");
+ }
+ // We automatically join the room.
+ JoinMuc(room.Str());
+}
+
+void CallClient::OnMucJoined(const buzz::Jid& endpoint) {
+ MucMap::iterator elem = mucs_.find(endpoint);
+ ASSERT(elem != mucs_.end() &&
+ elem->second->state() == buzz::Muc::MUC_JOINING);
+
+ buzz::Muc* muc = elem->second;
+ muc->set_state(buzz::Muc::MUC_JOINED);
+ console_->Printf("Joined \"%s\"", muc->jid().Str().c_str());
+}
+
+void CallClient::OnMucStatusUpdate(const buzz::Jid& jid,
+ const buzz::MucStatus& status) {
+
+ // Look up this muc.
+ MucMap::iterator elem = mucs_.find(jid);
+ ASSERT(elem != mucs_.end() &&
+ elem->second->state() == buzz::Muc::MUC_JOINED);
+
+ buzz::Muc* muc = elem->second;
+
+ if (status.jid().IsBare() || status.jid() == muc->local_jid()) {
+ // We are only interested in status about other users.
+ return;
+ }
+
+ if (!status.available()) {
+ // User is leaving the room.
+ buzz::Muc::MemberMap::iterator elem =
+ muc->members().find(status.jid().resource());
+
+ ASSERT(elem != muc->members().end());
+
+ // If user had src-ids, they have the left the room without explicitly
+ // hanging-up; must tear down the stream if in a call to this room.
+ if (call_ && session_->remote_name() == muc->jid().Str()) {
+ RemoveStream(elem->second.audio_src_id(), elem->second.video_src_id());
+ }
+
+ // Remove them from the room.
+ muc->members().erase(elem);
+ } else {
+ // Either user has joined or something changed about them.
+ // Note: The [] operator here will create a new entry if it does not
+ // exist, which is what we want.
+ buzz::MucStatus& member_status(
+ muc->members()[status.jid().resource()]);
+ if (call_ && session_->remote_name() == muc->jid().Str()) {
+ // We are in a call to this muc. Must potentially update our streams.
+ // The following code will correctly update our streams regardless of
+ // whether the SSRCs have been removed, added, or changed and regardless
+ // of whether that has been done to both or just one. This relies on the
+ // fact that AddStream/RemoveStream do nothing for SSRC arguments that are
+ // zero.
+ uint32 remove_audio_src_id = 0;
+ uint32 remove_video_src_id = 0;
+ uint32 add_audio_src_id = 0;
+ uint32 add_video_src_id = 0;
+ if (member_status.audio_src_id() != status.audio_src_id()) {
+ remove_audio_src_id = member_status.audio_src_id();
+ add_audio_src_id = status.audio_src_id();
+ }
+ if (member_status.video_src_id() != status.video_src_id()) {
+ remove_video_src_id = member_status.video_src_id();
+ add_video_src_id = status.video_src_id();
+ }
+ // Remove the old SSRCs, if any.
+ RemoveStream(remove_audio_src_id, remove_video_src_id);
+ // Add the new SSRCs, if any.
+ AddStream(add_audio_src_id, add_video_src_id);
+ }
+ // Update the status. This will use the compiler-generated copy
+ // constructor, which is perfectly adequate for this class.
+ member_status = status;
+ }
+}
+
+void CallClient::LeaveMuc(const std::string& room) {
+ buzz::Jid room_jid;
+ if (room.length() > 0) {
+ room_jid = buzz::Jid(room);
+ } else if (mucs_.size() > 0) {
+ // leave the first MUC if no JID specified
+ room_jid = mucs_.begin()->first;
+ }
+
+ if (!room_jid.IsValid()) {
+ console_->Printf("Invalid MUC JID.");
+ return;
+ }
+
+ MucMap::iterator elem = mucs_.find(room_jid);
+ if (elem == mucs_.end()) {
+ console_->Printf("No such MUC.");
+ return;
+ }
+
+ buzz::Muc* muc = elem->second;
+ muc->set_state(buzz::Muc::MUC_LEAVING);
+
+ buzz::Status status;
+ status.set_jid(my_status_.jid());
+ status.set_available(false);
+ status.set_priority(0);
+ presence_out_->SendDirected(muc->local_jid(), status);
+}
+
+void CallClient::OnMucLeft(const buzz::Jid& endpoint, int error) {
+ // We could be kicked from a room from any state. We would hope this
+ // happens While in the MUC_LEAVING state
+ MucMap::iterator elem = mucs_.find(endpoint);
+ if (elem == mucs_.end())
+ return;
+
+ buzz::Muc* muc = elem->second;
+ if (muc->state() == buzz::Muc::MUC_JOINING) {
+ console_->Printf("Failed to join \"%s\", code=%d",
+ muc->jid().Str().c_str(), error);
+ } else if (muc->state() == buzz::Muc::MUC_JOINED) {
+ console_->Printf("Kicked from \"%s\"",
+ muc->jid().Str().c_str());
+ }
+
+ delete muc;
+ mucs_.erase(elem);
+}
+
+void CallClient::InviteToMuc(const std::string& user, const std::string& room) {
+ // First find the room.
+ const buzz::Muc* found_muc;
+ if (room.length() == 0) {
+ if (mucs_.size() == 0) {
+ console_->Printf("Not in a room yet; can't invite.\n");
+ return;
+ }
+ // Invite to the first muc
+ found_muc = mucs_.begin()->second;
+ } else {
+ MucMap::iterator elem = mucs_.find(buzz::Jid(room));
+ if (elem == mucs_.end()) {
+ console_->Printf("Not in room %s.\n", room.c_str());
+ return;
+ }
+ found_muc = elem->second;
+ }
+ // Now find the user. We invite all of their resources.
+ bool found_user = false;
+ buzz::Jid user_jid(user);
+ for (RosterMap::iterator iter = roster_->begin();
+ iter != roster_->end(); ++iter) {
+ if (iter->second.jid.BareEquals(user_jid)) {
+ muc_invite_send_->Send(iter->second.jid, *found_muc);
+ found_user = true;
+ }
+ }
+ if (!found_user) {
+ console_->Printf("No such friend as %s.\n", user.c_str());
+ return;
+ }
+}
+
+void CallClient::GetDevices() {
+ std::vector<std::string> names;
+ media_client_->GetAudioInputDevices(&names);
+ printf("Audio input devices:\n");
+ PrintDevices(names);
+ media_client_->GetAudioOutputDevices(&names);
+ printf("Audio output devices:\n");
+ PrintDevices(names);
+ media_client_->GetVideoCaptureDevices(&names);
+ printf("Video capture devices:\n");
+ PrintDevices(names);
+}
+
+void CallClient::PrintDevices(const std::vector<std::string>& names) {
+ for (size_t i = 0; i < names.size(); ++i) {
+ printf("%d: %s\n", static_cast<int>(i), names[i].c_str());
+ }
+}
+
+void CallClient::OnDevicesChange() {
+ printf("Devices changed.\n");
+ RefreshStatus();
+}
+
+void CallClient::SetVolume(const std::string& level) {
+ media_client_->SetOutputVolume(strtol(level.c_str(), NULL, 10));
+}
diff --git a/third_party/libjingle/source/talk/examples/call/callclient.h b/third_party/libjingle/source/talk/examples/call/callclient.h
new file mode 100644
index 0000000..af27f34
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/callclient.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 TALK_EXAMPLES_CALL_CALLCLIENT_H_
+#define TALK_EXAMPLES_CALL_CALLCLIENT_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "talk/p2p/base/session.h"
+#include "talk/session/phone/mediachannel.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/examples/call/status.h"
+#include "talk/examples/call/console.h"
+#ifdef USE_TALK_SOUND
+#include "talk/sound/soundsystemfactory.h"
+#endif
+
+namespace buzz {
+class PresencePushTask;
+class PresenceOutTask;
+class MucInviteRecvTask;
+class MucInviteSendTask;
+class FriendInviteSendTask;
+class VoicemailJidRequester;
+class DiscoInfoQueryTask;
+class Muc;
+class Status;
+class MucStatus;
+struct AvailableMediaEntry;
+}
+
+namespace talk_base {
+class Thread;
+class NetworkManager;
+}
+
+namespace cricket {
+class PortAllocator;
+class MediaEngine;
+class MediaSessionClient;
+class Receiver;
+class Call;
+class SessionManagerTask;
+}
+
+struct RosterItem {
+ buzz::Jid jid;
+ buzz::Status::Show show;
+ std::string status;
+};
+
+class NullRenderer;
+
+class CallClient: public sigslot::has_slots<> {
+ public:
+ explicit CallClient(buzz::XmppClient* xmpp_client);
+ ~CallClient();
+
+ cricket::MediaSessionClient* media_client() const { return media_client_; }
+ void SetMediaEngine(cricket::MediaEngine* media_engine) {
+ media_engine_ = media_engine;
+ }
+ void SetAutoAccept(bool auto_accept) {
+ auto_accept_ = auto_accept;
+ }
+ void SetPmucDomain(const std::string &pmuc_domain) {
+ pmuc_domain_ = pmuc_domain;
+ }
+ void SetConsole(Console *console) {
+ console_ = console;
+ }
+
+ void ParseLine(const std::string &str);
+
+ void InviteFriend(const std::string& user);
+ void JoinMuc(const std::string& room);
+ void InviteToMuc(const std::string& user, const std::string& room);
+ void LeaveMuc(const std::string& room);
+ void SetPortAllocatorFlags(uint32 flags) { portallocator_flags_ = flags; }
+
+ typedef std::map<buzz::Jid, buzz::Muc*> MucMap;
+
+ const MucMap& mucs() const {
+ return mucs_;
+ }
+
+ private:
+ void AddStream(uint32 audio_src_id, uint32 video_src_id);
+ void RemoveStream(uint32 audio_src_id, uint32 video_src_id);
+ void OnStateChange(buzz::XmppEngine::State state);
+
+ void InitPhone();
+ void InitPresence();
+ void RefreshStatus();
+ void OnRequestSignaling();
+ void OnCallCreate(cricket::Call* call);
+ void OnCallDestroy(cricket::Call* call);
+ void OnSessionState(cricket::Call* call,
+ cricket::BaseSession* session,
+ cricket::BaseSession::State state);
+ void OnStatusUpdate(const buzz::Status& status);
+ void OnMucInviteReceived(const buzz::Jid& inviter, const buzz::Jid& room,
+ const std::vector<buzz::AvailableMediaEntry>& avail);
+ void OnMucJoined(const buzz::Jid& endpoint);
+ void OnMucStatusUpdate(const buzz::Jid& jid, const buzz::MucStatus& status);
+ void OnMucLeft(const buzz::Jid& endpoint, int error);
+ void OnDevicesChange();
+ void OnFoundVoicemailJid(const buzz::Jid& to, const buzz::Jid& voicemail);
+ void OnVoicemailJidError(const buzz::Jid& to);
+
+ static const std::string strerror(buzz::XmppEngine::Error err);
+
+ void PrintRoster();
+ void MakeCallTo(const std::string& name, bool video);
+ void PlaceCall(const buzz::Jid& jid, bool is_muc, bool video);
+ void CallVoicemail(const std::string& name);
+ void Accept();
+ void Reject();
+ void Quit();
+
+ void GetDevices();
+ void PrintDevices(const std::vector<std::string>& names);
+
+ void SetVolume(const std::string& level);
+
+ typedef std::map<std::string, RosterItem> RosterMap;
+
+ Console *console_;
+ buzz::XmppClient* xmpp_client_;
+ talk_base::Thread* worker_thread_;
+ talk_base::NetworkManager* network_manager_;
+ cricket::PortAllocator* port_allocator_;
+ cricket::SessionManager* session_manager_;
+ cricket::SessionManagerTask* session_manager_task_;
+ cricket::MediaEngine* media_engine_;
+ cricket::MediaSessionClient* media_client_;
+ MucMap mucs_;
+
+ cricket::Call* call_;
+ cricket::BaseSession *session_;
+ bool incoming_call_;
+ bool auto_accept_;
+ std::string pmuc_domain_;
+ NullRenderer* local_renderer_;
+ NullRenderer* remote_renderer_;
+
+ buzz::Status my_status_;
+ buzz::PresencePushTask* presence_push_;
+ buzz::PresenceOutTask* presence_out_;
+ buzz::MucInviteRecvTask* muc_invite_recv_;
+ buzz::MucInviteSendTask* muc_invite_send_;
+ buzz::FriendInviteSendTask* friend_invite_send_;
+ RosterMap* roster_;
+ uint32 portallocator_flags_;
+#ifdef USE_TALK_SOUND
+ cricket::SoundSystemFactory* sound_system_factory_;
+#endif
+};
+
+#endif // TALK_EXAMPLES_CALL_CALLCLIENT_H_
diff --git a/third_party/libjingle/source/talk/examples/call/console.cc b/third_party/libjingle/source/talk/examples/call/console.cc
new file mode 100644
index 0000000..0aa7a4f
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/console.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.
+ */
+
+#define _CRT_SECURE_NO_DEPRECATE 1
+
+#ifdef POSIX
+#include <unistd.h>
+#endif // POSIX
+#include <cassert>
+#include "talk/base/logging.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/stringutils.h"
+#include "talk/examples/call/console.h"
+#include "talk/examples/call/callclient.h"
+
+#ifdef POSIX
+#include <signal.h>
+
+static void DoNothing(int unused) {}
+#endif
+
+Console::Console(talk_base::Thread *thread, CallClient *client) :
+ client_(client), client_thread_(thread),
+ console_thread_(new talk_base::Thread()), prompt_(std::string("call")),
+ prompting_(true) {
+}
+
+Console::~Console() {
+ Stop();
+}
+
+void Console::Start() {
+ if (!console_thread_.get()) {
+ // stdin was closed in Stop(), so we can't restart.
+ LOG(LS_ERROR) << "Cannot re-start";
+ return;
+ }
+ if (console_thread_->started()) {
+ LOG(LS_WARNING) << "Already started";
+ return;
+ }
+ console_thread_->Start();
+ console_thread_->Post(this, MSG_START);
+}
+
+void Console::Stop() {
+ if (console_thread_.get() && console_thread_->started()) {
+#ifdef WIN32
+ CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
+#else
+ close(fileno(stdin));
+ // This forces the read() in fgets() to return with errno = EINTR. fgets()
+ // will retry the read() and fail, thus returning.
+ pthread_kill(console_thread_->GetPThread(), SIGUSR1);
+#endif
+ console_thread_->Stop();
+ console_thread_.reset();
+ }
+}
+
+void Console::SetEcho(bool on) {
+#ifdef WIN32
+ HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
+ if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL))
+ return;
+
+ DWORD mode;
+ if (!GetConsoleMode(hIn, &mode))
+ return;
+
+ if (on) {
+ mode = mode | ENABLE_ECHO_INPUT;
+ } else {
+ mode = mode & ~ENABLE_ECHO_INPUT;
+ }
+
+ SetConsoleMode(hIn, mode);
+#else
+ int re;
+ if (on)
+ re = system("stty echo");
+ else
+ re = system("stty -echo");
+ if (-1 == re)
+ return;
+#endif
+}
+
+void Console::Print(const char* str) {
+ printf("\n%s", str);
+ if (prompting_)
+ printf("\n(%s) ", prompt_.c_str());
+}
+
+void Console::Print(const std::string& str) {
+ Print(str.c_str());
+}
+
+void Console::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 Console::RunConsole() {
+ char input_buffer[128];
+ while (fgets(input_buffer, sizeof(input_buffer), stdin) != NULL) {
+ client_thread_->Post(this, MSG_INPUT,
+ new talk_base::TypedMessageData<std::string>(input_buffer));
+ }
+}
+
+void Console::OnMessage(talk_base::Message *msg) {
+ switch (msg->message_id) {
+ case MSG_START:
+#ifdef POSIX
+ // Install a no-op signal so that we can abort RunConsole() by raising
+ // SIGUSR1.
+ struct sigaction act;
+ act.sa_handler = &DoNothing;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ if (sigaction(SIGUSR1, &act, NULL) < 0) {
+ LOG(LS_WARNING) << "Can't install signal";
+ }
+#endif
+ RunConsole();
+ break;
+ case MSG_INPUT:
+ talk_base::TypedMessageData<std::string> *data =
+ static_cast<talk_base::TypedMessageData<std::string>*>(msg->pdata);
+ client_->ParseLine(data->data());
+ break;
+ }
+}
diff --git a/third_party/libjingle/source/talk/examples/call/console.h b/third_party/libjingle/source/talk/examples/call/console.h
new file mode 100644
index 0000000..e2ba4f7
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/console.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 TALK_EXAMPLES_CALL_CONSOLE_H_
+#define TALK_EXAMPLES_CALL_CONSOLE_H_
+
+#include <cstdio>
+
+#include "talk/base/thread.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/scoped_ptr.h"
+
+class CallClient;
+
+class Console : public talk_base::MessageHandler {
+ public:
+ Console(talk_base::Thread *thread, CallClient *client);
+ ~Console();
+
+ // Starts reading lines from the console and giving them to the CallClient.
+ void Start();
+ // Stops reading lines. Cannot be restarted.
+ void Stop();
+
+ virtual void OnMessage(talk_base::Message *msg);
+
+ void SetPrompt(const char *prompt) {
+ prompt_ = prompt ? std::string(prompt) : std::string("call");
+ }
+
+ void SetPrompting(bool prompting) {
+ prompting_ = prompting;
+ if (prompting)
+ printf("\n(%s) ", prompt_.c_str());
+ }
+
+ bool prompting() { return prompting_; }
+
+ void Print(const char* str);
+ void Print(const std::string& str);
+ void Printf(const char* format, ...);
+
+ static void SetEcho(bool on);
+
+ private:
+ enum {
+ MSG_START,
+ MSG_INPUT,
+ };
+
+ void RunConsole();
+ void ParseLine(std::string &str);
+
+ CallClient *client_;
+ talk_base::Thread *client_thread_;
+ talk_base::scoped_ptr<talk_base::Thread> console_thread_;
+ std::string prompt_;
+ bool prompting_;
+};
+
+#endif // TALK_EXAMPLES_CALL_CONSOLE_H_
diff --git a/third_party/libjingle/source/talk/examples/call/discoitemsquerytask.cc b/third_party/libjingle/source/talk/examples/call/discoitemsquerytask.cc
new file mode 100644
index 0000000..df6d96d
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/discoitemsquerytask.cc
@@ -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.
+ */
+
+#include "talk/examples/call/discoitemsquerytask.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/xmpp/constants.h"
+
+
+namespace buzz {
+
+namespace {
+const int kDiscoItemsTimeout = 60;
+} // namespace
+
+DiscoItemsQueryTask::DiscoItemsQueryTask(Task* parent,
+ const std::string node,
+ const Jid& to)
+ : XmppTask(parent, XmppEngine::HL_SINGLE), node_(node) {
+ set_timeout_seconds(kDiscoItemsTimeout);
+ to_ = to;
+}
+
+int DiscoItemsQueryTask::ProcessStart() {
+ talk_base::scoped_ptr<XmlElement> get(MakeIq(STR_GET, to_, task_id()));
+
+ XmlElement* element = new XmlElement(QN_DISCO_ITEMS_QUERY, true);
+ element->AddAttr(QN_NODE, node_);
+
+ get->AddElement(element);
+
+ if (SendStanza(get.get()) != XMPP_RETURN_OK) {
+ SignalDiscoItemsError(to_, NULL);
+ return STATE_ERROR;
+ }
+
+ return STATE_RESPONSE;
+}
+
+int DiscoItemsQueryTask::ProcessResponse() {
+ const XmlElement* stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+
+ bool success = false;
+ if (stanza->Attr(QN_TYPE) != STR_ERROR) {
+ const XmlElement* query = stanza->FirstNamed(QN_DISCO_ITEMS_QUERY);
+ if (query) {
+ SignalGotDiscoItems(to_, query);
+ success = true;
+ }
+ }
+
+ if (!success) {
+ SignalDiscoItemsError(to_, stanza->FirstNamed(QN_ERROR));
+ }
+
+ return STATE_DONE;
+}
+
+int DiscoItemsQueryTask::OnTimeout() {
+ SignalDiscoItemsError(to_, NULL);
+ return XmppTask::OnTimeout();
+}
+
+bool DiscoItemsQueryTask::HandleStanza(const XmlElement* stanza) {
+ if (!MatchResponseIq(stanza, to_, task_id()))
+ return false;
+ QueueStanza(stanza);
+ return true;
+
+}
+
+}
diff --git a/third_party/libjingle/source/talk/examples/call/discoitemsquerytask.h b/third_party/libjingle/source/talk/examples/call/discoitemsquerytask.h
new file mode 100644
index 0000000..6be8c35
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/discoitemsquerytask.h
@@ -0,0 +1,88 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Fires a disco items query, such as the following example:
+//
+// <iq type='get'
+// from='foo@gmail.com/asdf'
+// to='bar@google.com'
+// id='1234'>
+// <query xmlns=' http://jabber.org/protocol/disco#items'
+// node='blah '/>
+// </iq>
+//
+// Sample response:
+//
+// <iq type='result'
+// from=' hendriks@google.com'
+// to='rsturgell@google.com/asdf'
+// id='1234'>
+// <query xmlns=' http://jabber.org/protocol/disco#items '
+// node='blah'>
+// <item something='somethingelse'/>
+// </query>
+// </iq>
+
+
+#ifndef _DISCOITEMSQUERYTASK_H_
+#define _DISCOITEMSQUERYTASK_H_
+
+#include "talk/xmpp/xmpptask.h"
+
+namespace buzz {
+
+class DiscoItemsQueryTask : public XmppTask {
+ public:
+ // TODO: Currently, this only supports one query stanza - we may eventually
+ // need it to support multiple
+ DiscoItemsQueryTask(Task* parent, const std::string node, const Jid& to);
+
+ virtual int ProcessStart();
+ virtual int ProcessResponse();
+
+ // On success, fires a signal with the jid we sent the query to and the inner
+ // XmlElement
+ sigslot::signal2<Jid, const XmlElement*> SignalGotDiscoItems;
+
+ // The XmlElement here is the error element under the error response. If the
+ // request just timed out then this will be NULL
+ sigslot::signal2<Jid, const XmlElement*> SignalDiscoItemsError;
+
+ protected:
+ virtual bool HandleStanza(const XmlElement* stanza);
+ virtual int OnTimeout();
+
+ private:
+ // The jid we're querying
+ Jid to_;
+ // The name of the node
+ const std::string node_;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/call/friendinvitesendtask.cc b/third_party/libjingle/source/talk/examples/call/friendinvitesendtask.cc
new file mode 100644
index 0000000..cdb0b2c
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/friendinvitesendtask.cc
@@ -0,0 +1,76 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/constants.h"
+#include "talk/examples/call/friendinvitesendtask.h"
+
+namespace buzz {
+
+XmppReturnStatus
+FriendInviteSendTask::Send(const Jid& user) {
+ if (GetState() != STATE_INIT && GetState() != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ // Need to first add to roster, then subscribe to presence.
+ XmlElement* iq = new XmlElement(QN_IQ);
+ iq->AddAttr(QN_TYPE, STR_SET);
+ XmlElement* query = new XmlElement(QN_ROSTER_QUERY);
+ XmlElement* item = new XmlElement(QN_ROSTER_ITEM);
+ item->AddAttr(QN_JID, user.Str());
+ item->AddAttr(QN_NAME, user.node());
+ query->AddElement(item);
+ iq->AddElement(query);
+ QueueStanza(iq);
+
+ // Subscribe to presence
+ XmlElement* presence = new XmlElement(QN_PRESENCE);
+ presence->AddAttr(QN_TO, user.Str());
+ presence->AddAttr(QN_TYPE, STR_SUBSCRIBE);
+ XmlElement* invitation = new XmlElement(QN_INVITATION);
+ invitation->AddAttr(QN_INVITE_MESSAGE,
+ "I've been using Google Talk and thought you might like to try it out. "
+ "We can use it to call each other for free over the internet. Here's an "
+ "invitation to download Google Talk. Give it a try!");
+ presence->AddElement(invitation);
+ QueueStanza(presence);
+
+ return XMPP_RETURN_OK;
+}
+
+int
+FriendInviteSendTask::ProcessStart() {
+ const XmlElement* stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+
+ if (SendStanza(stanza) != XMPP_RETURN_OK)
+ return STATE_ERROR;
+
+ return STATE_START;
+}
+
+}
diff --git a/third_party/libjingle/source/talk/examples/call/friendinvitesendtask.h b/third_party/libjingle/source/talk/examples/call/friendinvitesendtask.h
new file mode 100644
index 0000000..3f776bb
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/friendinvitesendtask.h
@@ -0,0 +1,48 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FRIENDINVITESENDTASK_H_
+#define _FRIENDINVITESENDTASK_H_
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+
+namespace buzz {
+
+class FriendInviteSendTask : public XmppTask {
+public:
+ FriendInviteSendTask(Task* parent) : XmppTask(parent) {}
+ virtual ~FriendInviteSendTask() {}
+
+ XmppReturnStatus Send(const Jid& user);
+
+ virtual int ProcessStart();
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/call/muc.h b/third_party/libjingle/source/talk/examples/call/muc.h
new file mode 100644
index 0000000..2f6df2e
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/muc.h
@@ -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.
+ */
+
+#ifndef _MUC_H_
+#define _MUC_H_
+
+#include <map>
+#include "talk/xmpp/jid.h"
+#include "talk/examples/call/status.h"
+
+namespace buzz {
+
+class Muc {
+ public:
+ Muc(const Jid& jid, const std::string& nick) : state_(MUC_JOINING),
+ jid_(jid), local_jid_(Jid(jid.Str() + "/" + nick)) {}
+ ~Muc() {};
+
+ enum State { MUC_JOINING, MUC_JOINED, MUC_LEAVING };
+ State state() const { return state_; }
+ void set_state(State state) { state_ = state; }
+ const Jid & jid() const { return jid_; }
+ const Jid & local_jid() const { return local_jid_; }
+
+ typedef std::map<std::string, MucStatus> MemberMap;
+
+ // All the intelligence about how to manage the members is in
+ // CallClient, so we completely expose the map.
+ MemberMap& members() {
+ return members_;
+ }
+
+private:
+ State state_;
+ Jid jid_;
+ Jid local_jid_;
+ MemberMap members_;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/call/mucinviterecvtask.cc b/third_party/libjingle/source/talk/examples/call/mucinviterecvtask.cc
new file mode 100644
index 0000000..061db74
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/mucinviterecvtask.cc
@@ -0,0 +1,124 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/constants.h"
+#include "talk/examples/call/mucinviterecvtask.h"
+
+namespace buzz {
+
+const char* types[] = {
+ "unknown",
+ "audio",
+ "video",
+};
+
+const char* statuses[] = {
+ "unknown",
+ "sendrecv",
+ "sendonly",
+ "recvonly",
+ "inactive",
+};
+
+const char*
+AvailableMediaEntry::TypeAsString(type_t type) {
+ // The values of the constants have been chosen such that this is correct.
+ return types[type];
+}
+
+const char*
+AvailableMediaEntry::StatusAsString(status_t status) {
+ // The values of the constants have been chosen such that this is correct.
+ return statuses[status];
+}
+
+int bodytext_to_array_pos(const XmlElement* elem, const char* array[],
+ int len, int defval = -1) {
+ if (elem) {
+ const std::string& body(elem->BodyText());
+ for (int i = 0; i < len; ++i) {
+ if (body == array[i]) {
+ // Found it.
+ return i;
+ }
+ }
+ }
+ // If we get here, it's not any value in the array.
+ return defval;
+}
+
+bool
+MucInviteRecvTask::HandleStanza(const XmlElement* stanza) {
+ // Figuring out that we want to handle this is a lot of the work of
+ // actually handling it, so we handle it right here instead of queueing it.
+ const XmlElement* xstanza;
+ const XmlElement* invite;
+ if (stanza->Name() != QN_MESSAGE) return false;
+ xstanza = stanza->FirstNamed(QN_MUC_USER_X);
+ if (!xstanza) return false;
+ invite = xstanza->FirstNamed(QN_MUC_USER_INVITE);
+ if (!invite) return false;
+ // Else it's an invite and we definitely want to handle it. Parse the
+ // available-media, if any.
+ std::vector<AvailableMediaEntry> v;
+ const XmlElement* avail =
+ invite->FirstNamed(QN_GOOGLE_MUC_USER_AVAILABLE_MEDIA);
+ if (avail) {
+ for (const XmlElement* entry = avail->FirstNamed(QN_GOOGLE_MUC_USER_ENTRY);
+ entry;
+ entry = entry->NextNamed(QN_GOOGLE_MUC_USER_ENTRY)) {
+ AvailableMediaEntry tmp;
+ // In the interest of debugging, we accept as much valid-looking data
+ // as we can.
+ tmp.label = atoi(entry->Attr(QN_LABEL).c_str());
+ tmp.type = static_cast<AvailableMediaEntry::type_t>(
+ bodytext_to_array_pos(
+ entry->FirstNamed(QN_GOOGLE_MUC_USER_TYPE),
+ types,
+ sizeof(types)/sizeof(const char*),
+ AvailableMediaEntry::TYPE_UNKNOWN));
+ tmp.status = static_cast<AvailableMediaEntry::status_t>(
+ bodytext_to_array_pos(
+ entry->FirstNamed(QN_GOOGLE_MUC_USER_STATUS),
+ statuses,
+ sizeof(statuses)/sizeof(const char*),
+ AvailableMediaEntry::STATUS_UNKNOWN));
+ v.push_back(tmp);
+ }
+ }
+ SignalInviteReceived(Jid(invite->Attr(QN_FROM)), Jid(stanza->Attr(QN_FROM)),
+ v);
+ return true;
+}
+
+int
+MucInviteRecvTask::ProcessStart() {
+ // We never queue anything so we are always blocked.
+ return STATE_BLOCKED;
+}
+
+}
diff --git a/third_party/libjingle/source/talk/examples/call/mucinviterecvtask.h b/third_party/libjingle/source/talk/examples/call/mucinviterecvtask.h
new file mode 100644
index 0000000..892b484
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/mucinviterecvtask.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 _MUCINVITERECVTASK_H_
+#define _MUCINVITERECVTASK_H_
+
+#include <vector>
+
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+
+namespace buzz {
+
+struct AvailableMediaEntry {
+ enum type_t {
+ // SIP defines other media types, but these are the only ones we use in
+ // multiway jingle.
+ // These numbers are important; see .cc file
+ TYPE_UNKNOWN = 0, // indicates invalid string
+ TYPE_AUDIO = 1,
+ TYPE_VIDEO = 2,
+ };
+
+ enum status_t {
+ // These numbers are important; see .cc file
+ STATUS_UNKNOWN = 0, // indicates invalid string
+ STATUS_SENDRECV = 1,
+ STATUS_SENDONLY = 2,
+ STATUS_RECVONLY = 3,
+ STATUS_INACTIVE = 4,
+ };
+
+ uint32 label;
+ type_t type;
+ status_t status;
+
+ static const char* TypeAsString(type_t type);
+ static const char* StatusAsString(status_t status);
+};
+
+class MucInviteRecvTask : public XmppTask {
+ public:
+ MucInviteRecvTask(Task* parent) : XmppTask(parent, XmppEngine::HL_TYPE) {}
+ virtual int ProcessStart();
+
+ // First arg is inviter's JID; second is MUC's JID.
+ sigslot::signal3<const Jid&, const Jid&, const std::vector<AvailableMediaEntry>& > SignalInviteReceived;
+
+ protected:
+ virtual bool HandleStanza(const XmlElement* stanza);
+
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/call/mucinvitesendtask.cc b/third_party/libjingle/source/talk/examples/call/mucinvitesendtask.cc
new file mode 100644
index 0000000..efd9a81
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/mucinvitesendtask.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/examples/call/mucinvitesendtask.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmppclient.h"
+
+namespace buzz {
+
+XmppReturnStatus
+MucInviteSendTask::Send(const Jid& user, const Muc& muc) {
+ if (GetState() != STATE_INIT && GetState() != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ XmlElement* message = new XmlElement(QN_MESSAGE);
+ message->AddAttr(QN_TO, muc.jid().Str());
+ XmlElement* xstanza = new XmlElement(QN_MUC_USER_X);
+ XmlElement* invite = new XmlElement(QN_MUC_USER_INVITE);
+ invite->AddAttr(QN_TO, user.Str());
+ xstanza->AddElement(invite);
+ message->AddElement(xstanza);
+
+ QueueStanza(message);
+ return XMPP_RETURN_OK;
+}
+
+int
+MucInviteSendTask::ProcessStart() {
+ const XmlElement* stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+
+ if (SendStanza(stanza) != XMPP_RETURN_OK)
+ return STATE_ERROR;
+
+ return STATE_START;
+}
+
+}
diff --git a/third_party/libjingle/source/talk/examples/call/mucinvitesendtask.h b/third_party/libjingle/source/talk/examples/call/mucinvitesendtask.h
new file mode 100644
index 0000000..18e0f99
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/mucinvitesendtask.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 _MUCINVITESENDTASK_H_
+#define _MUCINVITESENDTASK_H_
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/examples/call/muc.h"
+
+namespace buzz {
+
+class MucInviteSendTask : public XmppTask {
+public:
+ MucInviteSendTask(Task* parent) : XmppTask(parent) {}
+ virtual ~MucInviteSendTask() {}
+
+ XmppReturnStatus Send(const Jid& user, const Muc& muc);
+
+ virtual int ProcessStart();
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/call/presenceouttask.cc b/third_party/libjingle/source/talk/examples/call/presenceouttask.cc
new file mode 100644
index 0000000..ff3d91b
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/presenceouttask.cc
@@ -0,0 +1,147 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <time.h>
+#include <sstream>
+#include "talk/base/stringencode.h"
+#include "talk/examples/call/presenceouttask.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmppclient.h"
+
+namespace buzz {
+
+XmppReturnStatus
+PresenceOutTask::Send(const Status & s) {
+ if (GetState() != STATE_INIT && GetState() != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ QueueStanza(TranslateStatus(s));
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+PresenceOutTask::SendDirected(const Jid & j, const Status & s) {
+ if (GetState() != STATE_INIT && GetState() != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ XmlElement * presence = TranslateStatus(s);
+ presence->AddAttr(QN_TO, j.Str());
+ QueueStanza(presence);
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus PresenceOutTask::SendProbe(const Jid & jid) {
+ if (GetState() != STATE_INIT && GetState() != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ XmlElement * presence = new XmlElement(QN_PRESENCE);
+ presence->AddAttr(QN_TO, jid.Str());
+ presence->AddAttr(QN_TYPE, "probe");
+
+ QueueStanza(presence);
+ return XMPP_RETURN_OK;
+}
+
+int
+PresenceOutTask::ProcessStart() {
+ const XmlElement * stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+
+ if (SendStanza(stanza) != XMPP_RETURN_OK)
+ return STATE_ERROR;
+
+ return STATE_START;
+}
+
+XmlElement *
+PresenceOutTask::TranslateStatus(const Status & s) {
+ XmlElement * result = new XmlElement(QN_PRESENCE);
+ if (!s.available()) {
+ result->AddAttr(QN_TYPE, STR_UNAVAILABLE);
+ }
+ else {
+ 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;
+ talk_base::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);
+
+ std::string caps;
+ caps.append(s.phone_capability() ? "voice-v1" : "");
+ caps.append(s.pmuc_capability() ? " pmuc-v1" : "");
+ caps.append(s.video_capability() ? " video-v1" : "");
+ caps.append(s.camera_capability() ? " camera-v1" : "");
+
+ result->AddAttr(QN_EXT, caps, 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/third_party/libjingle/source/talk/examples/call/presenceouttask.h b/third_party/libjingle/source/talk/examples/call/presenceouttask.h
new file mode 100644
index 0000000..36e7f15
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/presenceouttask.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 _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);
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/call/presencepushtask.cc b/third_party/libjingle/source/talk/examples/call/presencepushtask.cc
new file mode 100644
index 0000000..f820030
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/presencepushtask.cc
@@ -0,0 +1,254 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/call/presencepushtask.h"
+
+#include "talk/base/stringencode.h"
+#include "talk/examples/call/muc.h"
+#include "talk/xmpp/constants.h"
+
+
+
+namespace buzz {
+
+// string helper functions -----------------------------------------------------
+
+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;
+ 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;
+
+ Jid from(stanza->Attr(QN_FROM));
+ std::map<Jid, buzz::Muc*>::const_iterator elem =
+ client_->mucs().find(from.BareJid());
+ if (elem == client_->mucs().end()) {
+ HandlePresence(from, stanza);
+ } else {
+ HandleMucPresence(elem->second, from, stanza);
+ }
+
+ return STATE_START;
+}
+
+void PresencePushTask::HandlePresence(const Jid& from,
+ const XmlElement* stanza) {
+ if (stanza->Attr(QN_TYPE) == STR_ERROR)
+ return;
+
+ Status s;
+ FillStatus(from, stanza, &s);
+ SignalStatusUpdate(s);
+}
+
+void PresencePushTask::HandleMucPresence(buzz::Muc* muc,
+ const Jid& from,
+ const XmlElement* stanza) {
+ if (from == muc->local_jid()) {
+ if (!stanza->HasAttr(QN_TYPE)) {
+ // We joined the MUC.
+ const XmlElement* elem = stanza->FirstNamed(QN_MUC_USER_X);
+ if (elem) {
+ elem = elem->FirstNamed(QN_MUC_USER_STATUS);
+ }
+ if (elem && (elem->Attr(QN_CODE) == "110" ||
+ elem->Attr(QN_CODE) == "100")) {
+ SignalMucJoined(muc->jid());
+ }
+ } else {
+ // We've been kicked. Bye.
+ int error = 0;
+ if (stanza->Attr(QN_TYPE) == STR_ERROR) {
+ const XmlElement* elem = stanza->FirstNamed(QN_ERROR);
+ if (elem && elem->HasAttr(QN_CODE)) {
+ error = atoi(elem->Attr(QN_CODE).c_str());
+ }
+ }
+ SignalMucLeft(muc->jid(), error);
+ }
+ } else {
+ MucStatus s;
+ FillMucStatus(from, stanza, &s);
+ SignalMucStatusUpdate(muc->jid(), s);
+ }
+}
+
+void PresencePushTask::FillStatus(const Jid& from, const XmlElement* stanza,
+ Status* s) {
+ s->set_jid(from);
+ if (stanza->Attr(QN_TYPE) == STR_UNAVAILABLE) {
+ s->set_available(false);
+ } 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 (talk_base::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);
+ }
+ if (ListContainsToken(exts, "video-v1")) {
+ s->set_video_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);
+ }
+ }
+}
+
+void PresencePushTask::FillMucStatus(const Jid& from, const XmlElement* stanza,
+ MucStatus* s) {
+ // First get the normal user status info. Happily, this is in the same
+ // format as it is for user presence.
+ FillStatus(from, stanza, s);
+
+ // Now look for src IDs, which will be present if this user is in a
+ // multiway call to this MUC.
+ const XmlElement* xstanza = stanza->FirstNamed(QN_MUC_USER_X);
+ if (xstanza) {
+ const XmlElement* media;
+ for (media = xstanza->FirstNamed(QN_GOOGLE_MUC_USER_MEDIA);
+ media; media = media->NextNamed(QN_GOOGLE_MUC_USER_MEDIA)) {
+
+ const XmlElement* type = media->FirstNamed(QN_GOOGLE_MUC_USER_TYPE);
+ if (!type) continue; // Shouldn't happen
+
+ const XmlElement* src_id = media->FirstNamed(QN_GOOGLE_MUC_USER_SRC_ID);
+ if (!src_id) continue; // Shouldn't happen
+
+ char *endptr;
+ uint32 src_id_num = strtoul(src_id->BodyText().c_str(), &endptr, 10);
+ if (src_id->BodyText().c_str()[0] == '\0' || endptr[0] != '\0') {
+ // String is not composed exclusively of leading whitespace plus a
+ // number (shouldn't happen). Ignore it.
+ continue;
+ }
+ // Else it's valid. Set it.
+
+ if (type->BodyText() == "audio") {
+ // This is the audio media element. Get the src-id.
+ s->set_audio_src_id(src_id_num);
+ } else if (type->BodyText() == "video") {
+ // This is the video media element. Get the src-id.
+ s->set_video_src_id(src_id_num);
+ }
+ }
+ }
+}
+
+}
diff --git a/third_party/libjingle/source/talk/examples/call/presencepushtask.h b/third_party/libjingle/source/talk/examples/call/presencepushtask.h
new file mode 100644
index 0000000..998a98e
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/presencepushtask.h
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PRESENCEPUSHTASK_H_
+#define _PRESENCEPUSHTASK_H_
+
+#include <vector>
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/base/sigslot.h"
+#include "talk/examples/call/status.h"
+#include "talk/examples/call/callclient.h"
+
+namespace buzz {
+
+class PresencePushTask : public XmppTask {
+ public:
+ PresencePushTask(Task * parent, CallClient* client)
+ : XmppTask(parent, XmppEngine::HL_TYPE),
+ client_(client) {}
+ virtual int ProcessStart();
+
+ sigslot::signal1<const Status&> SignalStatusUpdate;
+ sigslot::signal1<const Jid&> SignalMucJoined;
+ sigslot::signal2<const Jid&, int> SignalMucLeft;
+ sigslot::signal2<const Jid&, const MucStatus&> SignalMucStatusUpdate;
+
+ protected:
+ virtual bool HandleStanza(const XmlElement * stanza);
+ void HandlePresence(const Jid& from, const XmlElement * stanza);
+ void HandleMucPresence(buzz::Muc* muc,
+ const Jid& from, const XmlElement * stanza);
+ static void FillStatus(const Jid& from, const XmlElement * stanza,
+ Status* status);
+ static void FillMucStatus(const Jid& from, const XmlElement * stanza,
+ MucStatus* status);
+
+ private:
+ CallClient* client_;
+};
+
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/call/status.h b/third_party/libjingle/source/talk/examples/call/status.h
new file mode 100644
index 0000000..be4c2bd
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/status.h
@@ -0,0 +1,245 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#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),
+ e_code_(0),
+ feedback_probation_(false),
+ know_capabilities_(false),
+ phone_capability_(false),
+ pmuc_capability_(false),
+ video_capability_(false),
+ camera_capability_(false),
+ is_google_client_(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_OFFLINE = 1,
+ SHOW_XA = 2,
+ SHOW_AWAY = 3,
+ SHOW_DND = 4,
+ SHOW_ONLINE = 5,
+ SHOW_CHAT = 6,
+ };
+
+ 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_ ; }
+ 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 pmuc_capability() const { return pmuc_capability_; }
+ bool video_capability() const { return video_capability_; }
+ bool camera_capability() const { return camera_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_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_pmuc_capability(bool f) { pmuc_capability_ = f; }
+ void set_video_capability(bool f) { video_capability_ = f; }
+ void set_camera_capability(bool f) { camera_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_;
+ int e_code_;
+ std::string e_str_;
+ bool feedback_probation_;
+
+ // capabilities (valid only if know_capabilities_
+ bool know_capabilities_;
+ bool phone_capability_;
+ bool pmuc_capability_;
+ bool video_capability_;
+ bool camera_capability_;
+ bool is_google_client_;
+ std::string version_;
+
+ std::string sent_time_; // from the jabber:x:delay element
+};
+
+class MucStatus : public Status {
+public:
+ MucStatus() : audio_src_id_(0), video_src_id_(0) {}
+ uint32 audio_src_id() const { return audio_src_id_; }
+ uint32 video_src_id() const { return video_src_id_; }
+ void set_audio_src_id(uint32 audio_src_id) {
+ audio_src_id_ = audio_src_id;
+ }
+ void set_video_src_id(uint32 video_src_id) {
+ video_src_id_ = video_src_id;
+ }
+private:
+ uint32 audio_src_id_;
+ uint32 video_src_id_;
+};
+
+}
+
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/call/voicemailjidrequester.cc b/third_party/libjingle/source/talk/examples/call/voicemailjidrequester.cc
new file mode 100644
index 0000000..81f3dbc
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/voicemailjidrequester.cc
@@ -0,0 +1,135 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/call/discoitemsquerytask.h"
+#include "talk/examples/call/voicemailjidrequester.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/xmpp/constants.h"
+
+namespace buzz {
+
+VoicemailJidRequester::VoicemailJidRequester(talk_base::Task* parent,
+ const Jid& their_jid,
+ const Jid& my_jid) : Task(parent),
+ their_jid_(their_jid),
+ my_jid_(my_jid),
+ done_with_query_(false) {
+ parent_ = parent;
+}
+
+int VoicemailJidRequester::ProcessStart() {
+ // Start first query to node='voicemail'
+ DiscoItemsQueryTask* disco_items_task = new DiscoItemsQueryTask(this,
+ STR_VOICEMAIL, their_jid_.BareJid());
+ disco_items_task->SignalGotDiscoItems.connect(this,
+ &VoicemailJidRequester::OnFirstVoicemailJidSuccess);
+ disco_items_task->SignalDiscoItemsError.connect(this,
+ &VoicemailJidRequester::OnFirstVoicemailJidError);
+ disco_items_task->Start();
+ return STATE_BLOCKED;
+}
+
+void VoicemailJidRequester::OnFirstVoicemailJidError(buzz::Jid jid,
+ const XmlElement* xml_element) {
+ // First query gave us an error - try second query to node='outgoingvoicemail'
+ // and send it to your own jid
+ StartSecondQuery();
+}
+
+void VoicemailJidRequester::OnFirstVoicemailJidSuccess(buzz::Jid jid,
+ const XmlElement* xml_element) {
+ // Process the XML and fire the appropriate signals. If the xml was valid,
+ // then we're done with queries. If it wasn't valid, then start the second
+ // query.
+ bool valid_xml = ProcessVoicemailXml(xml_element);
+ if (valid_xml) {
+ done_with_query_ = true;
+ Wake();
+ } else {
+ StartSecondQuery();
+ }
+}
+
+void VoicemailJidRequester::OnSecondVoicemailJidError(buzz::Jid jid,
+ const XmlElement* xml_element) {
+ SignalVoicemailJidError(their_jid_);
+ done_with_query_ = true;
+ Wake();
+}
+
+void VoicemailJidRequester::OnSecondVoicemailJidSuccess(buzz::Jid jid,
+ const XmlElement* xml_element) {
+ // Whether this is good xml or bad, we're still done with the query
+ bool valid_xml = ProcessVoicemailXml(xml_element);
+ if (!valid_xml) {
+ SignalVoicemailJidError(their_jid_);
+ }
+ done_with_query_ = true;
+ Wake();
+}
+
+
+void VoicemailJidRequester::StartSecondQuery() {
+ // Send a query to your own jid to get the voicemail jid
+ DiscoItemsQueryTask* disco_items_task = new DiscoItemsQueryTask(this,
+ STR_OUTGOINGVOICEMAIL, my_jid_.BareJid());
+ disco_items_task->SignalGotDiscoItems.connect(this,
+ &VoicemailJidRequester::OnSecondVoicemailJidSuccess);
+ disco_items_task->SignalDiscoItemsError.connect(this,
+ &VoicemailJidRequester::OnSecondVoicemailJidError);
+ disco_items_task->Start();
+}
+
+int VoicemailJidRequester::Process(int state) {
+ if (done_with_query_) {
+ return STATE_DONE;
+ } else {
+ return talk_base::Task::Process(state);
+ }
+}
+
+bool VoicemailJidRequester::ProcessVoicemailXml(const XmlElement* xml_element) {
+ if (!xml_element) {
+ return false;
+ }
+ const std::string& node_name = xml_element->Attr(QN_NODE);
+ // Verify that it's one of the two nodes - we don't really care which one
+ if (node_name != "voicemail" &&
+ node_name != "outgoingvoicemail") {
+ return false;
+ }
+
+ const XmlElement* item = xml_element->FirstNamed(QN_DISCO_ITEM);
+ if (item) {
+ const std::string& jid_str = item->Attr(QN_JID);
+ buzz::Jid voicemail_jid(jid_str);
+ SignalGotVoicemailJid(their_jid_, voicemail_jid);
+ return true;
+ }
+ return false;
+}
+}
diff --git a/third_party/libjingle/source/talk/examples/call/voicemailjidrequester.h b/third_party/libjingle/source/talk/examples/call/voicemailjidrequester.h
new file mode 100644
index 0000000..34b3c4b
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/call/voicemailjidrequester.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.
+ */
+
+// VoicemailJidRequester wraps the requesting of voicemail jids for a user.
+//
+// To request a voicemail jid, we first set off a query to the user's bare jid
+// that looks like this:
+//
+// <iq type='get'
+// from='foo@gmail.com/asdf'
+// to='bar@google.com'
+// id='1234'>
+// <query xmlns=' http://jabber.org/protocol/disco#items'
+// node='voicemail '/>
+// </iq>
+//
+// If foo@gmail.com's server supports voicemail, it'll return this, and forward
+// the jid up to phoneapp. We do not do the second query.
+//
+// <iq type='result'
+// from='foo@google.com'
+// to='bar@google.com/asdf'
+// id='1234'>
+// <query xmlns=' http://jabber.org/protocol/disco#items '
+// node=' voicemail '>
+// <item jid='bar@google.com/voicemail '/>
+// </query>
+// </iq>
+//
+// If we get an error, we spin off a new request:
+//
+// <iq type='get'
+// from='foo@google.com/asdf'
+// to='foo@google.com'
+// id='1234'>
+// <query xmlns=' http://jabber.org/protocol/disco#items'
+// node='outgoingvoicemail '/>
+// </iq>
+//
+// If both of these return errors, we then forward the request to phoneapp.
+
+#ifndef TALK_EXAMPLES_CALL_VOICEMAILJIDREQUESTER_H_
+#define TALK_EXAMPLES_CALL_VOICEMAILJIDREQUESTER_H_
+
+#include "talk/xmpp/xmpptask.h"
+
+namespace buzz {
+
+class Task;
+
+class VoicemailJidRequester : public sigslot::has_slots<>,
+ public talk_base::Task {
+ public:
+ VoicemailJidRequester(talk_base::Task* parent, const Jid& their_jid, const Jid& my_jid);
+
+ // Provides the target jid and the voicemail to reach it
+ sigslot::signal2<const Jid&, const Jid&> SignalGotVoicemailJid;
+ sigslot::signal1<const Jid&> SignalVoicemailJidError;
+
+ virtual int ProcessStart();
+ protected:
+
+ virtual int Process(int state);
+
+ private:
+ // The first query (to node='voicemail' has returned an error) - we now spin
+ // off a request to node='outgoingvoicemail')
+ void OnFirstVoicemailJidError(buzz::Jid jid, const XmlElement* xml_element);
+
+ // The first query (to node='voicemail' has returned a successfully)
+ void OnFirstVoicemailJidSuccess(buzz::Jid jid, const XmlElement* xml_element);
+
+ // The second query (to node='outgoingvoicemail') has returned an error -
+ // nothing we can do now, just fire our error signal
+ void OnSecondVoicemailJidError(buzz::Jid jid, const XmlElement* xml_element);
+
+ // The second query (to node='outgoingvoicemail') has returned a successfully
+ void OnSecondVoicemailJidSuccess(buzz::Jid jid,
+ const XmlElement* xml_element);
+
+ // Parse the xml, fire SignalGotVoicemail jid if it was valid (and had a jid)
+ // and return true if it was a valid xml.
+ bool ProcessVoicemailXml(const XmlElement* xml_element);
+
+ // Send a query to your own jid to get the voicemail jid. This is used after
+ // the first query fails.
+ void StartSecondQuery();
+
+ talk_base::Task* parent_;
+
+ Jid their_jid_;
+
+ // Your own jid (not the other user's)
+ Jid my_jid_;
+
+ // A flag indicating whether or not we're done with the query so that we can
+ // set the state correctly in Process(int state)
+ bool done_with_query_;
+};
+}
+
+#endif // TALK_EXAMPLES_CALL_VOICEMAILJIDREQUESTER_H_
diff --git a/third_party/libjingle/source/talk/examples/login/login_main.cc b/third_party/libjingle/source/talk/examples/login/login_main.cc
new file mode 100644
index 0000000..186020a
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/login/login_main.cc
@@ -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.
+ */
+
+#include <cstdio>
+#include <iostream>
+
+#include "talk/base/thread.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/examples/login/xmppthread.h"
+
+int main(int argc, char **argv) {
+ std::cout << "Auth Cookie: ";
+ std::string auth_cookie;
+ std::getline(std::cin, auth_cookie);
+
+ std::cout << "User Name: ";
+ std::string username;
+ std::getline(std::cin, username);
+
+ // Start xmpp on a different thread
+ XmppThread thread;
+ thread.Start();
+
+ buzz::XmppClientSettings xcs;
+ xcs.set_user(username.c_str());
+ xcs.set_host("gmail.com");
+ xcs.set_use_tls(false);
+ xcs.set_auth_cookie(auth_cookie.c_str());
+ xcs.set_server(talk_base::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/third_party/libjingle/source/talk/examples/login/xmppauth.cc b/third_party/libjingle/source/talk/examples/login/xmppauth.cc
new file mode 100644
index 0000000..3551b97
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/login/xmppauth.cc
@@ -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.
+ */
+
+#include "talk/examples/login/xmppauth.h"
+
+#include <algorithm>
+
+#include "talk/xmpp/saslcookiemechanism.h"
+#include "talk/xmpp/saslplainmechanism.h"
+
+XmppAuth::XmppAuth() : done_(false) {
+}
+
+XmppAuth::~XmppAuth() {
+}
+
+void XmppAuth::StartPreXmppAuth(const buzz::Jid & jid,
+ const talk_base::SocketAddress & server,
+ const talk_base::CryptString & pass,
+ const std::string & auth_cookie) {
+ jid_ = jid;
+ passwd_ = pass;
+ auth_cookie_ = auth_cookie;
+ 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() && !auth_cookie_.empty())
+ 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() && !auth_cookie_.empty())
+ return "X-GOOGLE-COOKIE";
+
+ 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/third_party/libjingle/source/talk/examples/login/xmppauth.h b/third_party/libjingle/source/talk/examples/login/xmppauth.h
new file mode 100644
index 0000000..18672b8
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/login/xmppauth.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 _XMPPAUTH_H_
+#define _XMPPAUTH_H_
+
+#include <vector>
+
+#include "talk/base/cryptstring.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/saslhandler.h"
+#include "talk/xmpp/prexmppauth.h"
+
+class XmppAuth: public buzz::PreXmppAuth {
+public:
+ XmppAuth();
+ virtual ~XmppAuth();
+
+ virtual void StartPreXmppAuth(const buzz::Jid & jid,
+ const talk_base::SocketAddress & server,
+ const talk_base::CryptString & pass,
+ const std::string & auth_cookie);
+
+ virtual bool IsAuthDone() const { return done_; }
+ virtual bool IsAuthorized() const { return true; }
+ virtual bool HadError() const { return false; }
+ virtual int GetError() const { return 0; }
+ virtual buzz::CaptchaChallenge GetCaptchaChallenge() const {
+ return buzz::CaptchaChallenge();
+ }
+ virtual std::string GetAuthCookie() const { 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_;
+ talk_base::CryptString passwd_;
+ std::string auth_cookie_;
+ bool done_;
+};
+
+#endif
diff --git a/third_party/libjingle/source/talk/examples/login/xmpppump.cc b/third_party/libjingle/source/talk/examples/login/xmpppump.cc
new file mode 100644
index 0000000..de4057b
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/login/xmpppump.cc
@@ -0,0 +1,78 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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);
+ if (!AllChildrenDone()) {
+ client_->SignalStateChange.connect(this, &XmppPump::OnStateChange);
+ client_->Connect(xcs, "", socket, auth);
+ client_->Start();
+ }
+}
+
+void XmppPump::DoDisconnect() {
+ if (!AllChildrenDone())
+ 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() {
+ talk_base::Thread::Current()->Post(this);
+}
+
+int64 XmppPump::CurrentTime() {
+ return (int64)talk_base::Time();
+}
+
+void XmppPump::OnMessage(talk_base::Message *pmsg) {
+ RunTasks();
+}
+
+buzz::XmppReturnStatus XmppPump::SendStanza(const buzz::XmlElement *stanza) {
+ if (!AllChildrenDone())
+ return client_->SendStanza(stanza);
+ return buzz::XMPP_RETURN_BADSTATE;
+}
diff --git a/third_party/libjingle/source/talk/examples/login/xmpppump.h b/third_party/libjingle/source/talk/examples/login/xmpppump.h
new file mode 100644
index 0000000..4e79748
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/login/xmpppump.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 _XMPPPUMP_H_
+#define _XMPPPUMP_H_
+
+#include "talk/base/messagequeue.h"
+#include "talk/base/taskrunner.h"
+#include "talk/base/thread.h"
+#include "talk/base/time.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+
+// Simple xmpp pump
+
+class XmppPumpNotify {
+public:
+ virtual ~XmppPumpNotify() {}
+ virtual void OnStateChange(buzz::XmppEngine::State state) = 0;
+};
+
+class XmppPump : public talk_base::MessageHandler, public talk_base::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();
+
+ int64 CurrentTime();
+
+ void OnMessage(talk_base::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/third_party/libjingle/source/talk/examples/login/xmppsocket.cc b/third_party/libjingle/source/talk/examples/login/xmppsocket.cc
new file mode 100644
index 0000000..89e8662
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/login/xmppsocket.cc
@@ -0,0 +1,249 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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_CONFIG_H
+#include <config.h>
+#endif
+
+#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/ssladapter.h"
+#endif
+#include "xmppsocket.h"
+
+#ifdef USE_SSLSTREAM
+#include "talk/base/socketstream.h"
+#ifdef FEATURE_ENABLE_SSL
+#include "talk/base/sslstreamadapter.h"
+#endif // FEATURE_ENABLE_SSL
+#endif // USE_SSLSTREAM
+
+XmppSocket::XmppSocket(bool tls) : tls_(tls) {
+ talk_base::Thread* pth = talk_base::Thread::Current();
+ talk_base::AsyncSocket* socket =
+ pth->socketserver()->CreateAsyncSocket(SOCK_STREAM);
+#ifndef USE_SSLSTREAM
+#ifdef FEATURE_ENABLE_SSL
+ if (tls_) {
+ socket = talk_base::SSLAdapter::Create(socket);
+ }
+#endif // FEATURE_ENABLE_SSL
+ cricket_socket_ = socket;
+ cricket_socket_->SignalReadEvent.connect(this, &XmppSocket::OnReadEvent);
+ cricket_socket_->SignalWriteEvent.connect(this, &XmppSocket::OnWriteEvent);
+ cricket_socket_->SignalConnectEvent.connect(this,
+ &XmppSocket::OnConnectEvent);
+ cricket_socket_->SignalCloseEvent.connect(this, &XmppSocket::OnCloseEvent);
+#else // USE_SSLSTREAM
+ cricket_socket_ = socket;
+ stream_ = new talk_base::SocketStream(cricket_socket_);
+#ifdef FEATURE_ENABLE_SSL
+ if (tls_)
+ stream_ = talk_base::SSLStreamAdapter::Create(stream_);
+#endif // FEATURE_ENABLE_SSL
+ stream_->SignalEvent.connect(this, &XmppSocket::OnEvent);
+#endif // USE_SSLSTREAM
+
+ state_ = buzz::AsyncSocket::STATE_CLOSED;
+}
+
+XmppSocket::~XmppSocket() {
+ Close();
+#ifndef USE_SSLSTREAM
+ delete cricket_socket_;
+#else // USE_SSLSTREAM
+ delete stream_;
+#endif // USE_SSLSTREAM
+}
+
+#ifndef USE_SSLSTREAM
+void XmppSocket::OnReadEvent(talk_base::AsyncSocket * socket) {
+ SignalRead();
+}
+
+void XmppSocket::OnWriteEvent(talk_base::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(talk_base::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();
+}
+
+void XmppSocket::OnCloseEvent(talk_base::AsyncSocket * socket, int error) {
+ SignalCloseEvent(error);
+}
+
+#else // USE_SSLSTREAM
+
+void XmppSocket::OnEvent(talk_base::StreamInterface* stream,
+ int events, int err) {
+ if ((events & talk_base::SE_OPEN)) {
+#if defined(FEATURE_ENABLE_SSL)
+ if (state_ == buzz::AsyncSocket::STATE_TLS_CONNECTING) {
+ state_ = buzz::AsyncSocket::STATE_TLS_OPEN;
+ SignalSSLConnected();
+ events |= talk_base::SE_WRITE;
+ } else
+#endif
+ {
+ state_ = buzz::AsyncSocket::STATE_OPEN;
+ SignalConnected();
+ }
+ }
+ if ((events & talk_base::SE_READ))
+ SignalRead();
+ if ((events & talk_base::SE_WRITE)) {
+ // Write bytes if there are any
+ while (buffer_.Length() != 0) {
+ talk_base::StreamResult result;
+ size_t written;
+ int error;
+ result = stream_->Write(buffer_.Data(), buffer_.Length(),
+ &written, &error);
+ if (result == talk_base::SR_ERROR) {
+ LOG(LS_ERROR) << "Send error: " << error;
+ return;
+ }
+ if (result == talk_base::SR_BLOCK)
+ return;
+ ASSERT(result == talk_base::SR_SUCCESS);
+ ASSERT(written > 0);
+ buffer_.Shift(written);
+ }
+ }
+ if ((events & talk_base::SE_CLOSE))
+ SignalCloseEvent(err);
+}
+#endif // USE_SSLSTREAM
+
+buzz::AsyncSocket::State XmppSocket::state() {
+ return state_;
+}
+
+buzz::AsyncSocket::Error XmppSocket::error() {
+ return buzz::AsyncSocket::ERROR_NONE;
+}
+
+int XmppSocket::GetError() {
+ return 0;
+}
+
+bool XmppSocket::Connect(const talk_base::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) {
+#ifndef USE_SSLSTREAM
+ int read = cricket_socket_->Recv(data, len);
+ if (read > 0) {
+ *len_read = (size_t)read;
+ return true;
+ }
+#else // USE_SSLSTREAM
+ talk_base::StreamResult result = stream_->Read(data, len, len_read, NULL);
+ if (result == talk_base::SR_SUCCESS)
+ return true;
+#endif // USE_SSLSTREAM
+ return false;
+}
+
+bool XmppSocket::Write(const char * data, size_t len) {
+ buffer_.WriteBytes(data, len);
+#ifndef USE_SSLSTREAM
+ OnWriteEvent(cricket_socket_);
+#else // USE_SSLSTREAM
+ OnEvent(stream_, talk_base::SE_WRITE, 0);
+#endif // USE_SSLSTREAM
+ return true;
+}
+
+bool XmppSocket::Close() {
+ if (state_ != buzz::AsyncSocket::STATE_OPEN)
+ return false;
+#ifndef USE_SSLSTREAM
+ if (cricket_socket_->Close() == 0) {
+ state_ = buzz::AsyncSocket::STATE_CLOSED;
+ SignalClosed();
+ return true;
+ }
+ return false;
+#else // USE_SSLSTREAM
+ state_ = buzz::AsyncSocket::STATE_CLOSED;
+ stream_->Close();
+ SignalClosed();
+ return true;
+#endif // USE_SSLSTREAM
+}
+
+bool XmppSocket::StartTls(const std::string & domainname) {
+#if defined(FEATURE_ENABLE_SSL)
+ if (!tls_)
+ return false;
+#ifndef USE_SSLSTREAM
+ talk_base::SSLAdapter* ssl_adapter =
+ static_cast<talk_base::SSLAdapter *>(cricket_socket_);
+ ssl_adapter->set_ignore_bad_cert(true);
+ if (ssl_adapter->StartSSL(domainname.c_str(), false) != 0)
+ return false;
+#else // USE_SSLSTREAM
+ talk_base::SSLStreamAdapter* ssl_stream =
+ static_cast<talk_base::SSLStreamAdapter *>(stream_);
+ ssl_stream->set_ignore_bad_cert(true);
+ if (ssl_stream->StartSSLWithServer(domainname.c_str()) != 0)
+ return false;
+#endif // USE_SSLSTREAM
+ state_ = buzz::AsyncSocket::STATE_TLS_CONNECTING;
+ return true;
+#else // !defined(FEATURE_ENABLE_SSL)
+ return false;
+#endif // !defined(FEATURE_ENABLE_SSL)
+}
diff --git a/third_party/libjingle/source/talk/examples/login/xmppsocket.h b/third_party/libjingle/source/talk/examples/login/xmppsocket.h
new file mode 100644
index 0000000..bce8ee3
--- /dev/null
+++ b/third_party/libjingle/source/talk/examples/login/xmppsocket.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 _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"
+
+// The below define selects the SSLStreamAdapter implementation for
+// SSL, as opposed to the SSLAdapter socket adapter.
+// #define USE_SSLSTREAM
+
+namespace talk_base {
+ class StreamInterface;
+};
+extern talk_base::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 int GetError();
+
+ virtual bool Connect(const talk_base::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);
+
+ sigslot::signal1<int> SignalCloseEvent;
+
+private:
+#ifndef USE_SSLSTREAM
+ void OnReadEvent(talk_base::AsyncSocket * socket);
+ void OnWriteEvent(talk_base::AsyncSocket * socket);
+ void OnConnectEvent(talk_base::AsyncSocket * socket);
+ void OnCloseEvent(talk_base::AsyncSocket * socket, int error);
+#else // USE_SSLSTREAM
+ void OnEvent(talk_base::StreamInterface* stream, int events, int err);
+#endif // USE_SSLSTREAM
+
+ talk_base::AsyncSocket * cricket_socket_;
+#ifdef USE_SSLSTREAM
+ talk_base::StreamInterface *stream_;
+#endif // USE_SSLSTREAM
+ buzz::AsyncSocket::State state_;
+ talk_base::ByteBuffer buffer_;
+ bool tls_;
+};
+
+#endif // _XMPPSOCKET_H_
diff --git a/third_party/libjingle/source/talk/examples/login/xmppthread.cc b/third_party/libjingle/source/talk/examples/login/xmppthread.cc
new file mode 100644
index 0000000..bb5d8cd
--- /dev/null
+++ b/third_party/libjingle/source/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 talk_base::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::ProcessMessages(int cms) {
+ talk_base::Thread::ProcessMessages(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(talk_base::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/third_party/libjingle/source/talk/examples/login/xmppthread.h b/third_party/libjingle/source/talk/examples/login/xmppthread.h
new file mode 100644
index 0000000..247b7bd
--- /dev/null
+++ b/third_party/libjingle/source/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 talk_base::Thread, XmppPumpNotify, talk_base::MessageHandler {
+public:
+ XmppThread();
+ ~XmppThread();
+
+ buzz::XmppClient* client() { return pump_->client(); }
+
+ void ProcessMessages(int cms);
+
+ void Login(const buzz::XmppClientSettings & xcs);
+ void Disconnect();
+
+private:
+ XmppPump* pump_;
+
+ void OnStateChange(buzz::XmppEngine::State state);
+ void OnMessage(talk_base::Message* pmsg);
+};
+
+#endif // _XMPPTHREAD_H_
diff --git a/third_party/libjingle/source/talk/libjingle.scons b/third_party/libjingle/source/talk/libjingle.scons
new file mode 100644
index 0000000..a6e1d62
--- /dev/null
+++ b/third_party/libjingle/source/talk/libjingle.scons
@@ -0,0 +1,204 @@
+import talk
+Import("env")
+
+talk.Library(env, name = "expat",
+ srcs = [
+ "third_party/expat/v2_0_1/Source/lib/xmlparse.c",
+ "third_party/expat/v2_0_1/Source/lib/xmlrole.c",
+ "third_party/expat/v2_0_1/Source/lib/xmltok.c",
+ "third_party/expat/v2_0_1/Source/lib/xmltok_impl.c",
+ "third_party/expat/v2_0_1/Source/lib/xmltok_ns.c",
+ ],
+ CPPDEFINES = [
+ "HAVE_BCOPY",
+ ],
+)
+talk.Library(env, name = "libjingle",
+ lin_srcs = [
+ "base/linux.cc",
+ ],
+ mac_srcs = [
+ "base/macconversion.cc",
+ "base/macutils.cc",
+ ],
+ posix_srcs = [
+ "base/unixfilesystem.cc",
+ ],
+ CPPDEFINES = [
+ "FEATURE_ENABLE_VOICEMAIL",
+ "FEATURE_ENABLE_SSL",
+ "SSL_USE_OPENSSL",
+ "HAVE_OPENSSL_SSL_H=1",
+ ],
+ srcs = [
+ "base/asynchttprequest.cc",
+ "base/asyncpacketsocket.cc",
+ "base/asynctcpsocket.cc",
+ "base/asyncudpsocket.cc",
+ "base/autodetectproxy.cc",
+ "base/base64.cc",
+ "base/bytebuffer.cc",
+ "base/checks.cc",
+ "base/common.cc",
+ "base/diskcache.cc",
+ "base/event.cc",
+ "base/fileutils.cc",
+ "base/firewallsocketserver.cc",
+ "base/flags.cc",
+ "base/helpers.cc",
+ "base/host.cc",
+ "base/httpbase.cc",
+ "base/httpclient.cc",
+ "base/httpcommon.cc",
+ "base/httprequest.cc",
+ "base/logging.cc",
+ "base/md5c.c",
+ "base/messagehandler.cc",
+ "base/messagequeue.cc",
+ "base/nethelpers.cc",
+ "base/network.cc",
+ "base/openssladapter.cc",
+ "base/opensslidentity.cc",
+ "base/opensslstreamadapter.cc",
+ "base/pathutils.cc",
+ "base/physicalsocketserver.cc",
+ "base/proxydetect.cc",
+ "base/proxyinfo.cc",
+ "base/signalthread.cc",
+ "base/socketadapters.cc",
+ "base/socketaddress.cc",
+ "base/socketpool.cc",
+ "base/ssladapter.cc",
+ "base/sslidentity.cc",
+ "base/sslsocketfactory.cc",
+ "base/sslstreamadapter.cc",
+ "base/stream.cc",
+ "base/stringdigest.cc",
+ "base/stringencode.cc",
+ "base/stringutils.cc",
+ "base/task.cc",
+ "base/taskparent.cc",
+ "base/taskrunner.cc",
+ "base/thread.cc",
+ "base/time.cc",
+ "base/urlencode.cc",
+ "p2p/base/constants.cc",
+ "p2p/base/p2ptransport.cc",
+ "p2p/base/p2ptransportchannel.cc",
+ "p2p/base/parsing.cc",
+ "p2p/base/port.cc",
+ "p2p/base/pseudotcp.cc",
+ "p2p/base/relayport.cc",
+ "p2p/base/rawtransport.cc",
+ "p2p/base/rawtransportchannel.cc",
+ "p2p/base/session.cc",
+ "p2p/base/sessionmanager.cc",
+ "p2p/base/sessionmessages.cc",
+ "p2p/base/stun.cc",
+ "p2p/base/stunport.cc",
+ "p2p/base/stunrequest.cc",
+ "p2p/base/tcpport.cc",
+ "p2p/base/transport.cc",
+ "p2p/base/transportchannel.cc",
+ "p2p/base/transportchannelproxy.cc",
+ "p2p/base/udpport.cc",
+ "p2p/client/basicportallocator.cc",
+ "p2p/client/httpportallocator.cc",
+ "p2p/client/socketmonitor.cc",
+ "session/tunnel/pseudotcpchannel.cc",
+ "session/tunnel/tunnelsessionclient.cc",
+ "session/tunnel/securetunnelsessionclient.cc",
+ "session/phone/audiomonitor.cc",
+ "session/phone/call.cc",
+ "session/phone/channel.cc",
+ "session/phone/channelmanager.cc",
+ "session/phone/codec.cc",
+ "session/phone/devicemanager.cc",
+ "session/phone/mediaengine.cc",
+ "session/phone/mediamonitor.cc",
+ "session/phone/mediasessionclient.cc",
+ "session/phone/soundclip.cc",
+ "session/phone/srtpfilter.cc",
+ "session/phone/v4llookup.cc",
+ "xmllite/qname.cc",
+ "xmllite/xmlbuilder.cc",
+ "xmllite/xmlconstants.cc",
+ "xmllite/xmlelement.cc",
+ "xmllite/xmlnsstack.cc",
+ "xmllite/xmlparser.cc",
+ "xmllite/xmlprinter.cc",
+ "xmpp/constants.cc",
+ "xmpp/jid.cc",
+ "xmpp/ratelimitmanager.cc",
+ "xmpp/saslmechanism.cc",
+ "xmpp/xmppclient.cc",
+ "xmpp/xmppengineimpl.cc",
+ "xmpp/xmppengineimpl_iq.cc",
+ "xmpp/xmpplogintask.cc",
+ "xmpp/xmppstanzaparser.cc",
+ "xmpp/xmpptask.cc",
+ ],
+ win_srcs = [
+ "base/schanneladapter.cc",
+ "base/win32.cc",
+ "base/win32filesystem.cc",
+ "base/win32securityerrors.cc",
+ "base/win32socketserver.cc",
+ "base/win32socketinit.cc",
+ "base/win32window.cc",
+ "base/winfirewall.cc",
+ "base/winping.cc",
+ ],
+)
+talk.App(env, name = "login",
+ libs = [
+ "libjingle",
+ "expat",
+ "libxmpphelp",
+ ],
+ srcs = [
+ "examples/login/xmppthread.cc",
+ "examples/login/login_main.cc",
+ ],
+ lin_libs = [
+ "libpthread",
+ ":libssl.so.0.9.8",
+ ],
+)
+talk.Library(env, name = "libxmpphelp",
+ libs = [
+ "libjingle",
+ ],
+ srcs = [
+ "examples/login/xmppauth.cc",
+ "examples/login/xmpppump.cc",
+ "examples/login/xmppsocket.cc",
+ ],
+)
+talk.App(env, name = "call",
+ libs = [
+ "libjingle",
+ "expat",
+ "libxmpphelp",
+ ],
+ srcs = [
+ "examples/call/call_main.cc",
+ "examples/call/callclient.cc",
+ "examples/call/console.cc",
+ "examples/call/discoitemsquerytask.cc",
+ "examples/call/friendinvitesendtask.cc",
+ "examples/call/mucinviterecvtask.cc",
+ "examples/call/mucinvitesendtask.cc",
+ "examples/call/presenceouttask.cc",
+ "examples/call/presencepushtask.cc",
+ "examples/call/voicemailjidrequester.cc",
+ ],
+ CPPDEFINES = [
+ "FEATURE_ENABLE_VOICEMAIL",
+ ],
+ lin_libs = [
+ "libasound",
+ "libpthread",
+ ":libssl.so.0.9.8",
+ ],
+)
diff --git a/third_party/libjingle/source/talk/main.scons b/third_party/libjingle/source/talk/main.scons
new file mode 100644
index 0000000..b14d978
--- /dev/null
+++ b/third_party/libjingle/source/talk/main.scons
@@ -0,0 +1,434 @@
+# -*- Python -*-
+# This is the main file of the hammer/scons build for magicflute.
+#
+# All the helper functions are defined in:
+# - site_scons/talk.py
+# Use 'import talk' in any .scons file to get access to it.
+# Add any new helper functions to it; unittest are available
+# in talk_unittest.py.
+#
+# Each 'component' that is built is defined in a .scons file.
+# See talk.Components(...) for further info on file naming convention.
+#
+# To add a new platform clone and modify the root_env object. Remember to add
+# the new environment object to the envs list otherwise it will not be included
+# in the build.
+#
+#
+# For more documentation and information check:
+# https://sites.google.com/a/google.com/wavelet/Home/Magic-Flute--RTC-Engine-
+#
+import talk
+import os
+
+#-------------------------------------------------------------------------------
+# The build files/directories to 'build'.
+# If the name is the name of a directory then that directory shall contain a
+# .scons file with the same name as the directory itself:
+# Ex: The directory magicflute contains a file called magicflute.scons
+#
+components = talk.Components("libjingle.scons")
+
+#-------------------------------------------------------------------------------
+# Build environments
+#
+
+# The list of build environments.
+envs = []
+
+# The root of all builds.
+root_env = Environment(
+ tools = [
+ 'component_bits',
+ 'component_setup',
+ 'replace_strings',
+ 'talk_noops',
+ ],
+ BUILD_SCONSCRIPTS = components,
+ DESTINATION_ROOT = '$MAIN_DIR/build',
+ CPPPATH = [
+ '$OBJ_ROOT', # generated headers are relative to here
+ '$MAIN_DIR/..', # TODO(dape): how can we use GOOGLECLIENT instead?
+ '$GOOGLE3', # google3 headers are relative to here
+ ],
+ CPPDEFINES = [
+ # Temp flag while porting to hammer.
+ 'HAMMER_TIME=1',
+ 'LOGGING=1',
+
+ # Feature selection
+ 'FEATURE_ENABLE_SSL',
+ 'FEATURE_ENABLE_VOICEMAIL',
+ 'FEATURE_ENABLE_PSTN',
+ 'HAVE_GIPS',
+ 'HAVE_LMI',
+ ]
+)
+
+#-------------------------------------------------------------------------------
+# W I N D O W S
+#
+win_env = root_env.Clone(
+ tools = [
+ 'atlmfc_vc80',
+ 'component_targets_msvs',
+ 'directx_9_0_c',
+ #'grid_builder',
+ 'midl',
+ 'target_platform_windows',
+ ],
+ # Don't use default vc80 midl.exe. It doesn't understand vista_sdk idl files.
+ MIDL = '$PLATFORM_SDK_VISTA_6_0_DIR/Bin/midl.exe ',
+ WIX_DIR = '$GOOGLECLIENT/third_party/wix/v3_0_2925/files',
+)
+
+win_env.Append(
+ COMPONENT_LIBRARY_PUBLISH = True, # Put dlls in output dir too
+ CCFLAGS = [
+ '/nologo',
+ '/W3', # level 3 warnings
+ '/WX', # warnings are errors
+ '/Zc:forScope', # handle 'for (int i = 0 ...)' right
+ '/GS', # enable stack smash protection
+ '/EHs-c-', # disable C++ EH
+ '/GR-', # disable RTTI
+ '/wd4996', # ignore POSIX deprecated warnings
+
+ # promote certain level 4 warnings
+ '/w14701', # potentially uninitialized var
+ '/w14702', # unreachable code
+ '/w14706', # assignment within a conditional
+ '/w14709', # comma operator within array index
+ '/w14063', # case 'identifier' is not a valid value for switch of enum
+ '/w14064', # switch of incomplete enum 'enumeration'
+ '/w14057', # 'identifier1' indirection to slightly different base
+ # types from 'identifier2'
+ '/w14263', # member function does not override any base class virtual
+ # member function
+ '/w14266', # no override available for virtual memberfunction from base
+ # 'type'; function is hidden
+ '/w14296', # expression is always false
+ '/w14355', # 'this' : used in base member initializer list
+ ],
+ CPPDEFINES = [
+ '_ATL_CSTRING_EXPLICIT_CONSTRUCTORS',
+ # TODO(dape): encapsulate all string operations that are not based
+ # on std::string/std::wstring and make sure we use the safest versions
+ # available on all platforms.
+ '_CRT_SECURE_NO_WARNINGS',
+ '_SCL_SECURE_NO_WARNINGS',
+ '_USE_32BIT_TIME_T',
+ '_UNICODE',
+ 'UNICODE',
+ '_HAS_EXCEPTIONS=0',
+ 'WIN32',
+ # TODO(dape): remove this from logging.cc and enable here instead.
+ #'WIN32_LEAN_AND_MEAN',
+
+ 'WINVER=0x0500',
+ '_WIN32_WINNT=0x0501',
+ '_WIN32_IE=0x0501',
+ # The Vista platform SDK 6.0 needs at least
+ # this NTDDI version or else the headers
+ # that LMI includes from it won't compile.
+ 'NTDDI_VERSION=NTDDI_WINXP',
+ ],
+ CPPPATH = [
+ '$THIRD_PARTY/wtl_71/include',
+ '$PLATFORM_SDK_VISTA_6_0_DIR/Include',
+ ],
+ LIBPATH = [
+ '$PLATFORM_SDK_VISTA_6_0_DIR/Lib'
+ ],
+ LINKFLAGS = [
+ '-manifest' # TODO(thaloun): Why do we need this?
+ ],
+ MIDLFLAGS = [
+ '/win32',
+ '/I$PLATFORM_SDK_VISTA_6_0_DIR/include'
+ ]
+)
+
+# TODO(dape): Figure out what this does; found it in
+# omaha/main.scons. This fixes the problem with redefinition
+# of OS_WINDOWS symbol.
+win_env.FilterOut(CPPDEFINES = ['OS_WINDOWS=OS_WINDOWS'])
+
+win_dbg_env = win_env.Clone(
+ BUILD_TYPE = 'dbg',
+ BUILD_TYPE_DESCRIPTION = 'Windows debug build',
+ BUILD_GROUPS = ['default', 'all'],
+ tools = ['target_debug']
+)
+envs.append(win_dbg_env)
+
+win_opt_env = win_env.Clone(
+ BUILD_TYPE = 'opt',
+ BUILD_TYPE_DESCRIPTION = 'Windows opt build',
+ BUILD_GROUPS = ['all'],
+ tools = ['target_optimized']
+)
+envs.append(win_opt_env)
+
+
+#-------------------------------------------------------------------------------
+# P O S I X
+#
+posix_env = root_env.Clone()
+posix_env.Append(
+ CPPDEFINES = [
+ 'HASHNAMESPACE=__gnu_cxx',
+ 'HASH_NAMESPACE=__gnu_cxx',
+ 'POSIX',
+ 'DISABLE_DYNAMIC_CAST',
+ 'HAVE_OPENSSL_SSL_H=1',
+ ],
+ CCFLAGS = [
+ '-m32',
+ '-Wall',
+ '-Werror',
+ '-Wno-switch',
+ '-fno-exceptions',
+ ],
+ CXXFLAGS = [
+ '-Wno-non-virtual-dtor',
+ '-Wno-ctor-dtor-privacy',
+ '-fno-rtti',
+ ],
+ LINKFLAGS = [
+ '-m32',
+ ],
+)
+
+#-------------------------------------------------------------------------------
+# M A C OSX
+#
+mac_env = posix_env.Clone(
+ tools = [
+ 'target_platform_mac',
+ #'talk_mac',
+ #'fill_plist',
+ ],
+)
+mac_env.Append(
+ CPPDEFINES = [
+ 'OSX',
+ 'MAC_OS_X_VERSION_MIN_REQUIRED=1040',
+ 'HAVE_SRTP',
+ ],
+ CCFLAGS = [
+ '-arch', 'i386',
+ '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk',
+ '-fasm-blocks',
+ ],
+ LINKFLAGS = [
+ '-Wl,-search_paths_first',
+ # This flag makes all members of a static library be included in the
+ # final exe - that increases the size of the exe, but without it
+ # Obj-C categories aren't properly included in the exe.
+ # TODO(thaloun): consider only defining for libs that actually have objc.
+ '-ObjC',
+ '-arch', 'i386',
+ ],
+ FRAMEWORKS = [
+ 'CoreServices',
+ 'Carbon',
+ 'Security',
+ 'SystemConfiguration',
+ ]
+)
+
+mac_dbg_env = mac_env.Clone(
+ BUILD_TYPE = 'dbg',
+ BUILD_TYPE_DESCRIPTION = 'Mac debug build',
+ BUILD_GROUPS = ['default', 'all'],
+ tools = ['target_debug']
+)
+mac_dbg_env.Append(
+ CPPDEFINES = [
+ 'FLAVOR_DBG',
+ 'ENABLE_DEBUG',
+ ],
+ CCFLAGS = [
+ '-O0',
+ ]
+)
+envs.append(mac_dbg_env)
+
+mac_opt_env = mac_env.Clone(
+ BUILD_TYPE = 'opt',
+ BUILD_TYPE_DESCRIPTION = 'Mac opt build',
+ BUILD_GROUPS = ['all'],
+ tools = ['target_optimized']
+)
+mac_opt_env.Append(
+ CCFLAGS = [
+ # TODO(thaloun): Figure out how mk can compile without
+ # this flag, then remove. Confirmed asserts are preprocessed
+ # out. Maybe it's a different version of gcc?
+ '-Wno-unused-variable',
+ ],
+)
+envs.append(mac_opt_env)
+
+#-------------------------------------------------------------------------------
+# L I N U X
+#
+linux_env = posix_env.Clone(
+ tools = ['target_platform_linux'],
+)
+
+linux_env.Append(
+ CPPDEFINES = [
+ 'LINUX',
+ 'HAVE_GLIB',
+ # TODO() Enable once we figure out multiple defines with gips lib
+ #'HAVE_SRTP',
+ # Also consider other linux flags: 64bit, no-strict-aliasing, wrap, etc
+ ],
+ LINKFLAGS = [
+ # TODO(tschmelcher) consider enabling gc-sections. Perhaps only in opt.
+ #'-Wl,--gc-sections',
+ '-Wl,--start-group',
+ ],
+ _LIBFLAGS = ['-Wl,--end-group'],
+)
+
+linux_dbg_env = linux_env.Clone(
+ BUILD_TYPE = 'dbg',
+ BUILD_TYPE_DESCRIPTION = 'Linux debug build',
+ BUILD_GROUPS = ['default', 'all'],
+ tools = ['target_debug']
+)
+envs.append(linux_dbg_env)
+
+linux_opt_env = linux_env.Clone(
+ BUILD_TYPE = 'opt',
+ BUILD_TYPE_DESCRIPTION = 'Linux optimized build',
+ BUILD_GROUPS = ['all'],
+ tools = ['target_optimized']
+)
+envs.append(linux_opt_env)
+
+#-------------------------------------------------------------------------------
+# L I N U X -- C H R O M E -- O S
+#
+linux_chromeos_env = linux_env.Clone(
+ AR = os.environ.get("AR"),
+ AS = os.environ.get("AS"),
+ LD = os.environ.get("LD"),
+ NM = os.environ.get("NM"),
+ RANLIB = os.environ.get("RANLIB"),
+ CC = str(os.environ.get("CC")) +
+ ' --sysroot=' + str(os.environ.get("SYSROOT")),
+ CXX = str(os.environ.get("CXX")) +
+ ' --sysroot=' + str(os.environ.get("SYSROOT")),
+)
+
+linux_chromeos_env.Append(
+ CPPPATH = [
+ os.environ.get("CPPPATH"),
+ ],
+
+ LIBPATH = [
+ os.environ.get("LIBPATH"),
+ ],
+
+ CCFLAGS = [
+ os.environ.get("CCFLAGS"),
+ ],
+
+ CXXFLAGS = [
+ os.environ.get("CXXFLAGS"),
+ ],
+
+ RPATH = [
+ os.environ.get("RPATH"),
+ ],
+)
+
+DeclareBit('linux_chromeos', 'Chrome OS ebuild')
+linux_chromeos_env.SetBits('linux_chromeos')
+
+linux_chromeos_dbg_env = linux_chromeos_env.Clone(
+ BUILD_TYPE = 'chromeos-dbg',
+ BUILD_TYPE_DESCRIPTION = 'Chrome OS debug build',
+ BUILD_GROUPS = ['chromeos'],
+ tools = ['target_debug']
+)
+envs.append(linux_chromeos_dbg_env)
+
+linux_chromeos_opt_env = linux_chromeos_env.Clone(
+ BUILD_TYPE = 'chromeos-opt',
+ BUILD_TYPE_DESCRIPTION = 'Chrome OS optimized build',
+ BUILD_GROUPS = ['chromeos'],
+ tools = ['target_optimized']
+)
+envs.append(linux_chromeos_opt_env)
+
+
+# TODO(): Clone linux envs for 64bit. See 'variant' documentation.
+
+# Parse child .scons files
+BuildEnvironments(envs)
+
+# Explicitly set which targets to build when not stated on commandline
+Default(None)
+# Build the following, which excludes unit test output (ie running them)
+# To run unittests, specify the test to run, or run_all_tests. See -h option.
+Default(['all_libraries', 'all_programs', 'all_test_programs'])
+
+# .sln creation code lifted from googleclient/bar/main.scons. Must be after
+# the call to BuildEnvironments for all_foo aliases to be defined.
+# Run 'hammer --mode=all --vsproj' to generate
+DeclareBit('vsproj', 'Generate Visual Studio projects and solution files.')
+win_env.SetBitFromOption('vsproj', False)
+
+if win_env.Bit('vsproj'):
+ vs_env = win_env.Clone()
+ vs_env.Append(
+ COMPONENT_VS_SOURCE_SUFFIXES = [
+ '.def',
+ '.grd',
+ '.html',
+ '.idl',
+ '.mk',
+ '.txt',
+ '.py',
+ '.scons',
+ '.wxs.template',
+ ]
+ )
+
+ # Source project
+ p = vs_env.ComponentVSDirProject(
+ 'flute_source',
+ ['$MAIN_DIR', '$GOOGLE3', '$THIRD_PARTY'],
+ COMPONENT_VS_SOURCE_FOLDERS = [
+ # Files are assigned to first matching folder. Folder names of None
+ # are filters.
+ (None, '$DESTINATION_ROOT'),
+ ('flute', '$MAIN_DIR'),
+ ('google3', '$GOOGLE3'),
+ ('third_party', '$THIRD_PARTY'),
+ ],
+ # Force source project to main dir, so that Visual Studio can find the
+ # source files corresponding to build errors.
+ COMPONENT_VS_PROJECT_DIR = '$MAIN_DIR',
+ )
+ vs_env.AlwaysBuild(p)
+
+ # Solution and target projects
+ s = vs_env.ComponentVSSolution(
+ '../../flute',
+ ['all_libraries', 'all_programs', 'all_test_programs'],
+ projects = [p],
+ )
+
+ print '***Unfortunately the vsproj creator isn\'t smart enough to '
+ print '***automatically get the correct output locations. It is very easy'
+ print '***though to change it in the properties pane to the following'
+ print '***($SolutionDir)/build/<foo>/staging/<bar>.exe'
+ Default(None)
+ Default([s])
+
diff --git a/third_party/libjingle/source/talk/p2p/base/candidate.h b/third_party/libjingle/source/talk/p2p/base/candidate.h
new file mode 100644
index 0000000..f8b78ff
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/candidate.h
@@ -0,0 +1,137 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_CANDIDATE_H_
+#define TALK_P2P_BASE_CANDIDATE_H_
+
+#include <string>
+#include <sstream>
+#include "talk/base/socketaddress.h"
+
+namespace cricket {
+
+// Candidate for ICE based connection discovery.
+
+class Candidate {
+ public:
+ Candidate() : preference_(0), generation_(0) {}
+ Candidate(const std::string& name, const std::string& protocol,
+ const talk_base::SocketAddress& address, float preference,
+ const std::string& username, const std::string& password,
+ const std::string& type, const std::string& network_name,
+ uint32 generation)
+ : name_(name), protocol_(protocol), address_(address),
+ preference_(preference), username_(username), password_(password),
+ type_(type), network_name_(network_name), generation_(generation) {}
+
+ 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 talk_base::SocketAddress & address() const { return address_; }
+ void set_address(const talk_base::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_);
+ }
+
+ std::string ToString() const {
+ std::ostringstream ost;
+ ost << "Cand[" << name_ << ":" << type_ << ":" << protocol_ << ":"
+ << network_name_ << ":" << address_.ToString() << ":"
+ << username_ << ":" << password_ << "]";
+ return ost.str();
+ }
+
+ private:
+ std::string name_;
+ std::string protocol_;
+ talk_base::SocketAddress address_;
+ float preference_;
+ std::string username_;
+ std::string password_;
+ std::string type_;
+ std::string network_name_;
+ uint32 generation_;
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_CANDIDATE_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/common.h b/third_party/libjingle/source/talk/p2p/base/common.h
new file mode 100644
index 0000000..72f8cfa
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/common.h
@@ -0,0 +1,36 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_COMMON_H__
+#define CRICKET_P2P_BASE_COMMON_H__
+
+#include "talk/base/logging.h"
+
+// Common log description format for jingle messages
+#define LOG_J(sev,obj) LOG(sev) << "Jingle:" << obj->ToString() << ": "
+
+#endif // CRICKET_P2P_BASE_COMMON_H__
diff --git a/third_party/libjingle/source/talk/p2p/base/constants.cc b/third_party/libjingle/source/talk/p2p/base/constants.cc
new file mode 100644
index 0000000..b1545e7
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/constants.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/p2p/base/constants.h"
+
+namespace cricket {
+
+
+const std::string NS_EMPTY("");
+const std::string NS_JINGLE("urn:xmpp:jingle:1");
+const std::string NS_GINGLE("http://www.google.com/session");
+
+// actions (aka <session> or <jingle>)
+const buzz::QName QN_INITIATOR(true, NS_EMPTY, "initiator");
+const buzz::QName QN_ACTION(true, NS_EMPTY, "action");
+
+const buzz::QName QN_JINGLE_JINGLE(true, NS_JINGLE, "jingle");
+const buzz::QName QN_JINGLE_CONTENT(true, NS_JINGLE, "content");
+const std::string JINGLE_ACTION_SESSION_INITIATE("session-initiate");
+const std::string JINGLE_ACTION_SESSION_INFO("session-info");
+const std::string JINGLE_ACTION_SESSION_ACCEPT("session-accept");
+const std::string JINGLE_ACTION_SESSION_TERMINATE("session-terminate");
+const std::string JINGLE_ACTION_TRANSPORT_INFO("transport-info");
+
+const buzz::QName QN_GINGLE_SESSION(true, NS_GINGLE, "session");
+const std::string GINGLE_ACTION_INITIATE("initiate");
+const std::string GINGLE_ACTION_INFO("info");
+const std::string GINGLE_ACTION_ACCEPT("accept");
+const std::string GINGLE_ACTION_REJECT("reject");
+const std::string GINGLE_ACTION_TERMINATE("terminate");
+const std::string GINGLE_ACTION_CANDIDATES("candidates");
+
+// SessionApps (aka Gingle <session><description>
+// or Jingle <content><description>)
+const std::string LN_DESCRIPTION("description");
+const std::string LN_PAYLOADTYPE("payload-type");
+const buzz::QName QN_ID(true, NS_EMPTY, "id");
+const buzz::QName QN_NAME(true, NS_EMPTY, "name");
+const buzz::QName QN_CLOCKRATE(true, NS_EMPTY, "clockrate");
+const buzz::QName QN_BITRATE(true, NS_EMPTY, "bitrate");
+const buzz::QName QN_CHANNELS(true, NS_EMPTY, "channels");
+const buzz::QName QN_WIDTH(true, NS_EMPTY, "width");
+const buzz::QName QN_HEIGHT(true, NS_EMPTY, "height");
+const buzz::QName QN_FRAMERATE(true, NS_EMPTY, "framerate");
+const buzz::QName QN_PARAMETER(true, NS_EMPTY, "parameter");
+const std::string LN_NAME("name");
+const std::string LN_VALUE("value");
+const buzz::QName QN_PAYLOADTYPE_PARAMETER_NAME(true, NS_EMPTY, LN_NAME);
+const buzz::QName QN_PAYLOADTYPE_PARAMETER_VALUE(true, NS_EMPTY, LN_VALUE);
+const std::string PAYLOADTYPE_PARAMETER_BITRATE("bitrate");
+const std::string PAYLOADTYPE_PARAMETER_HEIGHT("height");
+const std::string PAYLOADTYPE_PARAMETER_WIDTH("width");
+const std::string PAYLOADTYPE_PARAMETER_FRAMERATE("framerate");
+
+
+const std::string NS_JINGLE_AUDIO("urn:xmpp:jingle:apps:rtp:audio");
+const buzz::QName QN_JINGLE_AUDIO_FORMAT(
+ true, NS_JINGLE_AUDIO, LN_DESCRIPTION);
+const buzz::QName QN_JINGLE_AUDIO_PAYLOADTYPE(
+ true, NS_JINGLE_AUDIO, LN_PAYLOADTYPE);
+const std::string NS_JINGLE_VIDEO("urn:xmpp:jingle:apps:rtp:video");
+const buzz::QName QN_JINGLE_VIDEO_FORMAT(
+ true, NS_JINGLE_VIDEO, LN_DESCRIPTION);
+const buzz::QName QN_JINGLE_VIDEO_PAYLOADTYPE(
+ true, NS_JINGLE_VIDEO, LN_PAYLOADTYPE);
+
+const std::string NS_GINGLE_AUDIO("http://www.google.com/session/phone");
+const buzz::QName QN_GINGLE_AUDIO_FORMAT(
+ true, NS_GINGLE_AUDIO, LN_DESCRIPTION);
+const buzz::QName QN_GINGLE_AUDIO_PAYLOADTYPE(
+ true, NS_GINGLE_AUDIO, LN_PAYLOADTYPE);
+const buzz::QName QN_GINGLE_AUDIO_SRCID(true, NS_GINGLE_AUDIO, "src-id");
+const std::string NS_GINGLE_VIDEO("http://www.google.com/session/video");
+const buzz::QName QN_GINGLE_VIDEO_FORMAT(
+ true, NS_GINGLE_VIDEO, LN_DESCRIPTION);
+const buzz::QName QN_GINGLE_VIDEO_PAYLOADTYPE(
+ true, NS_GINGLE_VIDEO, LN_PAYLOADTYPE);
+const buzz::QName QN_GINGLE_VIDEO_SRCID(true, NS_GINGLE_VIDEO, "src-id");
+const buzz::QName QN_GINGLE_VIDEO_BANDWIDTH(true, NS_GINGLE_VIDEO, "bandwidth");
+
+// transports and candidates
+const std::string NS_JINGLE_P2P("urn:xmpp:jingle:transports:ice-udp:1");
+const std::string LN_TRANSPORT("transport");
+const std::string LN_CANDIDATE("candidate");
+const buzz::QName QN_JINGLE_P2P_TRANSPORT(true, NS_JINGLE_P2P, LN_TRANSPORT);
+const buzz::QName QN_JINGLE_P2P_CANDIDATE(true, NS_JINGLE_P2P, LN_CANDIDATE);
+const buzz::QName QN_UFRAG(true, cricket::NS_EMPTY, "ufrag");
+const buzz::QName QN_PWD(true, cricket::NS_EMPTY, "pwd");
+const buzz::QName QN_COMPONENT(true, cricket::NS_EMPTY, "component");
+const buzz::QName QN_IP(true, cricket::NS_EMPTY, "ip");
+const buzz::QName QN_PORT(true, cricket::NS_EMPTY, "port");
+const buzz::QName QN_NETWORK(true, cricket::NS_EMPTY, "network");
+const buzz::QName QN_GENERATION(true, cricket::NS_EMPTY, "generation");
+const buzz::QName QN_PRIORITY(true, cricket::NS_EMPTY, "priority");
+const buzz::QName QN_PROTOCOL(true, cricket::NS_EMPTY, "protocol");
+const std::string JINGLE_CANDIDATE_TYPE_PEER_STUN("prflx");
+const std::string JINGLE_CANDIDATE_TYPE_SERVER_STUN("srflx");
+const std::string JINGLE_CANDIDATE_NAME_RTP("1");
+const std::string JINGLE_CANDIDATE_NAME_RTCP("2");
+
+const std::string NS_GINGLE_P2P("http://www.google.com/transport/p2p");
+const buzz::QName QN_GINGLE_P2P_TRANSPORT(true, NS_GINGLE_P2P, LN_TRANSPORT);
+const buzz::QName QN_GINGLE2_P2P_CANDIDATE(true, NS_GINGLE_P2P, LN_CANDIDATE);
+const buzz::QName QN_GINGLE_P2P_CANDIDATE(true, NS_GINGLE, LN_CANDIDATE);
+const buzz::QName QN_GINGLE_P2P_UNKNOWN_CHANNEL_NAME(true,
+ NS_GINGLE_P2P, "unknown-channel-name");
+const buzz::QName QN_GINGLE_CANDIDATE(true, cricket::NS_GINGLE, "candidate");
+const buzz::QName QN_ADDRESS(true, cricket::NS_EMPTY, "address");
+const buzz::QName QN_USERNAME(true, cricket::NS_EMPTY, "username");
+const buzz::QName QN_PASSWORD(true, cricket::NS_EMPTY, "password");
+const buzz::QName QN_PREFERENCE(true, cricket::NS_EMPTY, "preference");
+const std::string GINGLE_CANDIDATE_TYPE_STUN("stun");
+const std::string GINGLE_CANDIDATE_NAME_RTP("rtp");
+const std::string GINGLE_CANDIDATE_NAME_RTCP("rtcp");
+const std::string GINGLE_CANDIDATE_NAME_VIDEO_RTP("video_rtp");
+const std::string GINGLE_CANDIDATE_NAME_VIDEO_RTCP("video_rtcp");
+
+const std::string NS_GINGLE_RAW("http://www.google.com/transport/raw-udp");
+const buzz::QName QN_GINGLE_RAW_TRANSPORT(true, NS_GINGLE_RAW, "transport");
+const buzz::QName QN_GINGLE_RAW_CHANNEL(true, NS_GINGLE_RAW, "channel");
+
+
+// old stuff
+#ifdef FEATURE_ENABLE_VOICEMAIL
+const std::string NS_VOICEMAIL("http://www.google.com/session/voicemail");
+const buzz::QName QN_VOICEMAIL_REGARDING(true, NS_VOICEMAIL, "regarding");
+#endif
+
+/* Disabling redirect until we can implement it the "Jingle way".
+const std::string GINGLE_ACTION_REDIRECT("redirect");
+const buzz::QName QN_REDIRECT_TARGET(true, NS_GINGLE_SESSION, "target");
+const buzz::QName QN_REDIRECT_COOKIE(true, NS_GINGLE_SESSION, "cookie");
+const buzz::QName QN_REDIRECT_REGARDING(true, NS_GINGLE_SESSION, "regarding");
+*/
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/constants.h b/third_party/libjingle/source/talk/p2p/base/constants.h
new file mode 100644
index 0000000..b144d1d
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/constants.h
@@ -0,0 +1,177 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_CONSTANTS_H_
+#define TALK_P2P_BASE_CONSTANTS_H_
+
+#include <string>
+#include "talk/xmllite/qname.h"
+
+// This file contains constants related to signaling that are used in various
+// classes in this directory.
+
+namespace cricket {
+
+// There are 3 different types of Jingle messages or protocols: Jingle
+// (the spec in XEP-166, etc), Gingle (the legacy protocol) and hybrid
+// (both at the same time). Gingle2 is a temporary protocol that we
+// are only keeping around right now during this refactoring phase.
+// Once we finish refactoring and start implementing Jingle, we will
+// remove Gingle2.
+
+// NS_ == namespace
+// QN_ == buzz::QName (namespace + name)
+// LN_ == "local name" == QName::LocalPart()
+// these are useful when you need to find a tag
+// that has different namespaces (like <description> or <transport>)
+
+extern const std::string NS_EMPTY;
+extern const std::string NS_JINGLE;
+extern const std::string NS_GINGLE;
+
+// TODO(pthatcher): remove GINGLE2 when we
+// move to purely Jingle and Gingle protocols.
+enum SignalingProtocol {
+ PROTOCOL_JINGLE,
+ PROTOCOL_GINGLE,
+ PROTOCOL_GINGLE2,
+ PROTOCOL_HYBRID,
+};
+
+// actions (aka Gingle <session> or Jingle <jingle>)
+extern const buzz::QName QN_INITIATOR;
+extern const buzz::QName QN_ACTION;
+
+extern const buzz::QName QN_JINGLE_JINGLE;
+extern const buzz::QName QN_JINGLE_CONTENT;
+extern const std::string JINGLE_ACTION_SESSION_INITIATE;
+extern const std::string JINGLE_ACTION_SESSION_INFO;
+extern const std::string JINGLE_ACTION_SESSION_ACCEPT;
+extern const std::string JINGLE_ACTION_SESSION_TERMINATE;
+extern const std::string JINGLE_ACTION_TRANSPORT_INFO;
+
+extern const buzz::QName QN_GINGLE_SESSION;
+extern const std::string GINGLE_ACTION_INITIATE;
+extern const std::string GINGLE_ACTION_INFO;
+extern const std::string GINGLE_ACTION_ACCEPT;
+extern const std::string GINGLE_ACTION_REJECT;
+extern const std::string GINGLE_ACTION_TERMINATE;
+extern const std::string GINGLE_ACTION_CANDIDATES;
+
+
+// SessionFormats (aka Gingle <session><description>
+// or Jingle <content><description>)
+// For now, FormatDescription == SessionDescription
+// Long term, everything will be FormatDescription
+extern const std::string LN_DESCRIPTION;
+extern const std::string LN_PAYLOADTYPE;
+extern const buzz::QName QN_ID;
+extern const buzz::QName QN_NAME;
+extern const buzz::QName QN_CLOCKRATE;
+extern const buzz::QName QN_BITRATE;
+extern const buzz::QName QN_CHANNELS;
+extern const buzz::QName QN_WIDTH;
+extern const buzz::QName QN_HEIGHT;
+extern const buzz::QName QN_FRAMERATE;
+extern const buzz::QName QN_PARAMETER;
+extern const std::string LN_NAME;
+extern const std::string LN_VALUE;
+extern const buzz::QName QN_PAYLOADTYPE_PARAMETER_NAME;
+extern const buzz::QName QN_PAYLOADTYPE_PARAMETER_VALUE;
+extern const std::string PAYLOADTYPE_PARAMETER_BITRATE;
+extern const std::string PAYLOADTYPE_PARAMETER_HEIGHT;
+extern const std::string PAYLOADTYPE_PARAMETER_WIDTH;
+extern const std::string PAYLOADTYPE_PARAMETER_FRAMERATE;
+
+extern const std::string NS_JINGLE_AUDIO;
+extern const buzz::QName QN_JINGLE_AUDIO_FORMAT;
+extern const buzz::QName QN_JINGLE_AUDIO_PAYLOADTYPE;
+extern const std::string NS_JINGLE_VIDEO;
+extern const buzz::QName QN_JINGLE_VIDEO_FORMAT;
+extern const buzz::QName QN_JINGLE_VIDEO_PAYLOADTYPE;
+
+extern const std::string NS_GINGLE_AUDIO;
+extern const buzz::QName QN_GINGLE_AUDIO_FORMAT;
+extern const buzz::QName QN_GINGLE_AUDIO_PAYLOADTYPE;
+extern const buzz::QName QN_GINGLE_AUDIO_SRCID;
+extern const std::string NS_GINGLE_VIDEO;
+extern const buzz::QName QN_GINGLE_VIDEO_FORMAT;
+extern const buzz::QName QN_GINGLE_VIDEO_PAYLOADTYPE;
+extern const buzz::QName QN_GINGLE_VIDEO_SRCID;
+extern const buzz::QName QN_GINGLE_VIDEO_BANDWIDTH;
+
+// transports and candidates
+extern const std::string NS_JINGLE_P2P;
+extern const std::string LN_TRANSPORT;
+extern const std::string LN_CANDIDATE;
+extern const buzz::QName QN_JINGLE_P2P_TRANSPORT;
+extern const buzz::QName QN_JINGLE_P2P_CANDIDATE;
+extern const buzz::QName QN_UFRAG;
+extern const buzz::QName QN_COMPONENT;
+extern const buzz::QName QN_PWD;
+extern const buzz::QName QN_IP;
+extern const buzz::QName QN_PORT;
+extern const buzz::QName QN_NETWORK;
+extern const buzz::QName QN_GENERATION;
+extern const buzz::QName QN_PRIORITY;
+extern const buzz::QName QN_PROTOCOL;
+extern const std::string JINGLE_CANDIDATE_TYPE_PEER_STUN;
+extern const std::string JINGLE_CANDIDATE_TYPE_SERVER_STUN;
+extern const std::string JINGLE_CANDIDATE_NAME_RTP;
+extern const std::string JINGLE_CANDIDATE_NAME_RTCP;
+
+extern const std::string NS_GINGLE_P2P;
+extern const buzz::QName QN_GINGLE_P2P_TRANSPORT;
+extern const buzz::QName QN_GINGLE2_P2P_CANDIDATE;
+extern const buzz::QName QN_GINGLE_P2P_CANDIDATE;
+extern const buzz::QName QN_GINGLE_P2P_UNKNOWN_CHANNEL_NAME;
+extern const buzz::QName QN_GINGLE_CANDIDATE;
+extern const buzz::QName QN_ADDRESS;
+extern const buzz::QName QN_USERNAME;
+extern const buzz::QName QN_PASSWORD;
+extern const buzz::QName QN_PREFERENCE;
+extern const std::string GINGLE_CANDIDATE_TYPE_STUN;
+extern const std::string GINGLE_CANDIDATE_NAME_RTP;
+extern const std::string GINGLE_CANDIDATE_NAME_RTCP;
+extern const std::string GINGLE_CANDIDATE_NAME_VIDEO_RTP;
+extern const std::string GINGLE_CANDIDATE_NAME_VIDEO_RTCP;
+
+extern const std::string NS_GINGLE_RAW;
+extern const buzz::QName QN_GINGLE_RAW_TRANSPORT;
+extern const buzz::QName QN_GINGLE_RAW_CHANNEL;
+extern const buzz::QName QN_GINGLE_RAW_BEHIND_SYM_NAT;
+extern const buzz::QName QN_GINGLE_RAW_CAN_RECEIVE_FROM_SYM_NAT;
+
+// old stuff
+#ifdef FEATURE_ENABLE_VOICEMAIL
+extern const std::string NS_VOICEMAIL;
+extern const buzz::QName QN_VOICEMAIL_REGARDING;
+#endif
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_CONSTANTS_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/p2ptransport.cc b/third_party/libjingle/source/talk/p2p/base/p2ptransport.cc
new file mode 100644
index 0000000..c47db4d
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/p2ptransport.cc
@@ -0,0 +1,194 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/p2ptransport.h"
+
+#include <string>
+#include <vector>
+#include "talk/base/base64.h"
+#include "talk/base/common.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/p2ptransportchannel.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/sessionmessages.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace {
+
+// We only allow usernames to be this many characters or fewer.
+const size_t kMaxUsernameSize = 16;
+
+} // namespace
+
+namespace cricket {
+
+P2PTransport::P2PTransport(talk_base::Thread* worker_thread,
+ PortAllocator* allocator)
+ : Transport(worker_thread, NS_GINGLE_P2P, allocator) {
+}
+
+P2PTransport::~P2PTransport() {
+ DestroyAllChannels();
+}
+
+void P2PTransport::OnTransportError(const buzz::XmlElement* error) {
+ // Need to know if it was <unknown-channel name="xxx">.
+ ASSERT(error->Name().Namespace() == name());
+ if ((error->Name() == QN_GINGLE_P2P_UNKNOWN_CHANNEL_NAME)
+ && error->HasAttr(buzz::QN_NAME)) {
+ std::string channel_name = error->Attr(buzz::QN_NAME);
+ if (HasChannel(channel_name)) {
+ SignalChannelGone(this, channel_name);
+ }
+ }
+}
+
+bool P2PTransport::ParseCandidates(const buzz::XmlElement* elem,
+ Candidates* candidates,
+ ParseError* error) {
+ for (const buzz::XmlElement* candidate_elem = elem->FirstElement();
+ candidate_elem != NULL;
+ candidate_elem = candidate_elem->NextElement()) {
+ // Only look at local part because it might be <session><candidate>
+ // or <tranport><candidate>.
+ if (candidate_elem->Name().LocalPart() == LN_CANDIDATE) {
+ Candidate candidate;
+ if (!ParseCandidate(candidate_elem, &candidate, error))
+ return false;
+ candidates->push_back(candidate);
+ }
+ }
+ return true;
+}
+
+bool P2PTransport::ParseCandidate(const buzz::XmlElement* elem,
+ Candidate* candidate,
+ ParseError* error) {
+ if (!elem->HasAttr(buzz::QN_NAME) ||
+ !elem->HasAttr(QN_ADDRESS) ||
+ !elem->HasAttr(QN_PORT) ||
+ !elem->HasAttr(QN_USERNAME) ||
+ !elem->HasAttr(QN_PREFERENCE) ||
+ !elem->HasAttr(QN_PROTOCOL) ||
+ !elem->HasAttr(QN_GENERATION)) {
+ return BadParse("candidate missing required attribute", error);
+ }
+
+ talk_base::SocketAddress address;
+ if (!ParseAddress(elem, QN_ADDRESS, QN_PORT, &address, error))
+ return false;
+
+ candidate->set_name(elem->Attr(buzz::QN_NAME));
+ candidate->set_address(address);
+ candidate->set_username(elem->Attr(QN_USERNAME));
+ candidate->set_preference_str(elem->Attr(QN_PREFERENCE));
+ candidate->set_protocol(elem->Attr(QN_PROTOCOL));
+ candidate->set_generation_str(elem->Attr(QN_GENERATION));
+ if (elem->HasAttr(QN_PASSWORD))
+ candidate->set_password(elem->Attr(QN_PASSWORD));
+ if (elem->HasAttr(buzz::QN_TYPE))
+ candidate->set_type(elem->Attr(buzz::QN_TYPE));
+ if (elem->HasAttr(QN_NETWORK))
+ candidate->set_network_name(elem->Attr(QN_NETWORK));
+
+ if (!VerifyUsernameFormat(candidate->username(), error))
+ return false;
+
+ if (!HasChannel(candidate->name())) {
+ buzz::XmlElement* extra_info =
+ new buzz::XmlElement(QN_GINGLE_P2P_UNKNOWN_CHANNEL_NAME);
+ extra_info->AddAttr(buzz::QN_NAME, candidate->name());
+ error->extra = extra_info;
+ return BadParse("channel named in candidate does not exist", error);
+ }
+
+ return true;
+}
+
+bool P2PTransport::VerifyUsernameFormat(const std::string& username,
+ ParseError* error) {
+ if (username.size() > kMaxUsernameSize)
+ return BadParse("candidate username is too long", error);
+ if (!talk_base::Base64::IsBase64Encoded(username))
+ return BadParse(
+ "candidate username has non-base64 encoded characters", error);
+ return true;
+}
+
+const buzz::QName& GetCandidateQName(SignalingProtocol protocol) {
+ if (protocol == PROTOCOL_GINGLE2) {
+ return QN_GINGLE2_P2P_CANDIDATE;
+ } else {
+ return QN_GINGLE_P2P_CANDIDATE;
+ }
+}
+
+void P2PTransport::WriteCandidates(const Candidates& candidates,
+ SignalingProtocol protocol,
+ XmlElements* candidate_elems) {
+ for (std::vector<Candidate>::const_iterator
+ iter = candidates.begin();
+ iter != candidates.end();
+ ++iter) {
+ buzz::XmlElement* cand_elem =
+ new buzz::XmlElement(GetCandidateQName(protocol));
+ WriteCandidate(*iter, cand_elem);
+ candidate_elems->push_back(cand_elem);
+ }
+}
+
+void P2PTransport::WriteCandidate(const Candidate& candidate,
+ buzz::XmlElement* elem) {
+ elem->SetAttr(buzz::QN_NAME, candidate.name());
+ elem->SetAttr(QN_ADDRESS, candidate.address().IPAsString());
+ elem->SetAttr(QN_PORT, candidate.address().PortAsString());
+ elem->SetAttr(QN_PREFERENCE, candidate.preference_str());
+ elem->SetAttr(QN_USERNAME, candidate.username());
+ elem->SetAttr(QN_PROTOCOL, candidate.protocol());
+ elem->SetAttr(QN_GENERATION, candidate.generation_str());
+ if (candidate.password().size() > 0)
+ elem->SetAttr(QN_PASSWORD, candidate.password());
+ if (candidate.type().size() > 0)
+ elem->SetAttr(buzz::QN_TYPE, candidate.type());
+ if (candidate.network_name().size() > 0)
+ elem->SetAttr(QN_NETWORK, candidate.network_name());
+}
+
+TransportChannelImpl* P2PTransport::CreateTransportChannel(
+ const std::string& name, const std::string& session_type) {
+ return new P2PTransportChannel(name, session_type, this, port_allocator());
+}
+
+void P2PTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
+ delete channel;
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/p2ptransport.h b/third_party/libjingle/source/talk/p2p/base/p2ptransport.h
new file mode 100644
index 0000000..bd88c48
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/p2ptransport.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 TALK_P2P_BASE_P2PTRANSPORT_H_
+#define TALK_P2P_BASE_P2PTRANSPORT_H_
+
+#include <string>
+#include <vector>
+#include "talk/p2p/base/transport.h"
+
+namespace cricket {
+
+class P2PTransport: public Transport {
+ public:
+ P2PTransport(talk_base::Thread* worker_thread, PortAllocator* allocator);
+ virtual ~P2PTransport();
+
+ virtual bool ParseCandidates(const buzz::XmlElement* elem,
+ Candidates* candidates,
+ ParseError* error);
+ virtual void WriteCandidates(const Candidates& candidates,
+ SignalingProtocol protocol,
+ XmlElements* candidate_elems);
+
+ virtual void OnTransportError(const buzz::XmlElement* error);
+
+ protected:
+ // Creates and destroys P2PTransportChannel.
+ virtual TransportChannelImpl* CreateTransportChannel(
+ const std::string& name, const std::string& session_type);
+ virtual void DestroyTransportChannel(TransportChannelImpl* channel);
+
+ private:
+ bool ParseCandidate(const buzz::XmlElement* elem,
+ Candidate* candidate,
+ ParseError* error);
+ void WriteCandidate(const Candidate& candidate,
+ buzz::XmlElement* elem);
+ bool VerifyUsernameFormat(const std::string& username,
+ ParseError* error);
+
+ friend class P2PTransportChannel;
+
+ DISALLOW_EVIL_CONSTRUCTORS(P2PTransport);
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_P2PTRANSPORT_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc b/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc
new file mode 100644
index 0000000..47b1e91
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc
@@ -0,0 +1,917 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/p2ptransportchannel.h"
+
+#include <set>
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/common.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 RTT 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 || !b_conn) // 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 {
+
+P2PTransportChannel::P2PTransportChannel(const std::string &name,
+ const std::string &session_type,
+ P2PTransport* transport,
+ PortAllocator *allocator) :
+ TransportChannelImpl(name, session_type),
+ transport_(transport),
+ allocator_(allocator),
+ worker_thread_(talk_base::Thread::Current()),
+ waiting_for_signaling_(false),
+ error_(0),
+ best_connection_(NULL),
+ pinging_started_(false),
+ sort_dirty_(false),
+ was_writable_(false),
+ was_timed_out_(true) {
+}
+
+P2PTransportChannel::~P2PTransportChannel() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ 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 P2PTransportChannel::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, &P2PTransportChannel::OnPortReady);
+ session->SignalCandidatesReady.connect(
+ this, &P2PTransportChannel::OnCandidatesReady);
+ session->GetInitialPorts();
+ if (pinging_started_)
+ session->StartGetAllPorts();
+}
+
+// Go into the state of processing candidates, and running in general
+void P2PTransportChannel::Connect() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Kick off an allocator session
+ Allocate();
+
+ // Start pinging as the ports come in.
+ thread()->Post(this, MSG_PING);
+}
+
+// Reset the socket, clear up any previous allocations and start over
+void P2PTransportChannel::Reset() {
+ ASSERT(worker_thread_ == talk_base::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 initial state.
+ set_readable(false);
+ set_writable(false);
+
+ // Reinitialize the rest of our state.
+ waiting_for_signaling_ = false;
+ pinging_started_ = false;
+ sort_dirty_ = false;
+ was_writable_ = false;
+ was_timed_out_ = true;
+
+ // If we allocated before, start a new one now.
+ if (transport_->connect_requested())
+ Allocate();
+
+ // 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 P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
+ Port* port) {
+ ASSERT(worker_thread_ == talk_base::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_J(LS_WARNING, port) << "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, &P2PTransportChannel::OnUnknownAddress);
+ port->SignalDestroyed.connect(this, &P2PTransportChannel::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 P2PTransportChannel::OnCandidatesReady(
+ PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
+ for (size_t i = 0; i < candidates.size(); ++i) {
+ SignalCandidateReady(this, candidates[i]);
+ }
+}
+
+// Handle stun packets
+void P2PTransportChannel::OnUnknownAddress(
+ Port *port, const talk_base::SocketAddress &address, StunMessage *stun_msg,
+ const std::string &remote_username) {
+ ASSERT(worker_thread_ == talk_base::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;
+}
+
+void P2PTransportChannel::OnCandidate(const Candidate& candidate) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Create connections to this remote candidate.
+ CreateConnections(candidate, NULL, false);
+
+ // Resort the connections list, which may have new elements.
+ SortConnections();
+}
+
+// Creates connections from all of the ports that we care about to the given
+// remote candidate. The return value is true if we created a connection from
+// the origin port.
+bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate,
+ Port* origin_port,
+ bool readable) {
+ ASSERT(worker_thread_ == talk_base::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) &&
+ std::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 P2PTransportChannel::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, &P2PTransportChannel::OnReadPacket);
+ connection->SignalStateChange.connect(
+ this, &P2PTransportChannel::OnConnectionStateChange);
+ connection->SignalDestroyed.connect(
+ this, &P2PTransportChannel::OnConnectionDestroyed);
+
+ LOG_J(LS_INFO, this) << "Created connection with origin=" << origin << ", ("
+ << connections_.size() << " total)";
+ }
+
+ // 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 P2PTransportChannel::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()) {
+ LOG(INFO) << "Pruning candidate from old generation: "
+ << remote_candidates_[i].address().ToString();
+ remote_candidates_.erase(remote_candidates_.begin() + i);
+ } 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 P2PTransportChannel::SendPacket(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;
+}
+
+// Begin allocate (or immediately re-allocate, if MSG_ALLOCATE pending)
+void P2PTransportChannel::Allocate() {
+ CancelPendingAllocate();
+ // Time for a new allocator, lets make sure we have a signalling channel
+ // to communicate candidates through first.
+ waiting_for_signaling_ = true;
+ SignalRequestSignaling();
+}
+
+// Cancels the pending allocate, if any.
+void P2PTransportChannel::CancelPendingAllocate() {
+ thread()->Clear(this, MSG_ALLOCATE);
+}
+
+// Monitor connection states
+void P2PTransportChannel::UpdateConnectionStates() {
+ uint32 now = talk_base::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 P2PTransportChannel::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 P2PTransportChannel::SortConnections() {
+ ASSERT(worker_thread_ == talk_base::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<talk_base::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<talk_base::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();
+ }
+
+ // Update the state of this channel. This method is called whenever the
+ // state of any connection changes, so this is a good place to do this.
+ UpdateChannelState();
+
+ // Notify of connection state change
+ SignalConnectionMonitor(this);
+}
+
+// Track the best connection, and let listeners know
+void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) {
+ // Note: the previous best_connection_ may be destroyed by now, so don't
+ // use it.
+ best_connection_ = conn;
+ if (best_connection_) {
+ LOG_J(LS_INFO, this) << "New best connection: " << conn->ToString();
+ SignalRouteChange(this, best_connection_->remote_candidate().address());
+ }
+}
+
+void P2PTransportChannel::UpdateChannelState() {
+ // The Handle* functions already set the writable state. We'll just double-
+ // check it here.
+ bool writable = ((best_connection_ != NULL) &&
+ (best_connection_->write_state() ==
+ Connection::STATE_WRITABLE));
+ ASSERT(writable == this->writable());
+ if (writable != this->writable())
+ LOG(LS_ERROR) << "UpdateChannelState: writable state mismatch";
+
+ bool readable = false;
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if (connections_[i]->read_state() == Connection::STATE_READABLE)
+ readable = true;
+ }
+ set_readable(readable);
+}
+
+// We checked the status of our connections and we had at least one that
+// was writable, go into the writable state.
+void P2PTransportChannel::HandleWritable() {
+ //
+ // One or more connections writable!
+ //
+ if (!writable()) {
+ for (uint32 i = 0; i < allocator_sessions_.size(); ++i) {
+ if (allocator_sessions_[i]->IsGettingAllPorts()) {
+ allocator_sessions_[i]->StopGetAllPorts();
+ }
+ }
+
+ // Stop further allocations.
+ CancelPendingAllocate();
+ }
+
+ // We're writable, obviously we aren't timed out
+ was_writable_ = true;
+ was_timed_out_ = false;
+ set_writable(true);
+}
+
+// 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 P2PTransportChannel::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;
+ Allocate();
+ }
+
+ // We were connecting, obviously not ALL timed out.
+ was_timed_out_ = false;
+ set_writable(false);
+}
+
+// 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 P2PTransportChannel::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)
+ Allocate();
+ }
+
+ // 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_writable(false);
+}
+
+// If we have a best connection, return it, otherwise return top one in the
+// list (later we will mark it best).
+Connection* P2PTransportChannel::GetBestConnectionOnNetwork(
+ talk_base::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 P2PTransportChannel::OnMessage(talk_base::Message *pmsg) {
+ if (pmsg->message_id == MSG_SORT)
+ OnSort();
+ else if (pmsg->message_id == MSG_PING)
+ OnPing();
+ else if (pmsg->message_id == MSG_ALLOCATE)
+ Allocate();
+ else
+ ASSERT(false);
+}
+
+// Handle queued up sort request
+void P2PTransportChannel::OnSort() {
+ // Resort the connections based on the new statistics.
+ SortConnections();
+}
+
+// Handle queued up ping request
+void P2PTransportChannel::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(talk_base::Time());
+
+ // Post ourselves a message to perform the next ping.
+ uint32 delay = 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 P2PTransportChannel::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 (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* P2PTransportChannel::FindNextPingableConnection() {
+ uint32 now = talk_base::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 P2PTransportChannel::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 P2PTransportChannel::OnConnectionStateChange(Connection *connection) {
+ ASSERT(worker_thread_ == talk_base::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 P2PTransportChannel::OnConnectionDestroyed(Connection *connection) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Note: the previous best_connection_ may be destroyed by now, so don't
+ // use it.
+
+ // Remove this connection from the list.
+ std::vector<Connection*>::iterator iter =
+ std::find(connections_.begin(), connections_.end(), connection);
+ ASSERT(iter != connections_.end());
+ connections_.erase(iter);
+
+ LOG_J(LS_INFO, this) << "Removed connection ("
+ << 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 P2PTransportChannel::OnPortDestroyed(Port* port) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Remove this port from the list (if we didn't drop it already).
+ std::vector<Port*>::iterator iter =
+ std::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 P2PTransportChannel::OnReadPacket(Connection *connection,
+ const char *data, size_t len) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // Let the client know of an incoming packet
+
+ SignalReadPacket(this, data, len);
+}
+
+// Set options on ourselves is simply setting options on all of our available
+// port objects.
+int P2PTransportChannel::SetOption(talk_base::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;
+}
+
+// When the signalling channel is ready, we can really kick off the allocator
+void P2PTransportChannel::OnSignalingReady() {
+ if (waiting_for_signaling_) {
+ waiting_for_signaling_ = false;
+ AddAllocatorSession(allocator_->CreateSession(name(), session_type()));
+ thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE);
+ }
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h b/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h
new file mode 100644
index 0000000..9068619
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.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.
+ */
+
+// P2PTransportChannel 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 TALK_P2P_BASE_P2PTRANSPORTCHANNEL_H_
+#define TALK_P2P_BASE_P2PTRANSPORTCHANNEL_H_
+
+#include <map>
+#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"
+#include "talk/p2p/base/transport.h"
+#include "talk/p2p/base/transportchannelimpl.h"
+#include "talk/p2p/base/p2ptransport.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_;
+};
+
+// P2PTransportChannel manages the candidates and connection process to keep
+// two P2P clients connected to each other.
+class P2PTransportChannel : public TransportChannelImpl,
+ public talk_base::MessageHandler {
+ public:
+ P2PTransportChannel(const std::string &name,
+ const std::string &session_type,
+ P2PTransport* transport,
+ PortAllocator *allocator);
+ virtual ~P2PTransportChannel();
+
+ // From TransportChannelImpl:
+ virtual Transport* GetTransport() { return transport_; }
+ virtual void Connect();
+ virtual void Reset();
+ virtual void OnSignalingReady();
+
+ // From TransportChannel:
+ virtual int SendPacket(const char *data, size_t len);
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError() { return error_; }
+
+ // This hack is here to allow the SocketMonitor to downcast to the
+ // P2PTransportChannel safely.
+ virtual P2PTransportChannel* GetP2PChannel() { return this; }
+
+ // These are used by the connection monitor.
+ sigslot::signal1<P2PTransportChannel*> SignalConnectionMonitor;
+ const std::vector<Connection *>& connections() const { return connections_; }
+ Connection* best_connection() const { return best_connection_; }
+
+ // Handler for internal messages.
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+ virtual void OnCandidate(const Candidate& candidate);
+
+ private:
+ void Allocate();
+ void CancelPendingAllocate();
+ void UpdateConnectionStates();
+ void RequestSort();
+ void SortConnections();
+ void SwitchBestConnectionTo(Connection* conn);
+ void UpdateChannelState();
+ void HandleWritable();
+ void HandleNotWritable();
+ void HandleAllTimedOut();
+ Connection* GetBestConnectionOnNetwork(talk_base::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 talk_base::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 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);
+
+ talk_base::Thread* thread() const { return worker_thread_; }
+
+ P2PTransport* transport_;
+ PortAllocator *allocator_;
+ talk_base::Thread *worker_thread_;
+ bool waiting_for_signaling_;
+ int error_;
+ std::vector<PortAllocatorSession*> allocator_sessions_;
+ std::vector<Port *> ports_;
+ std::vector<Connection *> connections_;
+ Connection *best_connection_;
+ std::vector<RemoteCandidate> remote_candidates_;
+ // indicates whether StartGetAllCandidates has been called
+ bool pinging_started_;
+ bool sort_dirty_; // indicates whether another sort is needed right now
+ bool was_writable_;
+ bool was_timed_out_;
+ typedef std::map<talk_base::Socket::Option, int> OptionMap;
+ OptionMap options_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(P2PTransportChannel);
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_P2PTRANSPORTCHANNEL_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/parsing.cc b/third_party/libjingle/source/talk/p2p/base/parsing.cc
new file mode 100644
index 0000000..3ea2849
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/parsing.cc
@@ -0,0 +1,134 @@
+/*
+ * libjingle
+ * Copyright 2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/parsing.h"
+
+#include <stdlib.h>
+#include "talk/base/stringutils.h"
+
+namespace cricket {
+
+bool BadParse(const std::string& text, ParseError* err) {
+ err->text = text;
+ return false;
+}
+
+std::string GetXmlAttr(const buzz::XmlElement* elem,
+ const buzz::QName& name,
+ const std::string& def) {
+ std::string val = elem->Attr(name);
+ return val.empty() ? def : val;
+}
+
+int GetXmlAttr(const buzz::XmlElement* elem,
+ const buzz::QName& name, int def) {
+ std::string val = elem->Attr(name);
+ return val.empty() ? def : atoi(val.c_str());
+}
+
+void AddXmlAttr(buzz::XmlElement* elem,
+ const buzz::QName& name, int n) {
+ char buf[32];
+ talk_base::sprintfn(buf, sizeof(buf), "%d", n);
+ elem->AddAttr(name, buf);
+}
+
+void SetXmlBody(buzz::XmlElement* elem, uint32 u) {
+ char buf[16];
+ talk_base::sprintfn(buf, sizeof(buf), "%u", u);
+ elem->SetBodyText(buf);
+}
+
+const buzz::XmlElement* GetXmlChild(const buzz::XmlElement* parent,
+ const std::string& name) {
+ for (const buzz::XmlElement* child = parent->FirstElement();
+ child != NULL;
+ child = child->NextElement()) {
+ if (child->Name().LocalPart() == name) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+bool RequireXmlChild(const buzz::XmlElement* parent,
+ const std::string& name,
+ const buzz::XmlElement** child,
+ ParseError* error) {
+ *child = GetXmlChild(parent, name);
+ if (*child == NULL) {
+ return BadParse("element '" + parent->Name().Merged() +
+ "' missing required child '" + name,
+ error);
+ } else {
+ return true;
+ }
+}
+
+bool RequireXmlAttr(const buzz::XmlElement* elem,
+ const buzz::QName& name,
+ std::string* value,
+ ParseError* error) {
+ if (!elem->HasAttr(name)) {
+ return BadParse("element '" + elem->Name().Merged() +
+ "' missing required attribute '"
+ + name.Merged() + "'",
+ error);
+ } else {
+ *value = elem->Attr(name);
+ return true;
+ }
+}
+
+void AddXmlChildren(buzz::XmlElement* parent,
+ const std::vector<buzz::XmlElement*>& children) {
+ for (std::vector<buzz::XmlElement*>::const_iterator iter = children.begin();
+ iter != children.end();
+ iter++) {
+ parent->AddElement(*iter);
+ }
+}
+
+void CopyXmlChildren(buzz::XmlElement* source, buzz::XmlElement* dest) {
+ for (buzz::XmlElement* child = source->FirstElement();
+ child != NULL;
+ child = child->NextElement()) {
+ dest->AddElement(new buzz::XmlElement(*child));
+ }
+}
+
+std::vector<buzz::XmlElement*> CopyOfXmlChildren(const buzz::XmlElement* elem) {
+ std::vector<buzz::XmlElement*> children;
+ for (const buzz::XmlElement* child = elem->FirstElement();
+ child != NULL;
+ child = child->NextElement()) {
+ children.push_back(new buzz::XmlElement(*child));
+ }
+ return children;
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/parsing.h b/third_party/libjingle/source/talk/p2p/base/parsing.h
new file mode 100644
index 0000000..3397eb6
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/parsing.h
@@ -0,0 +1,92 @@
+/*
+ * libjingle
+ * Copyright 2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_PARSING_H_
+#define TALK_P2P_BASE_PARSING_H_
+
+#include <string>
+#include <vector>
+#include "talk/base/basictypes.h"
+#include "talk/xmllite/xmlelement.h" // Needed to delete ParseError.extra.
+
+namespace cricket {
+
+// We decided "bool Parse(in, out*, error*)" is generally the best
+// parse signature. "out Parse(in)" doesn't allow for errors.
+// "error* Parse(in, out*)" doesn't allow flexible memory management.
+// If a function can't return an error (common for unparsing), "void
+// Parse(in, out*)" is acceptable.
+
+// The common error class for parsing and unparsing actions,
+// transports, and payloads.
+struct ParseError {
+ public:
+ // explains the error
+ std::string text;
+ // provide details about what wasn't parsable
+ const buzz::XmlElement* extra;
+
+ ParseError() : extra(NULL) {}
+
+ ~ParseError() {
+ delete extra;
+ }
+
+ void SetText(const std::string& text) {
+ this->text = text;
+ }
+};
+
+bool BadParse(const std::string& text, ParseError* err);
+
+// helper XML functions
+std::string GetXmlAttr(const buzz::XmlElement* elem,
+ const buzz::QName& name,
+ const std::string& def);
+int GetXmlAttr(const buzz::XmlElement* elem,
+ const buzz::QName& name, int def);
+void AddXmlAttr(buzz::XmlElement* elem,
+ const buzz::QName& name, int n);
+void SetXmlBody(buzz::XmlElement* elem, uint32 u);
+const buzz::XmlElement* GetXmlChild(const buzz::XmlElement* parent,
+ const std::string& name);
+bool RequireXmlChild(const buzz::XmlElement* parent,
+ const std::string& name,
+ const buzz::XmlElement** child,
+ ParseError* error);
+bool RequireXmlAttr(const buzz::XmlElement* elem,
+ const buzz::QName& name,
+ std::string* value,
+ ParseError* error);
+void AddXmlChildren(buzz::XmlElement* parent,
+ const std::vector<buzz::XmlElement*>& children);
+void CopyXmlChildren(buzz::XmlElement* source, buzz::XmlElement* dest);
+std::vector<buzz::XmlElement*> CopyOfXmlChildren(const buzz::XmlElement* elem);
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_PARSING_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/port.cc b/third_party/libjingle/source/talk/p2p/base/port.cc
new file mode 100644
index 0000000..2aa66a4
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/port.cc
@@ -0,0 +1,976 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <algorithm>
+#include <vector>
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/asynctcpsocket.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/common.h"
+#include "talk/p2p/base/port.h"
+
+#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.
+inline uint32 ConservativeRTTEstimate(uint32 rtt) {
+ return talk_base::_max(MINIMUM_RTT, talk_base::_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[] = { "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;
+}
+
+Port::Port(talk_base::Thread* thread, const std::string& type,
+ talk_base::SocketFactory* factory, talk_base::Network* network)
+ : thread_(thread), factory_(factory), type_(type), network_(network),
+ preference_(-1), lifetime_(LT_PRESTART), enable_port_packets_(false) {
+ if (factory_ == NULL)
+ factory_ = thread_->socketserver();
+
+ set_username_fragment(talk_base::CreateRandomString(16));
+ set_password(talk_base::CreateRandomString(16));
+ LOG_J(LS_INFO, this) << "Port created";
+}
+
+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 talk_base::SocketAddress& remote_addr) {
+ AddressMap::const_iterator iter = connections_.find(remote_addr);
+ if (iter != connections_.end())
+ return iter->second;
+ else
+ return NULL;
+}
+
+void Port::AddAddress(const talk_base::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 talk_base::SocketAddress& addr) {
+ // If the user has enabled port packets, just hand this over.
+ if (enable_port_packets_) {
+ SignalReadPacket(this, data, size, addr);
+ return;
+ }
+
+ // 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_J(LS_ERROR, this) << "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 {
+ // NOTE(tschmelcher): This is benign. It occurs if we pruned a
+ // connection for this port while it had STUN requests in flight, because
+ // we then get back responses for them, which this code correctly does not
+ // handle.
+ LOG_J(LS_ERROR, this) << "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(talk_base::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(), username.size());
+ request.AddAttribute(username_attr);
+
+ // Send the request message.
+ // NOTE: If we wanted to, this is where we would add the HMAC.
+ talk_base::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 talk_base::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.
+ talk_base::scoped_ptr<StunMessage> stun_msg(new StunMessage());
+ talk_base::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) {
+ // Username not present or corrupted, don't reply.
+ LOG_J(LS_ERROR, this) << "Received STUN request without username";
+ return true;
+ } else if (std::memcmp(username_attr->bytes(), username_frag_.c_str(),
+ username_frag_.size()) != 0) {
+ LOG_J(LS_ERROR, this) << "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) {
+ // NOTE(tschmelcher): This is benign. It occurs when the response to a
+ // StunBindingRequest to the real STUN server (which involves no
+ // usernames) took too long to reach us and so the base StunRequest
+ // re-sent itself, resulting in us getting an extraneous second response
+ // that gets forwarded on to this code and correctly discarded.
+ LOG_J(LS_ERROR, this) << "Received STUN response without username";
+ // Do not send error response to a response
+ return true;
+ } else if (std::memcmp(username_attr->bytes() + remote_frag_len,
+ username_frag_.c_str(),
+ username_frag_.size()) != 0) {
+ LOG_J(LS_ERROR, this) << "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_J(LS_ERROR, this) << "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_J(LS_ERROR, this)
+ << "Received STUN error response with no error code";
+ // Drop corrupt message
+ return true;
+ }
+ }
+ } else {
+ LOG_J(LS_ERROR, this) << "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 talk_base::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 != NULL);
+ if (username_attr == NULL) {
+ // No valid username, skip the response.
+ return;
+ }
+
+ // 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.
+ talk_base::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 != NULL);
+ if (conn)
+ conn->ReceivedPing();
+}
+
+void Port::SendBindingErrorResponse(
+ StunMessage* request, const talk_base::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 != NULL);
+ if (username_attr == NULL) {
+ // No valid username, skip the response.
+ return;
+ }
+
+ // 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.
+ talk_base::ByteBuffer buf;
+ response.Write(&buf);
+ SendTo(buf.Data(), buf.Length(), addr, false);
+}
+
+talk_base::AsyncPacketSocket* Port::CreatePacketSocket(ProtocolType proto) {
+ if (proto == PROTO_UDP) {
+ // UDP sockets are simple.
+ return talk_base::AsyncUDPSocket::Create(factory_);
+ } else if (proto == PROTO_TCP || proto == PROTO_SSLTCP) {
+ // Create the base TCP socket. Bail out if this fails.
+ talk_base::AsyncSocket* socket = factory_->CreateAsyncSocket(SOCK_STREAM);
+ if (!socket) {
+ return NULL;
+ }
+
+ // If using a proxy, wrap the socket in a proxy socket.
+ if (proxy().type == talk_base::PROXY_SOCKS5) {
+ socket = new talk_base::AsyncSocksProxySocket(
+ socket, proxy().address, proxy().username, proxy().password);
+ } else if (proxy().type == talk_base::PROXY_HTTPS) {
+ socket = new talk_base::AsyncHttpsProxySocket(
+ socket, user_agent(), proxy().address,
+ proxy().username, proxy().password);
+ }
+
+ // If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
+ if (proto == PROTO_SSLTCP) {
+ socket = new talk_base::AsyncSSLSocket(socket);
+ }
+
+ // Finally, wrap that socket in a TCP packet socket.
+ // [Insert obligatory Taco Town reference here]
+ return new talk_base::AsyncTCPSocket(socket);
+ } else {
+ LOG_J(LS_ERROR, this) << "Unknown protocol (" << proto << ")";
+ return NULL;
+ }
+}
+
+void Port::OnMessage(talk_base::Message *pmsg) {
+ ASSERT(pmsg->message_id == MSG_CHECKTIMEOUT);
+ ASSERT(lifetime_ == LT_PRETIMEOUT);
+ lifetime_ = LT_POSTTIMEOUT;
+ CheckTimeout();
+}
+
+std::string Port::ToString() const {
+ std::stringstream ss;
+ ss << "Port[" << name_ << ":" << type_ << ":" << network_->ToString() << "]";
+ return ss.str();
+}
+
+void Port::EnablePortPackets() {
+ enable_port_packets_ = true;
+}
+
+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_J(LS_WARNING, this) << "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::Destroy() {
+ ASSERT(connections_.empty());
+ LOG_J(LS_INFO, this) << "Port deleted";
+ SignalDestroyed(this);
+ delete this;
+}
+
+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()) {
+ Destroy();
+ }
+}
+
+// A ConnectionRequest is a simple STUN ping used to determine writability.
+class ConnectionRequest : public StunRequest {
+ public:
+ explicit 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(), 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() {
+ LOG_J(LS_VERBOSE, connection_) << "Timing-out STUN ping " << id();
+ }
+
+ 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)
+ : port_(port), local_candidate_index_(index),
+ remote_candidate_(remote_candidate), read_state_(STATE_READ_TIMEOUT),
+ write_state_(STATE_WRITE_CONNECT), connected_(true), pruned_(false),
+ requests_(port->thread()), rtt_(DEFAULT_RTT),
+ last_ping_sent_(0), last_ping_received_(0), reported_(false) {
+ // Wire up to send stun packets
+ requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket);
+ LOG_J(LS_INFO, this) << "Connection created";
+}
+
+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) {
+ LOG_J(LS_VERBOSE, this) << "set_read_state";
+ SignalStateChange(this);
+ CheckTimeout();
+ }
+}
+
+void Connection::set_write_state(WriteState value) {
+ WriteState old_value = write_state_;
+ write_state_ = value;
+ if (value != old_value) {
+ LOG_J(LS_VERBOSE, this) << "set_write_state";
+ SignalStateChange(this);
+ CheckTimeout();
+ }
+}
+
+void Connection::set_connected(bool value) {
+ bool old_value = connected_;
+ connected_ = value;
+ if (value != old_value) {
+ LOG_J(LS_VERBOSE, this) << "set_connected";
+ }
+}
+
+void Connection::OnSendStunPacket(
+ const void* data, size_t size, StunRequest* req) {
+ port_->SendTo(data, size, remote_candidate_.address(), false);
+}
+
+void Connection::OnReadPacket(const char* data, size_t size) {
+ StunMessage* msg;
+ std::string remote_username;
+ const talk_base::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_rate_tracker_.Update(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_J(LS_WARNING, this)
+ << "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_J(LS_ERROR, this) << "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() {
+ if (!pruned_) {
+ LOG_J(LS_VERBOSE, this) << "Connection pruned";
+ pruned_ = true;
+ requests_.Clear();
+ set_write_state(STATE_WRITE_TIMEOUT);
+ }
+}
+
+void Connection::Destroy() {
+ LOG_J(LS_VERBOSE, this) << "Connection destroyed";
+ 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_);
+
+ std::string pings;
+ for (size_t i = 0; i < pings_since_last_response_.size(); ++i) {
+ char buf[32];
+ talk_base::sprintfn(buf, sizeof(buf), "%u",
+ pings_since_last_response_[i]);
+ pings.append(buf).append(" ");
+ }
+ LOG_J(LS_VERBOSE, this) << "UpdateState(): pings_since_last_response_ = " <<
+ pings << ", rtt = " << rtt << ", now = " << now;
+
+ 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);
+ ConnectionRequest *req = new ConnectionRequest(this);
+ LOG_J(LS_VERBOSE, this) << "Sending STUN ping " << req->id() << " at " << now;
+ requests_.Send(req);
+}
+
+void Connection::ReceivedPing() {
+ last_ping_received_ = talk_base::Time();
+ set_read_state(STATE_READABLE);
+}
+
+std::string Connection::ToString() const {
+ const char CONNECT_STATE_ABBREV[2] = {
+ '-', // not connected (false)
+ 'C', // connected (true)
+ };
+ const char READ_STATE_ABBREV[2] = {
+ 'R', // STATE_READABLE
+ '-', // STATE_READ_TIMEOUT
+ };
+ const char WRITE_STATE_ABBREV[3] = {
+ 'W', // STATE_WRITABLE
+ 'w', // STATE_WRITE_CONNECT
+ '-', // STATE_WRITE_TIMEOUT
+ };
+ const Candidate& local = local_candidate();
+ const Candidate& remote = remote_candidate();
+ std::stringstream ss;
+ ss << "Conn[" << local.generation()
+ << ":" << local.name() << ":" << local.type() << ":"
+ << local.protocol() << ":" << local.address().ToString()
+ << "->" << remote.name() << ":" << remote.type() << ":"
+ << remote.protocol() << ":" << remote.address().ToString()
+ << "|"
+ << CONNECT_STATE_ABBREV[connected()]
+ << READ_STATE_ABBREV[read_state()]
+ << WRITE_STATE_ABBREV[write_state()]
+ << "|" << rtt_ << "]";
+ return ss.str();
+}
+
+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_J(LS_ERROR, this) << "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_J(LS_ERROR, this) << "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_J(LS_ERROR, this) << "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);
+
+ std::string pings;
+ for (size_t i = 0; i < pings_since_last_response_.size(); ++i) {
+ char buf[32];
+ talk_base::sprintfn(buf, sizeof(buf), "%u",
+ pings_since_last_response_[i]);
+ pings.append(buf).append(" ");
+ }
+ LOG_J(LS_VERBOSE, this) << "OnConnectionRequestResponse(): "
+ "pings_since_last_response_ = " << pings << ", rtt = " << rtt;
+
+ pings_since_last_response_.clear();
+ rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1);
+
+ LOG_J(LS_VERBOSE, this) << "Received STUN ping response " <<
+ response->transaction_id() << " after rtt = " << rtt;
+ }
+}
+
+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.
+ LOG_J(LS_ERROR, this) << "Received STUN error response; killing connection";
+ set_write_state(STATE_WRITE_TIMEOUT);
+ }
+}
+
+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(talk_base::Message *pmsg) {
+ ASSERT(pmsg->message_id == MSG_DELETE);
+
+ LOG_J(LS_INFO, this) << "Connection deleted";
+ SignalDestroyed(this);
+ delete this;
+}
+
+size_t Connection::recv_bytes_second() {
+ return recv_rate_tracker_.bytes_second();
+}
+
+size_t Connection::recv_total_bytes() {
+ return recv_rate_tracker_.total_bytes();
+}
+
+size_t Connection::sent_bytes_second() {
+ return send_rate_tracker_.bytes_second();
+}
+
+size_t Connection::sent_total_bytes() {
+ return send_rate_tracker_.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 {
+ send_rate_tracker_.Update(sent);
+ }
+ return sent;
+}
+
+RateTracker::RateTracker()
+ : total_bytes_(0), bytes_second_(0),
+ last_bytes_second_time_(static_cast<uint32>(-1)),
+ last_bytes_second_calc_(0) {
+}
+
+size_t RateTracker::total_bytes() const {
+ return total_bytes_;
+}
+
+size_t RateTracker::bytes_second() {
+ // Snapshot bytes / second calculator. Determine how many seconds have
+ // elapsed since our last reference point. If over 1 second, establish
+ // a new reference point that is an integer number of seconds since the
+ // last one, and compute the bytes over that interval.
+
+ uint32 current_time = talk_base::Time();
+ if (last_bytes_second_time_ != static_cast<uint32>(-1)) {
+ int delta = talk_base::TimeDiff(current_time, last_bytes_second_time_);
+ if (delta >= 1000) {
+ int fraction_time = delta % 1000;
+ int seconds = delta / 1000;
+ int fraction_bytes =
+ static_cast<int>(total_bytes_ - last_bytes_second_calc_) *
+ fraction_time / delta;
+ // Compute "bytes received during the interval" / "seconds in interval"
+ bytes_second_ =
+ (total_bytes_ - last_bytes_second_calc_ - fraction_bytes) / seconds;
+ last_bytes_second_time_ = current_time - fraction_time;
+ last_bytes_second_calc_ = total_bytes_ - fraction_bytes;
+ }
+ }
+ if (last_bytes_second_time_ == static_cast<uint32>(-1)) {
+ last_bytes_second_time_ = current_time;
+ last_bytes_second_calc_ = total_bytes_;
+ }
+
+ return bytes_second_;
+}
+
+void RateTracker::Update(size_t bytes) {
+ total_bytes_ += bytes;
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/port.h b/third_party/libjingle/source/talk/p2p/base/port.h
new file mode 100644
index 0000000..ffa8c89
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/port.h
@@ -0,0 +1,429 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_PORT_H_
+#define TALK_P2P_BASE_PORT_H_
+
+#include <string>
+#include <vector>
+#include <map>
+
+#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"
+
+namespace talk_base {
+class AsyncPacketSocket;
+}
+
+namespace cricket {
+
+class Connection;
+
+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 {
+ talk_base::SocketAddress address;
+ ProtocolType proto;
+
+ ProtocolAddress(const talk_base::SocketAddress& a, ProtocolType p)
+ : address(a), proto(p) { }
+};
+
+// Computes instantaneous bytes per second.
+class RateTracker {
+ public:
+ RateTracker();
+ size_t total_bytes() const;
+ size_t bytes_second();
+ void Update(size_t bytes);
+
+ private:
+ size_t total_bytes_;
+ size_t bytes_second_;
+ uint32 last_bytes_second_time_;
+ size_t last_bytes_second_calc_;
+};
+
+// 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 talk_base::MessageHandler, public sigslot::has_slots<> {
+ public:
+ Port(talk_base::Thread* thread, const std::string &type,
+ talk_base::SocketFactory* factory, talk_base::Network* network);
+ virtual ~Port();
+
+ // The thread on which this port performs its I/O.
+ talk_base::Thread* thread() { return thread_; }
+
+ // The factory used to create the sockets of this port.
+ talk_base::SocketFactory* socket_factory() const { return factory_; }
+ void set_socket_factory(talk_base::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.
+ talk_base::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. If errors are
+ // preventing the port from getting an address, it may send
+ // SignalAddressError.
+ virtual void PrepareAddress() = 0;
+ sigslot::signal1<Port*> SignalAddressReady;
+ sigslot::signal1<Port*> SignalAddressError;
+
+ // 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<talk_base::SocketAddress, Connection*> AddressMap;
+ const AddressMap& connections() { return connections_; }
+
+ // Returns the connection to the given address or NULL if none exists.
+ Connection* GetConnection(const talk_base::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 talk_base::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 talk_base::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 talk_base::SocketAddress& addr);
+ void SendBindingErrorResponse(
+ StunMessage* request, const talk_base::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(talk_base::Socket::Option opt, int value) = 0;
+ virtual int GetError() = 0;
+
+ void set_proxy(const std::string& user_agent,
+ const talk_base::ProxyInfo& proxy) {
+ user_agent_ = user_agent;
+ proxy_ = proxy;
+ }
+ const std::string& user_agent() { return user_agent_; }
+ const talk_base::ProxyInfo& proxy() { return proxy_; }
+
+ talk_base::AsyncPacketSocket * CreatePacketSocket(ProtocolType proto);
+
+ // Normally, packets arrive through a connection (or they result signaling of
+ // unknown address). Calling this method turns off delivery of packets
+ // through their respective connection and instead delivers every packet
+ // through this port.
+ void EnablePortPackets();
+ sigslot::signal4<Port*, const char*, size_t, const talk_base::SocketAddress&>
+ SignalReadPacket;
+
+ // 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();
+
+ // Called if the port has no connections and is no longer useful.
+ void Destroy();
+
+ // Signaled when this port decides to delete itself because it no longer has
+ // any usefulness.
+ sigslot::signal1<Port*> SignalDestroyed;
+
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+ // Debugging description of this port
+ std::string ToString() const;
+
+ protected:
+ talk_base::Thread* thread_;
+ talk_base::SocketFactory* factory_;
+ std::string type_;
+ talk_base::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_;
+ bool enable_port_packets_;
+
+ // 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) {
+ username_frag_ = username_fragment;
+ }
+ void set_password(const std::string& password) { password_ = password; }
+
+ // Fills in the local address of the port.
+ void AddAddress(const talk_base::SocketAddress& address,
+ const std::string& protocol, bool final);
+
+ // 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 talk_base::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 talk_base::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();
+
+ // Information to use when going through a proxy.
+ std::string user_agent_;
+ talk_base::ProxyInfo proxy_;
+};
+
+// Represents a communication link between a port on the local client and a
+// port on the remote client.
+class Connection : public talk_base::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();
+
+ // Debugging description of this connection
+ std::string ToString() const;
+
+ bool reported() { return reported_; }
+ void set_reported(bool reported) { reported_ = reported;}
+
+ 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 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_;
+
+ RateTracker recv_rate_tracker_;
+ RateTracker send_rate_tracker_;
+
+ // 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, StunRequest* req);
+
+ // 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(talk_base::Message *pmsg);
+
+ friend class Port;
+ friend class ConnectionRequest;
+
+ private:
+ bool reported_;
+};
+
+// 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 // TALK_P2P_BASE_PORT_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/portallocator.h b/third_party/libjingle/source/talk/p2p/base/portallocator.h
new file mode 100644
index 0000000..332326e
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/portallocator.h
@@ -0,0 +1,105 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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.
+
+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 PortAllocatorSession : public sigslot::has_slots<> {
+public:
+ PortAllocatorSession(uint32 flags) : flags_(flags) {}
+
+ // Subclasses should clean up any ports created.
+ virtual ~PortAllocatorSession() {}
+
+ uint32 flags() const { return flags_; }
+ void set_flags(uint32 flags) { flags_ = flags; }
+
+ // 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 flags_;
+ uint32 generation_;
+};
+
+class PortAllocator {
+public:
+ PortAllocator() : flags_(kDefaultPortAllocatorFlags) {}
+ virtual ~PortAllocator() {}
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type) = 0;
+
+ uint32 flags() const { return flags_; }
+ void set_flags(uint32 flags) { flags_ = flags; }
+
+ const std::string& user_agent() const { return agent_; }
+ const talk_base::ProxyInfo& proxy() const { return proxy_; }
+ void set_proxy(const std::string& agent, const talk_base::ProxyInfo& proxy) {
+ agent_ = agent; proxy_ = proxy;
+ }
+
+protected:
+ uint32 flags_;
+ std::string agent_;
+ talk_base::ProxyInfo proxy_;
+};
+
+} // namespace cricket
+
+#endif // _PORTALLOCATOR_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc b/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc
new file mode 100644
index 0000000..bcc62f9
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc
@@ -0,0 +1,1069 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/pseudotcp.h"
+
+#include <cstdio>
+#include <cstdlib>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/socket.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/time.h"
+
+// The following logging is for detailed (packet-level) pseudotcp analysis only.
+#define _DBG_NONE 0
+#define _DBG_NORMAL 1
+#define _DBG_VERBOSE 2
+#define _DEBUGMSG _DBG_NONE
+
+namespace cricket {
+
+//////////////////////////////////////////////////////////////////////
+// Network Constants
+//////////////////////////////////////////////////////////////////////
+
+// 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 MAX_PACKET = 65535;
+// Note: we removed lowest level because packet overhead was larger!
+const uint32 MIN_PACKET = 296;
+
+const uint32 IP_HEADER_SIZE = 20; // (+ up to 40 bytes of options?)
+const uint32 ICMP_HEADER_SIZE = 8;
+const uint32 UDP_HEADER_SIZE = 8;
+// TODO: Make JINGLE_HEADER_SIZE transparent to this code?
+const uint32 JINGLE_HEADER_SIZE = 64; // when relay framing is in use
+
+//////////////////////////////////////////////////////////////////////
+// Global Constants and Functions
+//////////////////////////////////////////////////////////////////////
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 0 | Conversation Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 4 | Sequence Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 8 | Acknowledgment Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | | |U|A|P|R|S|F| |
+// 12 | Control | |R|C|S|S|Y|I| Window |
+// | | |G|K|H|T|N|N| |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 16 | Timestamp sending |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 20 | Timestamp receiving |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 24 | data |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+//////////////////////////////////////////////////////////////////////
+
+#define PSEUDO_KEEPALIVE 0
+
+const uint32 MAX_SEQ = 0xFFFFFFFF;
+const uint32 HEADER_SIZE = 24;
+const uint32 PACKET_OVERHEAD = HEADER_SIZE + UDP_HEADER_SIZE + IP_HEADER_SIZE + JINGLE_HEADER_SIZE;
+
+const uint32 MIN_RTO = 250; // 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second")
+const uint32 DEF_RTO = 3000; // 3 seconds (RFC1122, Sec 4.2.3.1)
+const uint32 MAX_RTO = 60000; // 60 seconds
+const uint32 ACK_DELAY = 100; // 100 milliseconds
+
+const uint8 FLAG_CTL = 0x02;
+const uint8 FLAG_RST = 0x04;
+
+const uint8 CTL_CONNECT = 0;
+//const uint8 CTL_REDIRECT = 1;
+const uint8 CTL_EXTRA = 255;
+
+/*
+const uint8 FLAG_FIN = 0x01;
+const uint8 FLAG_SYN = 0x02;
+const uint8 FLAG_ACK = 0x10;
+*/
+
+const uint32 CTRL_BOUND = 0x80000000;
+
+const long DEFAULT_TIMEOUT = 4000; // If there are no pending clocks, wake up every 4 seconds
+const long CLOSED_TIMEOUT = 60 * 1000; // If the connection is closed, once per minute
+
+#if PSEUDO_KEEPALIVE
+// !?! Rethink these times
+const uint32 IDLE_PING = 20 * 1000; // 20 seconds (note: WinXP SP2 firewall udp timeout is 90 seconds)
+const uint32 IDLE_TIMEOUT = 90 * 1000; // 90 seconds;
+#endif // PSEUDO_KEEPALIVE
+
+//////////////////////////////////////////////////////////////////////
+// Helper Functions
+//////////////////////////////////////////////////////////////////////
+
+inline void long_to_bytes(uint32 val, void* buf) {
+ *static_cast<uint32*>(buf) = talk_base::HostToNetwork32(val);
+}
+
+inline void short_to_bytes(uint16 val, void* buf) {
+ *static_cast<uint16*>(buf) = talk_base::HostToNetwork16(val);
+}
+
+inline uint32 bytes_to_long(const void* buf) {
+ return talk_base::NetworkToHost32(*static_cast<const uint32*>(buf));
+}
+
+inline uint16 bytes_to_short(const void* buf) {
+ return talk_base::NetworkToHost16(*static_cast<const uint16*>(buf));
+}
+
+uint32 bound(uint32 lower, uint32 middle, uint32 upper) {
+ return talk_base::_min(talk_base::_max(lower, middle), upper);
+}
+
+//////////////////////////////////////////////////////////////////////
+// Debugging Statistics
+//////////////////////////////////////////////////////////////////////
+
+#if 0 // Not used yet
+
+enum Stat {
+ S_SENT_PACKET, // All packet sends
+ S_RESENT_PACKET, // All packet sends that are retransmits
+ S_RECV_PACKET, // All packet receives
+ S_RECV_NEW, // All packet receives that are too new
+ S_RECV_OLD, // All packet receives that are too old
+ S_NUM_STATS
+};
+
+const char* const STAT_NAMES[S_NUM_STATS] = {
+ "snt",
+ "snt-r",
+ "rcv"
+ "rcv-n",
+ "rcv-o"
+};
+
+int g_stats[S_NUM_STATS];
+inline void Incr(Stat s) { ++g_stats[s]; }
+void ReportStats() {
+ char buffer[256];
+ size_t len = 0;
+ for (int i=0; i<S_NUM_STATS; ++i) {
+ len += talk_base::sprintfn(buffer, ARRAY_SIZE(buffer), "%s%s:%d",
+ (i == 0) ? "" : ",", STAT_NAMES[i], g_stats[i]);
+ g_stats[i] = 0;
+ }
+ LOG(LS_INFO) << "Stats[" << buffer << "]";
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// PseudoTcp
+//////////////////////////////////////////////////////////////////////
+
+uint32 PseudoTcp::Now() {
+#if 0 // Use this to synchronize timers with logging timestamps (easier debug)
+ return talk_base::TimeSince(StartTime());
+#else
+ return talk_base::Time();
+#endif
+}
+
+PseudoTcp::PseudoTcp(IPseudoTcpNotify * notify, uint32 conv)
+ : m_notify(notify), m_shutdown(SD_NONE), m_error(0) {
+
+ // Sanity check on buffer sizes (needed for OnTcpWriteable notification logic)
+ ASSERT(sizeof(m_rbuf) + MIN_PACKET < sizeof(m_sbuf));
+
+ uint32 now = Now();
+
+ m_state = TCP_LISTEN;
+ m_conv = conv;
+ m_rcv_wnd = sizeof(m_rbuf);
+ m_snd_nxt = m_slen = 0;
+ m_snd_wnd = 1;
+ m_snd_una = m_rcv_nxt = m_rlen = 0;
+ m_bReadEnable = true;
+ m_bWriteEnable = false;
+ m_t_ack = 0;
+
+ m_msslevel = 0;
+ m_largest = 0;
+ ASSERT(MIN_PACKET > PACKET_OVERHEAD);
+ m_mss = MIN_PACKET - PACKET_OVERHEAD;
+ m_mtu_advise = MAX_PACKET;
+
+ m_rto_base = 0;
+
+ m_cwnd = 2 * m_mss;
+ m_ssthresh = sizeof(m_rbuf);
+ m_lastrecv = m_lastsend = m_lasttraffic = now;
+ m_bOutgoing = false;
+
+ m_dup_acks = 0;
+ m_recover = 0;
+
+ m_ts_recent = m_ts_lastack = 0;
+
+ m_rx_rto = DEF_RTO;
+ m_rx_srtt = m_rx_rttvar = 0;
+}
+
+PseudoTcp::~PseudoTcp() {
+}
+
+int
+PseudoTcp::Connect() {
+ if (m_state != TCP_LISTEN) {
+ m_error = EINVAL;
+ return -1;
+ }
+
+ m_state = TCP_SYN_SENT;
+ LOG(LS_INFO) << "State: TCP_SYN_SENT";
+
+ char buffer[1];
+ buffer[0] = CTL_CONNECT;
+ queue(buffer, 1, true);
+ attemptSend();
+
+ return 0;
+}
+
+void
+PseudoTcp::NotifyMTU(uint16 mtu) {
+ m_mtu_advise = mtu;
+ if (m_state == TCP_ESTABLISHED) {
+ adjustMTU();
+ }
+}
+
+void
+PseudoTcp::NotifyClock(uint32 now) {
+ if (m_state == TCP_CLOSED)
+ return;
+
+ // Check if it's time to retransmit a segment
+ if (m_rto_base && (talk_base::TimeDiff(m_rto_base + m_rx_rto, now) <= 0)) {
+ if (m_slist.empty()) {
+ ASSERT(false);
+ } else {
+ // Note: (m_slist.front().xmit == 0)) {
+ // retransmit segments
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "timeout retransmit (rto: " << m_rx_rto
+ << ") (rto_base: " << m_rto_base
+ << ") (now: " << now
+ << ") (dup_acks: " << static_cast<unsigned>(m_dup_acks)
+ << ")";
+#endif // _DEBUGMSG
+ if (!transmit(m_slist.begin(), now)) {
+ closedown(ECONNABORTED);
+ return;
+ }
+
+ uint32 nInFlight = m_snd_nxt - m_snd_una;
+ m_ssthresh = talk_base::_max(nInFlight / 2, 2 * m_mss);
+ //LOG(LS_INFO) << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss;
+ m_cwnd = m_mss;
+
+ // Back off retransmit timer. Note: the limit is lower when connecting.
+ uint32 rto_limit = (m_state < TCP_ESTABLISHED) ? DEF_RTO : MAX_RTO;
+ m_rx_rto = talk_base::_min(rto_limit, m_rx_rto * 2);
+ m_rto_base = now;
+ }
+ }
+
+ // Check if it's time to probe closed windows
+ if ((m_snd_wnd == 0)
+ && (talk_base::TimeDiff(m_lastsend + m_rx_rto, now) <= 0)) {
+ if (talk_base::TimeDiff(now, m_lastrecv) >= 15000) {
+ closedown(ECONNABORTED);
+ return;
+ }
+
+ // probe the window
+ packet(m_snd_nxt - 1, 0, 0, 0);
+ m_lastsend = now;
+
+ // back off retransmit timer
+ m_rx_rto = talk_base::_min(MAX_RTO, m_rx_rto * 2);
+ }
+
+ // Check if it's time to send delayed acks
+ if (m_t_ack && (talk_base::TimeDiff(m_t_ack + ACK_DELAY, now) <= 0)) {
+ packet(m_snd_nxt, 0, 0, 0);
+ }
+
+#if PSEUDO_KEEPALIVE
+ // Check for idle timeout
+ if ((m_state == TCP_ESTABLISHED) && (TimeDiff(m_lastrecv + IDLE_TIMEOUT, now) <= 0)) {
+ closedown(ECONNABORTED);
+ return;
+ }
+
+ // Check for ping timeout (to keep udp mapping open)
+ if ((m_state == TCP_ESTABLISHED) && (TimeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3/2 : IDLE_PING), now) <= 0)) {
+ packet(m_snd_nxt, 0, 0, 0);
+ }
+#endif // PSEUDO_KEEPALIVE
+}
+
+bool
+PseudoTcp::NotifyPacket(const char * buffer, size_t len) {
+ if (len > MAX_PACKET) {
+ LOG_F(WARNING) << "packet too large";
+ return false;
+ }
+ return parse(reinterpret_cast<const uint8 *>(buffer), uint32(len));
+}
+
+bool
+PseudoTcp::GetNextClock(uint32 now, long& timeout) {
+ return clock_check(now, timeout);
+}
+
+//
+// IPStream Implementation
+//
+
+int
+PseudoTcp::Recv(char * buffer, size_t len) {
+ if (m_state != TCP_ESTABLISHED) {
+ m_error = ENOTCONN;
+ return SOCKET_ERROR;
+ }
+
+ if (m_rlen == 0) {
+ m_bReadEnable = true;
+ m_error = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+
+ uint32 read = talk_base::_min(uint32(len), m_rlen);
+ memcpy(buffer, m_rbuf, read);
+ m_rlen -= read;
+
+ // !?! until we create a circular buffer, we need to move all of the rest of the buffer up!
+ memmove(m_rbuf, m_rbuf + read, sizeof(m_rbuf) - read/*m_rlen*/);
+
+ if ((sizeof(m_rbuf) - m_rlen - m_rcv_wnd)
+ >= talk_base::_min<uint32>(sizeof(m_rbuf) / 2, m_mss)) {
+ bool bWasClosed = (m_rcv_wnd == 0); // !?! Not sure about this was closed business
+
+ m_rcv_wnd = sizeof(m_rbuf) - m_rlen;
+
+ if (bWasClosed) {
+ attemptSend(sfImmediateAck);
+ }
+ }
+
+ return read;
+}
+
+int
+PseudoTcp::Send(const char * buffer, size_t len) {
+ if (m_state != TCP_ESTABLISHED) {
+ m_error = ENOTCONN;
+ return SOCKET_ERROR;
+ }
+
+ if (m_slen == sizeof(m_sbuf)) {
+ m_bWriteEnable = true;
+ m_error = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+
+ int written = queue(buffer, uint32(len), false);
+ attemptSend();
+ return written;
+}
+
+void
+PseudoTcp::Close(bool force) {
+ LOG_F(LS_VERBOSE) << "(" << (force ? "true" : "false") << ")";
+ m_shutdown = force ? SD_FORCEFUL : SD_GRACEFUL;
+}
+
+int PseudoTcp::GetError() {
+ return m_error;
+}
+
+//
+// Internal Implementation
+//
+
+uint32
+PseudoTcp::queue(const char * data, uint32 len, bool bCtrl) {
+ if (len > sizeof(m_sbuf) - m_slen) {
+ ASSERT(!bCtrl);
+ len = sizeof(m_sbuf) - m_slen;
+ }
+
+ // We can concatenate data if the last segment is the same type
+ // (control v. regular data), and has not been transmitted yet
+ if (!m_slist.empty() && (m_slist.back().bCtrl == bCtrl) && (m_slist.back().xmit == 0)) {
+ m_slist.back().len += len;
+ } else {
+ SSegment sseg(m_snd_una + m_slen, len, bCtrl);
+ m_slist.push_back(sseg);
+ }
+
+ memcpy(m_sbuf + m_slen, data, len);
+ m_slen += len;
+ //LOG(LS_INFO) << "PseudoTcp::queue - m_slen = " << m_slen;
+ return len;
+}
+
+IPseudoTcpNotify::WriteResult
+PseudoTcp::packet(uint32 seq, uint8 flags, const char * data, uint32 len) {
+ ASSERT(HEADER_SIZE + len <= MAX_PACKET);
+
+ uint32 now = Now();
+
+ uint8 buffer[MAX_PACKET];
+ long_to_bytes(m_conv, buffer);
+ long_to_bytes(seq, buffer + 4);
+ long_to_bytes(m_rcv_nxt, buffer + 8);
+ buffer[12] = 0;
+ buffer[13] = flags;
+ short_to_bytes(uint16(m_rcv_wnd), buffer + 14);
+
+ // Timestamp computations
+ long_to_bytes(now, buffer + 16);
+ long_to_bytes(m_ts_recent, buffer + 20);
+ m_ts_lastack = m_rcv_nxt;
+
+ memcpy(buffer + HEADER_SIZE, data, len);
+
+#if _DEBUGMSG >= _DBG_VERBOSE
+ LOG(LS_INFO) << "<-- <CONV=" << m_conv
+ << "><FLG=" << static_cast<unsigned>(flags)
+ << "><SEQ=" << seq << ":" << seq + len
+ << "><ACK=" << m_rcv_nxt
+ << "><WND=" << m_rcv_wnd
+ << "><TS=" << (now % 10000)
+ << "><TSR=" << (m_ts_recent % 10000)
+ << "><LEN=" << len << ">";
+#endif // _DEBUGMSG
+
+ IPseudoTcpNotify::WriteResult wres = m_notify->TcpWritePacket(this, reinterpret_cast<char *>(buffer), len + HEADER_SIZE);
+ // Note: When data is NULL, this is an ACK packet. We don't read the return value for those,
+ // and thus we won't retry. So go ahead and treat the packet as a success (basically simulate
+ // as if it were dropped), which will prevent our timers from being messed up.
+ if ((wres != IPseudoTcpNotify::WR_SUCCESS) && (NULL != data))
+ return wres;
+
+ m_t_ack = 0;
+ if (len > 0) {
+ m_lastsend = now;
+ }
+ m_lasttraffic = now;
+ m_bOutgoing = true;
+
+ return IPseudoTcpNotify::WR_SUCCESS;
+}
+
+bool
+PseudoTcp::parse(const uint8 * buffer, uint32 size) {
+ if (size < 12)
+ return false;
+
+ Segment seg;
+ seg.conv = bytes_to_long(buffer);
+ seg.seq = bytes_to_long(buffer + 4);
+ seg.ack = bytes_to_long(buffer + 8);
+ seg.flags = buffer[13];
+ seg.wnd = bytes_to_short(buffer + 14);
+
+ seg.tsval = bytes_to_long(buffer + 16);
+ seg.tsecr = bytes_to_long(buffer + 20);
+
+ seg.data = reinterpret_cast<const char *>(buffer) + HEADER_SIZE;
+ seg.len = size - HEADER_SIZE;
+
+#if _DEBUGMSG >= _DBG_VERBOSE
+ LOG(LS_INFO) << "--> <CONV=" << seg.conv
+ << "><FLG=" << static_cast<unsigned>(seg.flags)
+ << "><SEQ=" << seg.seq << ":" << seg.seq + seg.len
+ << "><ACK=" << seg.ack
+ << "><WND=" << seg.wnd
+ << "><TS=" << (seg.tsval % 10000)
+ << "><TSR=" << (seg.tsecr % 10000)
+ << "><LEN=" << seg.len << ">";
+#endif // _DEBUGMSG
+
+ return process(seg);
+}
+
+bool
+PseudoTcp::clock_check(uint32 now, long& nTimeout) {
+ if (m_shutdown == SD_FORCEFUL)
+ return false;
+
+ if ((m_shutdown == SD_GRACEFUL)
+ && ((m_state != TCP_ESTABLISHED)
+ || ((m_slen == 0) && (m_t_ack == 0)))) {
+ return false;
+ }
+
+ if (m_state == TCP_CLOSED) {
+ nTimeout = CLOSED_TIMEOUT;
+ return true;
+ }
+
+ nTimeout = DEFAULT_TIMEOUT;
+
+ if (m_t_ack) {
+ nTimeout = talk_base::_min<int32>(nTimeout,
+ talk_base::TimeDiff(m_t_ack + ACK_DELAY, now));
+ }
+ if (m_rto_base) {
+ nTimeout = talk_base::_min<int32>(nTimeout,
+ talk_base::TimeDiff(m_rto_base + m_rx_rto, now));
+ }
+ if (m_snd_wnd == 0) {
+ nTimeout = talk_base::_min<int32>(nTimeout, talk_base::TimeDiff(m_lastsend + m_rx_rto, now));
+ }
+#if PSEUDO_KEEPALIVE
+ if (m_state == TCP_ESTABLISHED) {
+ nTimeout = talk_base::_min<int32>(nTimeout,
+ talk_base::TimeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3/2 : IDLE_PING), now));
+ }
+#endif // PSEUDO_KEEPALIVE
+ return true;
+}
+
+bool
+PseudoTcp::process(Segment& seg) {
+ // If this is the wrong conversation, send a reset!?! (with the correct conversation?)
+ if (seg.conv != m_conv) {
+ //if ((seg.flags & FLAG_RST) == 0) {
+ // packet(tcb, seg.ack, 0, FLAG_RST, 0, 0);
+ //}
+ LOG_F(LS_ERROR) << "wrong conversation";
+ return false;
+ }
+
+ uint32 now = Now();
+ m_lasttraffic = m_lastrecv = now;
+ m_bOutgoing = false;
+
+ if (m_state == TCP_CLOSED) {
+ // !?! send reset?
+ LOG_F(LS_ERROR) << "closed";
+ return false;
+ }
+
+ // Check if this is a reset segment
+ if (seg.flags & FLAG_RST) {
+ closedown(ECONNRESET);
+ return false;
+ }
+
+ // Check for control data
+ bool bConnect = false;
+ if (seg.flags & FLAG_CTL) {
+ if (seg.len == 0) {
+ LOG_F(LS_ERROR) << "Missing control code";
+ return false;
+ } else if (seg.data[0] == CTL_CONNECT) {
+ bConnect = true;
+ if (m_state == TCP_LISTEN) {
+ m_state = TCP_SYN_RECEIVED;
+ LOG(LS_INFO) << "State: TCP_SYN_RECEIVED";
+ //m_notify->associate(addr);
+ char buffer[1];
+ buffer[0] = CTL_CONNECT;
+ queue(buffer, 1, true);
+ } else if (m_state == TCP_SYN_SENT) {
+ m_state = TCP_ESTABLISHED;
+ LOG(LS_INFO) << "State: TCP_ESTABLISHED";
+ adjustMTU();
+ if (m_notify) {
+ m_notify->OnTcpOpen(this);
+ }
+ //notify(evOpen);
+ }
+ } else {
+ LOG_F(LS_WARNING) << "Unknown control code: " << seg.data[0];
+ return false;
+ }
+ }
+
+ // Update timestamp
+ if ((seg.seq <= m_ts_lastack) && (m_ts_lastack < seg.seq + seg.len)) {
+ m_ts_recent = seg.tsval;
+ }
+
+ // Check if this is a valuable ack
+ if ((seg.ack > m_snd_una) && (seg.ack <= m_snd_nxt)) {
+ // Calculate round-trip time
+ if (seg.tsecr) {
+ long rtt = talk_base::TimeDiff(now, seg.tsecr);
+ if (rtt >= 0) {
+ if (m_rx_srtt == 0) {
+ m_rx_srtt = rtt;
+ m_rx_rttvar = rtt / 2;
+ } else {
+ m_rx_rttvar = (3 * m_rx_rttvar + abs(long(rtt - m_rx_srtt))) / 4;
+ m_rx_srtt = (7 * m_rx_srtt + rtt) / 8;
+ }
+ m_rx_rto = bound(MIN_RTO, m_rx_srtt +
+ talk_base::_max<uint32>(1, 4 * m_rx_rttvar), MAX_RTO);
+#if _DEBUGMSG >= _DBG_VERBOSE
+ LOG(LS_INFO) << "rtt: " << rtt
+ << " srtt: " << m_rx_srtt
+ << " rto: " << m_rx_rto;
+#endif // _DEBUGMSG
+ } else {
+ ASSERT(false);
+ }
+ }
+
+ m_snd_wnd = seg.wnd;
+
+ uint32 nAcked = seg.ack - m_snd_una;
+ m_snd_una = seg.ack;
+
+ m_rto_base = (m_snd_una == m_snd_nxt) ? 0 : now;
+
+ m_slen -= nAcked;
+ memmove(m_sbuf, m_sbuf + nAcked, m_slen);
+ //LOG(LS_INFO) << "PseudoTcp::process - m_slen = " << m_slen;
+
+ for (uint32 nFree = nAcked; nFree > 0; ) {
+ ASSERT(!m_slist.empty());
+ if (nFree < m_slist.front().len) {
+ m_slist.front().len -= nFree;
+ nFree = 0;
+ } else {
+ if (m_slist.front().len > m_largest) {
+ m_largest = m_slist.front().len;
+ }
+ nFree -= m_slist.front().len;
+ m_slist.pop_front();
+ }
+ }
+
+ if (m_dup_acks >= 3) {
+ if (m_snd_una >= m_recover) { // NewReno
+ uint32 nInFlight = m_snd_nxt - m_snd_una;
+ m_cwnd = talk_base::_min(m_ssthresh, nInFlight + m_mss); // (Fast Retransmit)
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "exit recovery";
+#endif // _DEBUGMSG
+ m_dup_acks = 0;
+ } else {
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "recovery retransmit";
+#endif // _DEBUGMSG
+ if (!transmit(m_slist.begin(), now)) {
+ closedown(ECONNABORTED);
+ return false;
+ }
+ m_cwnd += m_mss - talk_base::_min(nAcked, m_cwnd);
+ }
+ } else {
+ m_dup_acks = 0;
+ // Slow start, congestion avoidance
+ if (m_cwnd < m_ssthresh) {
+ m_cwnd += m_mss;
+ } else {
+ m_cwnd += talk_base::_max<uint32>(1, m_mss * m_mss / m_cwnd);
+ }
+ }
+
+ // !?! A bit hacky
+ if ((m_state == TCP_SYN_RECEIVED) && !bConnect) {
+ m_state = TCP_ESTABLISHED;
+ LOG(LS_INFO) << "State: TCP_ESTABLISHED";
+ adjustMTU();
+ if (m_notify) {
+ m_notify->OnTcpOpen(this);
+ }
+ //notify(evOpen);
+ }
+
+ // If we make room in the send queue, notify the user
+ // The goal it to make sure we always have at least enough data to fill the
+ // window. We'd like to notify the app when we are halfway to that point.
+ const uint32 kIdealRefillSize = (sizeof(m_sbuf) + sizeof(m_rbuf)) / 2;
+ if (m_bWriteEnable && (m_slen < kIdealRefillSize)) {
+ m_bWriteEnable = false;
+ if (m_notify) {
+ m_notify->OnTcpWriteable(this);
+ }
+ //notify(evWrite);
+ }
+ } else if (seg.ack == m_snd_una) {
+ // !?! Note, tcp says don't do this... but otherwise how does a closed window become open?
+ m_snd_wnd = seg.wnd;
+
+ // Check duplicate acks
+ if (seg.len > 0) {
+ // it's a dup ack, but with a data payload, so don't modify m_dup_acks
+ } else if (m_snd_una != m_snd_nxt) {
+ m_dup_acks += 1;
+ if (m_dup_acks == 3) { // (Fast Retransmit)
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "enter recovery";
+ LOG(LS_INFO) << "recovery retransmit";
+#endif // _DEBUGMSG
+ if (!transmit(m_slist.begin(), now)) {
+ closedown(ECONNABORTED);
+ return false;
+ }
+ m_recover = m_snd_nxt;
+ uint32 nInFlight = m_snd_nxt - m_snd_una;
+ m_ssthresh = talk_base::_max(nInFlight / 2, 2 * m_mss);
+ //LOG(LS_INFO) << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss;
+ m_cwnd = m_ssthresh + 3 * m_mss;
+ } else if (m_dup_acks > 3) {
+ m_cwnd += m_mss;
+ }
+ } else {
+ m_dup_acks = 0;
+ }
+ }
+
+ // Conditions were acks must be sent:
+ // 1) Segment is too old (they missed an ACK) (immediately)
+ // 2) Segment is too new (we missed a segment) (immediately)
+ // 3) Segment has data (so we need to ACK!) (delayed)
+ // ... so the only time we don't need to ACK, is an empty segment that points to rcv_nxt!
+
+ SendFlags sflags = sfNone;
+ if (seg.seq != m_rcv_nxt) {
+ sflags = sfImmediateAck; // (Fast Recovery)
+ } else if (seg.len != 0) {
+ sflags = sfDelayedAck;
+ }
+#if _DEBUGMSG >= _DBG_NORMAL
+ if (sflags == sfImmediateAck) {
+ if (seg.seq > m_rcv_nxt) {
+ LOG_F(LS_INFO) << "too new";
+ } else if (seg.seq + seg.len <= m_rcv_nxt) {
+ LOG_F(LS_INFO) << "too old";
+ }
+ }
+#endif // _DEBUGMSG
+
+ // Adjust the incoming segment to fit our receive buffer
+ if (seg.seq < m_rcv_nxt) {
+ uint32 nAdjust = m_rcv_nxt - seg.seq;
+ if (nAdjust < seg.len) {
+ seg.seq += nAdjust;
+ seg.data += nAdjust;
+ seg.len -= nAdjust;
+ } else {
+ seg.len = 0;
+ }
+ }
+ if ((seg.seq + seg.len - m_rcv_nxt) > (sizeof(m_rbuf) - m_rlen)) {
+ uint32 nAdjust = seg.seq + seg.len - m_rcv_nxt - (sizeof(m_rbuf) - m_rlen);
+ if (nAdjust < seg.len) {
+ seg.len -= nAdjust;
+ } else {
+ seg.len = 0;
+ }
+ }
+
+ bool bIgnoreData = (seg.flags & FLAG_CTL) || (m_shutdown != SD_NONE);
+ bool bNewData = false;
+
+ if (seg.len > 0) {
+ if (bIgnoreData) {
+ if (seg.seq == m_rcv_nxt) {
+ m_rcv_nxt += seg.len;
+ }
+ } else {
+ uint32 nOffset = seg.seq - m_rcv_nxt;
+ memcpy(m_rbuf + m_rlen + nOffset, seg.data, seg.len);
+ if (seg.seq == m_rcv_nxt) {
+ m_rlen += seg.len;
+ m_rcv_nxt += seg.len;
+ m_rcv_wnd -= seg.len;
+ bNewData = true;
+
+ RList::iterator it = m_rlist.begin();
+ while ((it != m_rlist.end()) && (it->seq <= m_rcv_nxt)) {
+ if (it->seq + it->len > m_rcv_nxt) {
+ sflags = sfImmediateAck; // (Fast Recovery)
+ uint32 nAdjust = (it->seq + it->len) - m_rcv_nxt;
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "Recovered " << nAdjust << " bytes (" << m_rcv_nxt << " -> " << m_rcv_nxt + nAdjust << ")";
+#endif // _DEBUGMSG
+ m_rlen += nAdjust;
+ m_rcv_nxt += nAdjust;
+ m_rcv_wnd -= nAdjust;
+ }
+ it = m_rlist.erase(it);
+ }
+ } else {
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "Saving " << seg.len << " bytes (" << seg.seq << " -> " << seg.seq + seg.len << ")";
+#endif // _DEBUGMSG
+ RSegment rseg;
+ rseg.seq = seg.seq;
+ rseg.len = seg.len;
+ RList::iterator it = m_rlist.begin();
+ while ((it != m_rlist.end()) && (it->seq < rseg.seq)) {
+ ++it;
+ }
+ m_rlist.insert(it, rseg);
+ }
+ }
+ }
+
+ attemptSend(sflags);
+
+ // If we have new data, notify the user
+ if (bNewData && m_bReadEnable) {
+ m_bReadEnable = false;
+ if (m_notify) {
+ m_notify->OnTcpReadable(this);
+ }
+ //notify(evRead);
+ }
+
+ return true;
+}
+
+bool
+PseudoTcp::transmit(const SList::iterator& seg, uint32 now) {
+ if (seg->xmit >= ((m_state == TCP_ESTABLISHED) ? 15 : 30)) {
+ LOG_F(LS_VERBOSE) << "too many retransmits";
+ return false;
+ }
+
+ uint32 nTransmit = talk_base::_min(seg->len, m_mss);
+
+ while (true) {
+ uint32 seq = seg->seq;
+ uint8 flags = (seg->bCtrl ? FLAG_CTL : 0);
+ const char * buffer = m_sbuf + (seg->seq - m_snd_una);
+ IPseudoTcpNotify::WriteResult wres = this->packet(seq, flags, buffer, nTransmit);
+
+ if (wres == IPseudoTcpNotify::WR_SUCCESS)
+ break;
+
+ if (wres == IPseudoTcpNotify::WR_FAIL) {
+ LOG_F(LS_VERBOSE) << "packet failed";
+ return false;
+ }
+
+ ASSERT(wres == IPseudoTcpNotify::WR_TOO_LARGE);
+
+ while (true) {
+ if (PACKET_MAXIMUMS[m_msslevel + 1] == 0) {
+ LOG_F(LS_VERBOSE) << "MTU too small";
+ return false;
+ }
+ // !?! We need to break up all outstanding and pending packets and then retransmit!?!
+
+ m_mss = PACKET_MAXIMUMS[++m_msslevel] - PACKET_OVERHEAD;
+ m_cwnd = 2 * m_mss; // I added this... haven't researched actual formula
+ if (m_mss < nTransmit) {
+ nTransmit = m_mss;
+ break;
+ }
+ }
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "Adjusting mss to " << m_mss << " bytes";
+#endif // _DEBUGMSG
+ }
+
+ if (nTransmit < seg->len) {
+ LOG_F(LS_VERBOSE) << "mss reduced to " << m_mss;
+
+ SSegment subseg(seg->seq + nTransmit, seg->len - nTransmit, seg->bCtrl);
+ //subseg.tstamp = seg->tstamp;
+ subseg.xmit = seg->xmit;
+ seg->len = nTransmit;
+
+ SList::iterator next = seg;
+ m_slist.insert(++next, subseg);
+ }
+
+ if (seg->xmit == 0) {
+ m_snd_nxt += seg->len;
+ }
+ seg->xmit += 1;
+ //seg->tstamp = now;
+ if (m_rto_base == 0) {
+ m_rto_base = now;
+ }
+
+ return true;
+}
+
+void
+PseudoTcp::attemptSend(SendFlags sflags) {
+ uint32 now = Now();
+
+ if (talk_base::TimeDiff(now, m_lastsend) > static_cast<long>(m_rx_rto)) {
+ m_cwnd = m_mss;
+ }
+
+#if _DEBUGMSG
+ bool bFirst = true;
+ UNUSED(bFirst);
+#endif // _DEBUGMSG
+
+ while (true) {
+ uint32 cwnd = m_cwnd;
+ if ((m_dup_acks == 1) || (m_dup_acks == 2)) { // Limited Transmit
+ cwnd += m_dup_acks * m_mss;
+ }
+ uint32 nWindow = talk_base::_min(m_snd_wnd, cwnd);
+ uint32 nInFlight = m_snd_nxt - m_snd_una;
+ uint32 nUseable = (nInFlight < nWindow) ? (nWindow - nInFlight) : 0;
+
+ uint32 nAvailable = talk_base::_min(m_slen - nInFlight, m_mss);
+
+ if (nAvailable > nUseable) {
+ if (nUseable * 4 < nWindow) {
+ // RFC 813 - avoid SWS
+ nAvailable = 0;
+ } else {
+ nAvailable = nUseable;
+ }
+ }
+
+#if _DEBUGMSG >= _DBG_VERBOSE
+ if (bFirst) {
+ bFirst = false;
+ LOG(LS_INFO) << "[cwnd: " << m_cwnd
+ << " nWindow: " << nWindow
+ << " nInFlight: " << nInFlight
+ << " nAvailable: " << nAvailable
+ << " nQueued: " << m_slen - nInFlight
+ << " nEmpty: " << sizeof(m_sbuf) - m_slen
+ << " ssthresh: " << m_ssthresh << "]";
+ }
+#endif // _DEBUGMSG
+
+ if (nAvailable == 0) {
+ if (sflags == sfNone)
+ return;
+
+ // If this is an immediate ack, or the second delayed ack
+ if ((sflags == sfImmediateAck) || m_t_ack) {
+ packet(m_snd_nxt, 0, 0, 0);
+ } else {
+ m_t_ack = Now();
+ }
+ return;
+ }
+
+ // Nagle algorithm
+ if ((m_snd_nxt > m_snd_una) && (nAvailable < m_mss)) {
+ return;
+ }
+
+ // Find the next segment to transmit
+ SList::iterator it = m_slist.begin();
+ while (it->xmit > 0) {
+ ++it;
+ ASSERT(it != m_slist.end());
+ }
+ SList::iterator seg = it;
+
+ // If the segment is too large, break it into two
+ if (seg->len > nAvailable) {
+ SSegment subseg(seg->seq + nAvailable, seg->len - nAvailable, seg->bCtrl);
+ seg->len = nAvailable;
+ m_slist.insert(++it, subseg);
+ }
+
+ if (!transmit(seg, now)) {
+ LOG_F(LS_VERBOSE) << "transmit failed";
+ // TODO: consider closing socket
+ return;
+ }
+
+ sflags = sfNone;
+ }
+}
+
+void
+PseudoTcp::closedown(uint32 err) {
+ m_slen = 0;
+
+ LOG(LS_INFO) << "State: TCP_CLOSED";
+ m_state = TCP_CLOSED;
+ if (m_notify) {
+ m_notify->OnTcpClosed(this, err);
+ }
+ //notify(evClose, err);
+}
+
+void
+PseudoTcp::adjustMTU() {
+ // Determine our current mss level, so that we can adjust appropriately later
+ for (m_msslevel = 0; PACKET_MAXIMUMS[m_msslevel + 1] > 0; ++m_msslevel) {
+ if (static_cast<uint16>(PACKET_MAXIMUMS[m_msslevel]) <= m_mtu_advise) {
+ break;
+ }
+ }
+ m_mss = m_mtu_advise - PACKET_OVERHEAD;
+ // !?! Should we reset m_largest here?
+#if _DEBUGMSG >= _DBG_NORMAL
+ LOG(LS_INFO) << "Adjusting mss to " << m_mss << " bytes";
+#endif // _DEBUGMSG
+ // Enforce minimums on ssthresh and cwnd
+ m_ssthresh = talk_base::_max(m_ssthresh, 2 * m_mss);
+ m_cwnd = talk_base::_max(m_cwnd, m_mss);
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/pseudotcp.h b/third_party/libjingle/source/talk/p2p/base/pseudotcp.h
new file mode 100644
index 0000000..7b0e01f
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/pseudotcp.h
@@ -0,0 +1,183 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PSEUDOTCP_H__
+#define __PSEUDOTCP_H__
+
+#include <list>
+#include "talk/base/basictypes.h"
+
+namespace cricket {
+
+//////////////////////////////////////////////////////////////////////
+// IPseudoTcpNotify
+//////////////////////////////////////////////////////////////////////
+
+class PseudoTcp;
+
+class IPseudoTcpNotify {
+public:
+ virtual ~IPseudoTcpNotify() {}
+ // Notification of tcp events
+ virtual void OnTcpOpen(PseudoTcp * tcp) = 0;
+ virtual void OnTcpReadable(PseudoTcp * tcp) = 0;
+ virtual void OnTcpWriteable(PseudoTcp * tcp) = 0;
+ virtual void OnTcpClosed(PseudoTcp * tcp, uint32 nError) = 0;
+
+ // Write the packet onto the network
+ enum WriteResult { WR_SUCCESS, WR_TOO_LARGE, WR_FAIL };
+ virtual WriteResult TcpWritePacket(PseudoTcp * tcp, const char * buffer, size_t len) = 0;
+};
+
+//////////////////////////////////////////////////////////////////////
+// PseudoTcp
+//////////////////////////////////////////////////////////////////////
+
+class PseudoTcp {
+public:
+ static uint32 Now();
+
+ PseudoTcp(IPseudoTcpNotify * notify, uint32 conv);
+ virtual ~PseudoTcp();
+
+ int Connect();
+ int Recv(char * buffer, size_t len);
+ int Send(const char * buffer, size_t len);
+ void Close(bool force);
+ int GetError();
+
+ enum TcpState { TCP_LISTEN, TCP_SYN_SENT, TCP_SYN_RECEIVED, TCP_ESTABLISHED, TCP_CLOSED };
+ TcpState State() const { return m_state; }
+
+ // Call this when the PMTU changes.
+ void NotifyMTU(uint16 mtu);
+
+ // Call this based on timeout value returned from GetNextClock.
+ // It's ok to call this too frequently.
+ void NotifyClock(uint32 now);
+
+ // Call this whenever a packet arrives.
+ // Returns true if the packet was processed successfully.
+ bool NotifyPacket(const char * buffer, size_t len);
+
+ // Call this to determine the next time NotifyClock should be called.
+ // Returns false if the socket is ready to be destroyed.
+ bool GetNextClock(uint32 now, long& timeout);
+
+protected:
+ enum SendFlags { sfNone, sfDelayedAck, sfImmediateAck };
+ enum {
+ // Note: can't go as high as 1024 * 64, because of uint16 precision
+ kRcvBufSize = 1024 * 60,
+ // Note: send buffer should be larger to make sure we can always fill the
+ // receiver window
+ kSndBufSize = 1024 * 90
+ };
+
+ struct Segment {
+ uint32 conv, seq, ack;
+ uint8 flags;
+ uint16 wnd;
+ const char * data;
+ uint32 len;
+ uint32 tsval, tsecr;
+ };
+
+ struct SSegment {
+ uint32 seq, len;
+ //uint32 tstamp;
+ uint8 xmit;
+ bool bCtrl;
+
+ SSegment(uint32 s, uint32 l, bool c) : seq(s), len(l), /*tstamp(0),*/ xmit(0), bCtrl(c) { }
+ };
+ typedef std::list<SSegment> SList;
+
+ struct RSegment {
+ uint32 seq, len;
+ };
+
+ uint32 queue(const char * data, uint32 len, bool bCtrl);
+
+ IPseudoTcpNotify::WriteResult packet(uint32 seq, uint8 flags, const char * data, uint32 len);
+ bool parse(const uint8 * buffer, uint32 size);
+
+ void attemptSend(SendFlags sflags = sfNone);
+
+ void closedown(uint32 err = 0);
+
+ bool clock_check(uint32 now, long& nTimeout);
+
+ bool process(Segment& seg);
+ bool transmit(const SList::iterator& seg, uint32 now);
+
+ void adjustMTU();
+
+private:
+ IPseudoTcpNotify * m_notify;
+ enum Shutdown { SD_NONE, SD_GRACEFUL, SD_FORCEFUL } m_shutdown;
+ int m_error;
+
+ // TCB data
+ TcpState m_state;
+ uint32 m_conv;
+ bool m_bReadEnable, m_bWriteEnable, m_bOutgoing;
+ uint32 m_lasttraffic;
+
+ // Incoming data
+ typedef std::list<RSegment> RList;
+ RList m_rlist;
+ char m_rbuf[kRcvBufSize];
+ uint32 m_rcv_nxt, m_rcv_wnd, m_rlen, m_lastrecv;
+
+ // Outgoing data
+ SList m_slist;
+ char m_sbuf[kSndBufSize];
+ uint32 m_snd_nxt, m_snd_wnd, m_slen, m_lastsend, m_snd_una;
+ // Maximum segment size, estimated protocol level, largest segment sent
+ uint32 m_mss, m_msslevel, m_largest, m_mtu_advise;
+ // Retransmit timer
+ uint32 m_rto_base;
+
+ // Timestamp tracking
+ uint32 m_ts_recent, m_ts_lastack;
+
+ // Round-trip calculation
+ uint32 m_rx_rttvar, m_rx_srtt, m_rx_rto;
+
+ // Congestion avoidance, Fast retransmit/recovery, Delayed ACKs
+ uint32 m_ssthresh, m_cwnd;
+ uint8 m_dup_acks;
+ uint32 m_recover;
+ uint32 m_t_ack;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __PSEUDOTCP_H__
diff --git a/third_party/libjingle/source/talk/p2p/base/rawtransport.cc b/third_party/libjingle/source/talk/p2p/base/rawtransport.cc
new file mode 100644
index 0000000..da7c69e
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/rawtransport.cc
@@ -0,0 +1,124 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "talk/p2p/base/rawtransport.h"
+#include "talk/base/common.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/rawtransportchannel.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+#if defined(FEATURE_ENABLE_PSTN)
+namespace cricket {
+
+RawTransport::RawTransport(talk_base::Thread* worker_thread,
+ PortAllocator* allocator)
+ : Transport(worker_thread, NS_GINGLE_RAW, allocator) {
+}
+
+RawTransport::~RawTransport() {
+ DestroyAllChannels();
+}
+
+bool RawTransport::ParseCandidates(const buzz::XmlElement* elem,
+ Candidates* candidates,
+ ParseError* error) {
+ ASSERT(elem->FirstChild() == NULL);
+ for (const buzz::XmlElement* cand_elem = elem->FirstElement();
+ cand_elem != NULL;
+ cand_elem = cand_elem->NextElement()) {
+ if (cand_elem->Name() == QN_GINGLE_RAW_CHANNEL) {
+ talk_base::SocketAddress addr;
+ if (!ParseRawAddress(cand_elem, &addr, error))
+ return false;
+
+ Candidate candidate;
+ candidate.set_name(cand_elem->Attr(buzz::QN_NAME));
+ candidate.set_address(addr);
+ candidates->push_back(candidate);
+ }
+ }
+ return true;
+}
+
+void RawTransport::WriteCandidates(const Candidates& candidates,
+ SignalingProtocol protocol,
+ XmlElements* candidate_elems) {
+ for (std::vector<Candidate>::const_iterator
+ cand = candidates.begin();
+ cand != candidates.end();
+ ++cand) {
+ ASSERT(cand->protocol() == "udp");
+ talk_base::SocketAddress addr = cand->address();
+
+ buzz::XmlElement* elem = new buzz::XmlElement(QN_GINGLE_RAW_CHANNEL);
+ elem->SetAttr(buzz::QN_NAME, name());
+ elem->SetAttr(QN_ADDRESS, addr.IPAsString());
+ elem->SetAttr(QN_PORT, addr.PortAsString());
+ candidate_elems->push_back(elem);
+ }
+}
+
+bool RawTransport::ParseRawAddress(const buzz::XmlElement* elem,
+ talk_base::SocketAddress* addr,
+ ParseError* error) {
+ // Make sure the required attributes exist
+ if (!elem->HasAttr(buzz::QN_NAME) ||
+ !elem->HasAttr(QN_ADDRESS) ||
+ !elem->HasAttr(QN_PORT)) {
+ return BadParse("channel missing required attribute", error);
+ }
+
+ // Make sure the channel named actually exists.
+ if (!HasChannel(elem->Attr(buzz::QN_NAME)))
+ return BadParse("channel named does not exist", error);
+
+ // Parse the address.
+ if (!ParseAddress(elem, QN_ADDRESS, QN_PORT, addr, error))
+ return false;
+
+ return true;
+}
+
+TransportChannelImpl* RawTransport::CreateTransportChannel(
+ const std::string& name, const std::string& session_type) {
+ return new RawTransportChannel(name, session_type, this,
+ worker_thread(),
+ port_allocator());
+}
+
+void RawTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
+ delete channel;
+}
+
+} // namespace cricket
+#endif // defined(FEATURE_ENABLE_PSTN)
diff --git a/third_party/libjingle/source/talk/p2p/base/rawtransport.h b/third_party/libjingle/source/talk/p2p/base/rawtransport.h
new file mode 100644
index 0000000..b5a5054
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/rawtransport.h
@@ -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.
+ */
+
+#ifndef TALK_P2P_BASE_RAWTRANSPORT_H_
+#define TALK_P2P_BASE_RAWTRANSPORT_H_
+
+#include <string>
+#include "talk/p2p/base/transport.h"
+
+#if defined(FEATURE_ENABLE_PSTN)
+namespace cricket {
+
+// Implements a transport that only sends raw packets, no STUN. As a result,
+// it cannot do pings to determine connectivity, so it only uses a single port
+// that it thinks will work.
+class RawTransport: public Transport {
+ public:
+ RawTransport(talk_base::Thread* worker_thread, PortAllocator* allocator);
+ virtual ~RawTransport();
+
+ virtual bool ParseCandidates(const buzz::XmlElement* elem,
+ Candidates* candidates,
+ ParseError* error);
+ virtual void WriteCandidates(const Candidates& candidates,
+ SignalingProtocol protocol,
+ XmlElements* candidate_elems);
+
+ protected:
+ // Creates and destroys raw channels.
+ virtual TransportChannelImpl* CreateTransportChannel(
+ const std::string& name, const std::string &session_type);
+ virtual void DestroyTransportChannel(TransportChannelImpl* channel);
+
+ private:
+ // Parses the given element, which should describe the address to use for a
+ // given channel. This will return false and signal an error if the address
+ // or channel name is bad.
+ bool ParseRawAddress(const buzz::XmlElement* elem,
+ talk_base::SocketAddress* addr,
+ ParseError* error);
+
+ friend class RawTransportChannel; // For ParseAddress.
+
+ DISALLOW_EVIL_CONSTRUCTORS(RawTransport);
+};
+
+} // namespace cricket
+
+#endif // defined(FEATURE_ENABLE_PSTN)
+
+#endif // TALK_P2P_BASE_RAWTRANSPORT_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc b/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc
new file mode 100644
index 0000000..b3beffb
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc
@@ -0,0 +1,278 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/rawtransportchannel.h"
+
+#include <string>
+#include <vector>
+#include "talk/base/common.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/rawtransport.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+#if defined(FEATURE_ENABLE_PSTN)
+
+namespace {
+
+const uint32 MSG_DESTROY_UNUSED_PORTS = 1;
+
+} // namespace
+
+namespace cricket {
+
+RawTransportChannel::RawTransportChannel(const std::string &name,
+ const std::string &session_type,
+ RawTransport* transport,
+ talk_base::Thread *worker_thread,
+ PortAllocator *allocator)
+ : TransportChannelImpl(name, session_type),
+ raw_transport_(transport),
+ allocator_(allocator),
+ allocator_session_(NULL),
+ stun_port_(NULL),
+ relay_port_(NULL),
+ port_(NULL),
+ use_relay_(false) {
+ if (worker_thread == NULL)
+ worker_thread_ = raw_transport_->worker_thread();
+ else
+ worker_thread_ = worker_thread;
+}
+
+RawTransportChannel::~RawTransportChannel() {
+ delete allocator_session_;
+}
+
+int RawTransportChannel::SendPacket(const char *data, size_t size) {
+ if (port_ == NULL)
+ return -1;
+ if (remote_address_.IsAny())
+ return -1;
+ return port_->SendTo(data, size, remote_address_, true);
+}
+
+int RawTransportChannel::SetOption(talk_base::Socket::Option opt, int value) {
+ // TODO: allow these to be set before we have a port
+ if (port_ == NULL)
+ return -1;
+ return port_->SetOption(opt, value);
+}
+
+int RawTransportChannel::GetError() {
+ return (port_ != NULL) ? port_->GetError() : 0;
+}
+
+void RawTransportChannel::Connect() {
+ // Create an allocator that only returns stun and relay ports.
+ allocator_session_ = allocator_->CreateSession(name(), session_type());
+
+ uint32 flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP;
+
+#if !defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
+ flags |= PORTALLOCATOR_DISABLE_RELAY;
+#endif
+ allocator_session_->set_flags(flags);
+ allocator_session_->SignalPortReady.connect(
+ this, &RawTransportChannel::OnPortReady);
+ allocator_session_->SignalCandidatesReady.connect(
+ this, &RawTransportChannel::OnCandidatesReady);
+
+ // The initial ports will include stun.
+ allocator_session_->GetInitialPorts();
+}
+
+void RawTransportChannel::Reset() {
+ set_readable(false);
+ set_writable(false);
+
+ delete allocator_session_;
+
+ allocator_session_ = NULL;
+ stun_port_ = NULL;
+ relay_port_ = NULL;
+ port_ = NULL;
+ remote_address_ = talk_base::SocketAddress();
+}
+
+void RawTransportChannel::OnCandidate(const Candidate& candidate) {
+ remote_address_ = candidate.address();
+ ASSERT(!remote_address_.IsAny());
+ set_readable(true);
+
+ // We can write once we have a port and a remote address.
+ if (port_ != NULL)
+ SetWritable();
+}
+
+void RawTransportChannel::OnRemoteAddress(
+ const talk_base::SocketAddress& remote_address) {
+ remote_address_ = remote_address;
+ set_readable(true);
+
+ if (port_ != NULL)
+ SetWritable();
+}
+
+// Note about stun classification
+// Code to classify our NAT type and use the relay port if we are behind an
+// asymmetric NAT is under a FEATURE_ENABLE_STUN_CLASSIFICATION #define.
+// To turn this one we will have to enable a second stun address and make sure
+// that the relay server works for raw UDP.
+//
+// Another option is to classify the NAT type early and not offer the raw
+// transport type at all if we can't support it.
+
+void RawTransportChannel::OnPortReady(
+ PortAllocatorSession* session, Port* port) {
+ ASSERT(session == allocator_session_);
+
+ if (port->type() == STUN_PORT_TYPE) {
+ stun_port_ = static_cast<StunPort*>(port);
+
+#if defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
+ // We need a secondary address to determine the NAT type.
+ stun_port_->PrepareSecondaryAddress();
+#endif
+ } else if (port->type() == RELAY_PORT_TYPE) {
+ relay_port_ = static_cast<RelayPort*>(port);
+ } else {
+ ASSERT(false);
+ }
+}
+
+void RawTransportChannel::OnCandidatesReady(
+ PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
+ ASSERT(session == allocator_session_);
+ ASSERT(candidates.size() >= 1);
+
+ // The most recent candidate is the one we haven't seen yet.
+ Candidate c = candidates[candidates.size() - 1];
+
+ if (c.type() == STUN_PORT_TYPE) {
+ ASSERT(stun_port_ != NULL);
+
+#if defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
+ // We need to wait until we have two addresses.
+ if (stun_port_->candidates().size() < 2)
+ return;
+
+ // This is the second address. If these addresses are the same, then we
+ // are not behind a symmetric NAT. Hence, a stun port should be sufficient.
+ if (stun_port_->candidates()[0].address() ==
+ stun_port_->candidates()[1].address()) {
+ SetPort(stun_port_);
+ return;
+ }
+
+ // We will need to use relay.
+ use_relay_ = true;
+
+ // If we weren't given a relay port, we'll need to request it.
+ if (relay_port_ == NULL) {
+ allocator_session_->StartGetAllPorts();
+ return;
+ }
+
+ // If we already have a relay address, we're good. Otherwise, we will need
+ // to wait until one arrives.
+ if (relay_port_->candidates().size() > 0)
+ SetPort(relay_port_);
+#else // defined(FEATURE_ENABLE_STUN_CLASSIFICATION)
+ // Always use the stun port. We don't classify right now so just assume it
+ // will work fine.
+ SetPort(stun_port_);
+#endif
+ } else if (c.type() == RELAY_PORT_TYPE) {
+ if (use_relay_)
+ SetPort(relay_port_);
+ } else {
+ ASSERT(false);
+ }
+}
+
+void RawTransportChannel::SetPort(Port* port) {
+ ASSERT(port_ == NULL);
+ port_ = port;
+
+ // We don't need any ports other than the one we picked.
+ allocator_session_->StopGetAllPorts();
+ worker_thread_->Post(
+ this, MSG_DESTROY_UNUSED_PORTS, NULL);
+
+ // Send a message to the other client containing our address.
+
+ ASSERT(port_->candidates().size() >= 1);
+ ASSERT(port_->candidates()[0].protocol() == "udp");
+ SignalCandidateReady(this, port_->candidates()[0]);
+
+ // Read all packets from this port.
+ port_->EnablePortPackets();
+ port_->SignalReadPacket.connect(this, &RawTransportChannel::OnReadPacket);
+
+ // We can write once we have a port and a remote address.
+ if (!remote_address_.IsAny())
+ SetWritable();
+}
+
+void RawTransportChannel::SetWritable() {
+ ASSERT(port_ != NULL);
+ ASSERT(!remote_address_.IsAny());
+
+ set_writable(true);
+
+ SignalRouteChange(this, remote_address_);
+}
+
+void RawTransportChannel::OnReadPacket(
+ Port* port, const char* data, size_t size,
+ const talk_base::SocketAddress& addr) {
+ ASSERT(port_ == port);
+ SignalReadPacket(this, data, size);
+}
+
+void RawTransportChannel::OnMessage(talk_base::Message* msg) {
+ ASSERT(msg->message_id == MSG_DESTROY_UNUSED_PORTS);
+ ASSERT(port_ != NULL);
+ if (port_ != stun_port_) {
+ stun_port_->Destroy();
+ stun_port_ = NULL;
+ }
+ if (port_ != relay_port_ && relay_port_ != NULL) {
+ relay_port_->Destroy();
+ relay_port_ = NULL;
+ }
+}
+
+} // namespace cricket
+#endif // defined(FEATURE_ENABLE_PSTN)
diff --git a/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h b/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h
new file mode 100644
index 0000000..4f7e483
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h
@@ -0,0 +1,131 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_RAWTRANSPORTCHANNEL_H_
+#define TALK_P2P_BASE_RAWTRANSPORTCHANNEL_H_
+
+#include <string>
+#include <vector>
+#include "talk/base/messagequeue.h"
+#include "talk/p2p/base/transportchannelimpl.h"
+#include "talk/p2p/base/rawtransport.h"
+#include "talk/p2p/base/candidate.h"
+
+#if defined(FEATURE_ENABLE_PSTN)
+
+namespace talk_base {
+class Thread;
+}
+
+namespace cricket {
+
+class Port;
+class Connection;
+class StunPort;
+class RelayPort;
+class PortAllocator;
+class PortAllocatorSession;
+
+// Implements a channel that just sends bare packets once we have received the
+// address of the other side. We pick a single address to send them based on
+// a simple investigation of NAT type.
+class RawTransportChannel : public TransportChannelImpl,
+ public talk_base::MessageHandler {
+ public:
+ RawTransportChannel(const std::string &name,
+ const std::string &session_type,
+ RawTransport* transport,
+ talk_base::Thread *worker_thread,
+ PortAllocator *allocator);
+ virtual ~RawTransportChannel();
+
+ // Implementation of normal channel packet sending.
+ virtual int SendPacket(const char *data, size_t len);
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ // Returns the raw transport that created this channel.
+ virtual Transport* GetTransport() { return raw_transport_; }
+
+ // Creates an allocator session to start figuring out which type of
+ // port we should send to the other client. This will send
+ // SignalAvailableCandidate once we have decided.
+ virtual void Connect();
+
+ // Resets state back to unconnected.
+ virtual void Reset();
+
+ // We don't actually worry about signaling since we can't send new candidates.
+ virtual void OnSignalingReady() {}
+
+ // Handles a message setting the remote address. We are writable once we
+ // have this since we now know where to send.
+ virtual void OnCandidate(const Candidate& candidate);
+
+ void OnRemoteAddress(const talk_base::SocketAddress& remote_address);
+
+
+ private:
+ RawTransport* raw_transport_;
+ talk_base::Thread *worker_thread_;
+ PortAllocator* allocator_;
+ PortAllocatorSession* allocator_session_;
+ StunPort* stun_port_;
+ RelayPort* relay_port_;
+ Port* port_;
+ bool use_relay_;
+ talk_base::SocketAddress remote_address_;
+
+ // Called when the allocator creates another port.
+ void OnPortReady(PortAllocatorSession* session, Port* port);
+
+ // Called when one of the ports we are using has determined its address.
+ void OnCandidatesReady(PortAllocatorSession *session,
+ const std::vector<Candidate>& candidates);
+
+ // Called once we have chosen the port to use for communication with the
+ // other client. This will send its address and prepare the port for use.
+ void SetPort(Port* port);
+
+ // Called once we have a port and a remote address. This will set mark the
+ // channel as writable and signal the route to the client.
+ void SetWritable();
+
+ // Called when we receive a packet from the other client.
+ void OnReadPacket(Port* port, const char* data, size_t size,
+ const talk_base::SocketAddress& addr);
+
+ // Handles a message to destroy unused ports.
+ virtual void OnMessage(talk_base::Message *msg);
+
+ DISALLOW_EVIL_CONSTRUCTORS(RawTransportChannel);
+};
+
+} // namespace cricket
+
+#endif // defined(FEATURE_ENABLE_PSTN)
+#endif // TALK_P2P_BASE_RAWTRANSPORTCHANNEL_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/relayport.cc b/third_party/libjingle/source/talk/p2p/base/relayport.cc
new file mode 100644
index 0000000..c59967e
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/relayport.cc
@@ -0,0 +1,799 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/relayport.h"
+
+namespace cricket {
+
+static const uint32 kMessageConnectTimeout = 1;
+static const int kKeepAliveDelay = 10 * 60 * 1000;
+static const int kRetryTimeout = 50 * 1000; // ICE says 50 secs
+// How long to wait for a socket to connect to remote host in milliseconds
+// before trying another connection.
+static const int kSoftConnectTimeoutMs = 3 * 1000;
+
+// Handles a connection to one address/port/protocol combination for a
+// particular RelayEntry.
+class RelayConnection : public sigslot::has_slots<> {
+ public:
+ RelayConnection(const ProtocolAddress* protocol_address,
+ talk_base::AsyncPacketSocket* socket,
+ talk_base::Thread* thread);
+ ~RelayConnection();
+ talk_base::AsyncPacketSocket* socket() const { return socket_; }
+
+ const ProtocolAddress* protocol_address() {
+ return protocol_address_;
+ }
+
+ talk_base::SocketAddress GetAddress() const {
+ return protocol_address_->address;
+ }
+
+ ProtocolType GetProtocol() const {
+ return protocol_address_->proto;
+ }
+
+ int SetSocketOption(talk_base::Socket::Option opt, int value);
+
+ // Validates a response to a STUN allocate request.
+ bool CheckResponse(StunMessage* msg);
+
+ // Sends data to the relay server.
+ int Send(const void* pv, size_t cb);
+
+ // Sends a STUN allocate request message to the relay server.
+ void SendAllocateRequest(RelayEntry* entry, int delay);
+
+ // Return the latest error generated by the socket.
+ int GetError() { return socket_->GetError(); }
+
+ // 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, StunRequest* req);
+
+ private:
+ talk_base::AsyncPacketSocket* socket_;
+ const ProtocolAddress* protocol_address_;
+ StunRequestManager *request_manager_;
+};
+
+// Manages a number of connections to the relayserver, one for each
+// available protocol. 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 talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+ public:
+ RelayEntry(RelayPort* port, const talk_base::SocketAddress& ext_addr,
+ const talk_base::SocketAddress& local_addr);
+ ~RelayEntry();
+
+ RelayPort* port() { return port_; }
+
+ const talk_base::SocketAddress& address() const { return ext_addr_; }
+ void set_address(const talk_base::SocketAddress& addr) { ext_addr_ = addr; }
+
+ bool connected() const { return connected_; }
+ bool locked() const { return locked_; }
+
+ // Returns the last error on the socket of this entry.
+ int GetError();
+
+ // Returns the most preferred connection of the given
+ // ones. Connections are rated based on protocol in the order of:
+ // UDP, TCP and SSLTCP, where UDP is the most preferred protocol
+ static RelayConnection* GetBestConnection(RelayConnection* conn1,
+ RelayConnection* conn2);
+
+ // 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 talk_base::SocketAddress& mapped_addr,
+ RelayConnection* socket);
+
+ // 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 talk_base::SocketAddress& addr);
+
+ // Schedules a keep-alive allocate request.
+ void ScheduleKeepAlive();
+
+ void SetServerIndex(size_t sindex) { server_index_ = sindex; }
+
+ // Sets this option on the socket of each connection.
+ int SetSocketOption(talk_base::Socket::Option opt, int value);
+
+ size_t ServerIndex() const { return server_index_; }
+
+ // Try a different server address
+ void HandleConnectFailure(talk_base::AsyncPacketSocket* socket);
+
+ // Implementation of the MessageHandler Interface.
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+ private:
+ RelayPort* port_;
+ talk_base::SocketAddress ext_addr_, local_addr_;
+ size_t server_index_;
+ bool connected_;
+ bool locked_;
+ RelayConnection* current_connection_;
+
+ // Called when a TCP connection is established or fails
+ void OnSocketConnect(talk_base::AsyncTCPSocket* socket);
+ void OnSocketClose(talk_base::AsyncTCPSocket* socket, int error);
+
+ // Called when a packet is received on this socket.
+ void OnReadPacket(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ // 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, RelayConnection* connection);
+ 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_;
+ RelayConnection* connection_;
+ uint32 start_time_;
+};
+
+const std::string RELAY_PORT_TYPE("relay");
+
+RelayPort::RelayPort(
+ talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network, const talk_base::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, talk_base::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 (size_t 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 == talk_base::PROXY_HTTPS ||
+ proxy().type == talk_base::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;
+ }
+ }
+ AddAddress(addr.address, proto_name, false);
+}
+
+void RelayPort::SetReady() {
+ if (!ready_) {
+ ready_ = true;
+ SignalAddressReady(this);
+ }
+}
+
+const ProtocolAddress * RelayPort::ServerAddress(size_t index) const {
+ if (index < server_addr_.size())
+ return &server_addr_[index];
+ return NULL;
+}
+
+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 conns 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 talk_base::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 (size_t 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()) {
+ 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;
+}
+
+int RelayPort::SetOption(talk_base::Socket::Option opt, int value) {
+ int result = 0;
+ for (size_t i = 0; i < entries_.size(); ++i) {
+ if (entries_[i]->SetSocketOption(opt, value) < 0) {
+ result = -1;
+ error_ = entries_[i]->GetError();
+ }
+ }
+ options_.push_back(OptionValue(opt, value));
+ return result;
+}
+
+int RelayPort::GetError() {
+ return error_;
+}
+
+void RelayPort::OnReadPacket(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr) {
+ if (Connection* conn = GetConnection(remote_addr)) {
+ conn->OnReadPacket(data, size);
+ } else {
+ Port::OnReadPacket(data, size, remote_addr);
+ }
+}
+
+void RelayPort::DisposeSocket(talk_base::AsyncPacketSocket * socket) {
+ // TODO(oja): Socket should be deleted by the RelayConnection destructor.
+ thread_->Dispose(socket);
+}
+
+void RelayPort::DisposeConnection(RelayConnection* connection) {
+ thread_->Dispose(connection);
+ DisposeSocket(connection->socket());
+}
+
+RelayConnection::RelayConnection(const ProtocolAddress* protocol_address,
+ talk_base::AsyncPacketSocket* socket,
+ talk_base::Thread* thread)
+ : socket_(socket),
+ protocol_address_(protocol_address) {
+ request_manager_ = new StunRequestManager(thread);
+ request_manager_->SignalSendPacket.connect(this,
+ &RelayConnection::OnSendPacket);
+}
+
+RelayConnection::~RelayConnection() {
+ delete request_manager_;
+}
+
+int RelayConnection::SetSocketOption(talk_base::Socket::Option opt,
+ int value) {
+ if (socket_) {
+ return socket_->SetOption(opt, value);
+ }
+ return 0;
+}
+
+bool RelayConnection::CheckResponse(StunMessage* msg) {
+ return request_manager_->CheckResponse(msg);
+}
+
+void RelayConnection::OnSendPacket(const void* data, size_t size,
+ StunRequest* req) {
+ int sent = socket_->SendTo(data, size, GetAddress());
+ if (sent <= 0) {
+ LOG(LS_VERBOSE) << "OnSendPacket: failed sending to " << GetAddress() <<
+ std::strerror(socket_->GetError());
+ ASSERT(sent < 0);
+ }
+}
+
+int RelayConnection::Send(const void* pv, size_t cb) {
+ return socket_->SendTo(pv, cb, GetAddress());
+}
+
+void RelayConnection::SendAllocateRequest(RelayEntry* entry, int delay) {
+ request_manager_->SendDelayed(new AllocateRequest(entry, this), delay);
+}
+
+RelayEntry::RelayEntry(RelayPort* port,
+ const talk_base::SocketAddress& ext_addr,
+ const talk_base::SocketAddress& local_addr)
+ : port_(port), ext_addr_(ext_addr), local_addr_(local_addr),
+ server_index_(0), connected_(false), locked_(false),
+ current_connection_(NULL) {
+}
+
+RelayEntry::~RelayEntry() {
+ // Remove all RelayConnections and dispose sockets.
+ delete current_connection_;
+ current_connection_ = NULL;
+}
+
+void RelayEntry::Connect() {
+ // If we're already connected, return.
+ if (connected_)
+ return;
+
+ // If we've exhausted all options, bail out.
+ const ProtocolAddress* ra = port()->ServerAddress(server_index_);
+ if (!ra) {
+ LOG(LS_WARNING) << "No more relay addresses left to try";
+ return;
+ }
+
+ // Remove any previous connection.
+ if (current_connection_) {
+ port()->DisposeConnection(current_connection_);
+ current_connection_ = NULL;
+ }
+
+ // Try to set up our new socket.
+ LOG(LS_INFO) << "Connecting to relay via " << ProtoToString(ra->proto) <<
+ " @ " << ra->address.ToString();
+
+ talk_base::AsyncPacketSocket* socket = port_->CreatePacketSocket(ra->proto);
+ if (!socket) {
+ LOG(LS_WARNING) << "Socket creation failed";
+ } else if (socket->Bind(local_addr_) < 0) {
+ LOG(LS_WARNING) << "Socket bind failed with error " << socket->GetError();
+ delete socket;
+ socket = NULL;
+ }
+
+ // If we failed to get a socket, move on to the next protocol.
+ if (!socket) {
+ port()->thread()->Post(this, kMessageConnectTimeout);
+ return;
+ }
+
+ // Otherwise, create the new connection and configure any socket options.
+ socket->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket);
+ current_connection_ = new RelayConnection(ra, socket, port()->thread());
+ for (size_t i = 0; i < port_->options().size(); ++i) {
+ current_connection_->SetSocketOption(port_->options()[i].first,
+ port_->options()[i].second);
+ }
+
+ // If we're trying UDP, start binding requests.
+ // If we're trying TCP, initiate a connection with a fixed timeout.
+ if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) {
+ talk_base::AsyncTCPSocket* tcp =
+ static_cast<talk_base::AsyncTCPSocket*>(socket);
+ tcp->SignalClose.connect(this, &RelayEntry::OnSocketClose);
+ tcp->SignalConnect.connect(this, &RelayEntry::OnSocketConnect);
+ tcp->Connect(ra->address);
+ port()->thread()->PostDelayed(kSoftConnectTimeoutMs, this,
+ kMessageConnectTimeout);
+ } else {
+ current_connection_->SendAllocateRequest(this, 0);
+ }
+}
+
+int RelayEntry::GetError() {
+ if (current_connection_ != NULL) {
+ return current_connection_->GetError();
+ }
+ return 0;
+}
+
+RelayConnection* RelayEntry::GetBestConnection(RelayConnection* conn1,
+ RelayConnection* conn2) {
+ return conn1->GetProtocol() <= conn2->GetProtocol() ? conn1 : conn2;
+}
+
+void RelayEntry::OnConnect(const talk_base::SocketAddress& mapped_addr,
+ RelayConnection* connection) {
+ // We are connected, notify our parent.
+ 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 talk_base::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(talk_base::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.
+
+ talk_base::ByteBuffer buf;
+ request.Write(&buf);
+
+ return SendPacket(buf.Data(), buf.Length());
+}
+
+void RelayEntry::ScheduleKeepAlive() {
+ if (current_connection_) {
+ current_connection_->SendAllocateRequest(this, kKeepAliveDelay);
+ }
+}
+
+int RelayEntry::SetSocketOption(talk_base::Socket::Option opt, int value) {
+ // Set the option on all available sockets.
+ int socket_error = 0;
+ if (current_connection_) {
+ socket_error = current_connection_->SetSocketOption(opt, value);
+ }
+ return socket_error;
+}
+
+void RelayEntry::HandleConnectFailure(
+ talk_base::AsyncPacketSocket* socket) {
+ // Make sure it's the current connection that has failed, it might
+ // be an old socked that has not yet been disposed.
+ if (!socket || socket == current_connection_->socket()) {
+ if (current_connection_)
+ port()->SignalConnectFailure(current_connection_->protocol_address());
+
+ // Try to connect to the next server address.
+ server_index_ += 1;
+ Connect();
+ }
+}
+
+void RelayEntry::OnMessage(talk_base::Message *pmsg) {
+ ASSERT(pmsg->message_id == kMessageConnectTimeout);
+ if (current_connection_) {
+ const ProtocolAddress* ra = current_connection_->protocol_address();
+ LOG(LS_WARNING) << "Relay " << ra->proto << " connection to " <<
+ ra->address << " timed out";
+
+ // Currently we connect to each server address in sequence. If we
+ // have more addresses to try, treat this is an error and move on to
+ // the next address, otherwise give this connection more time and
+ // await the real timeout.
+ //
+ // TODO(oja): Connect to servers in pararel to speed up connect time
+ // and to avoid giving up to early.
+ port_->SignalSoftTimeout(ra);
+ HandleConnectFailure(current_connection_->socket());
+ } else {
+ HandleConnectFailure(NULL);
+ }
+}
+
+void RelayEntry::OnSocketConnect(talk_base::AsyncTCPSocket* socket) {
+ LOG(INFO) << "relay tcp connected to " <<
+ socket->GetRemoteAddress().ToString();
+ if (current_connection_ != NULL) {
+ current_connection_->SendAllocateRequest(this, 0);
+ }
+}
+
+void RelayEntry::OnSocketClose(talk_base::AsyncTCPSocket* socket, int error) {
+ PLOG(LERROR, error) << "Relay connection failed: socket closed";
+ HandleConnectFailure(socket);
+}
+
+void RelayEntry::OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ // ASSERT(remote_addr == port_->server_addr());
+ // TODO: are we worried about this?
+
+ if (current_connection_ == NULL || socket != current_connection_->socket()) {
+ // This packet comes from an unknown address.
+ LOG(WARNING) << "Dropping packet: unknown address";
+ return;
+ }
+
+ // 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;
+ }
+
+ talk_base::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 (current_connection_->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;
+ }
+
+ talk_base::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);
+}
+
+int RelayEntry::SendPacket(const void* data, size_t size) {
+ int sent = 0;
+ if (current_connection_) {
+ // We are connected, no need to send packets anywere else than to
+ // the current connection.
+ sent = current_connection_->Send(data, size);
+ }
+ return sent;
+}
+
+AllocateRequest::AllocateRequest(RelayEntry* entry,
+ RelayConnection* connection) :
+ entry_(entry), connection_(connection) {
+ start_time_ = talk_base::Time();
+}
+
+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 * talk_base::_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 {
+ talk_base::SocketAddress addr(addr_attr->ip(), addr_attr->port());
+ entry_->OnConnect(addr, connection_);
+ }
+
+ // 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 (talk_base::TimeSince(start_time_) <= kRetryTimeout)
+ entry_->ScheduleKeepAlive();
+}
+
+void AllocateRequest::OnTimeout() {
+ LOG(INFO) << "Allocate request timed out";
+ entry_->HandleConnectFailure(connection_->socket());
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/relayport.h b/third_party/libjingle/source/talk/p2p/base/relayport.h
new file mode 100644
index 0000000..09d1cf5
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/relayport.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 TALK_P2P_BASE_RELAYPORT_H_
+#define TALK_P2P_BASE_RELAYPORT_H_
+
+#include <string>
+#include <vector>
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/stunrequest.h"
+
+namespace cricket {
+
+extern const std::string RELAY_PORT_TYPE;
+class RelayEntry;
+class RelayConnection;
+
+// Communicates using an allocated port on the relay server. For each
+// remote candidate that we try to send data to a RelayEntry instance
+// is created. The RelayEntry will try to reach the remote destination
+// by connecting to all available server addresses in a pre defined
+// order with a small delay in between. When a connection is
+// successful all other connection attemts are aborted.
+class RelayPort : public Port {
+ public:
+ typedef std::pair<talk_base::Socket::Option, int> OptionValue;
+
+ // RelayPort doesn't yet do anything fancy in the ctor.
+ static RelayPort* Create(
+ talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network, const talk_base::SocketAddress& local_addr,
+ const std::string& username, const std::string& password,
+ const std::string& magic_cookie) {
+ return new RelayPort(thread, factory, network, local_addr,
+ username, password, magic_cookie);
+ }
+ RelayPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network*, const talk_base::SocketAddress& local_addr,
+ const std::string& username, const std::string& password,
+ const std::string& magic_cookie);
+ bool Init();
+ virtual ~RelayPort();
+
+ void AddServerAddress(const ProtocolAddress& addr);
+ void AddExternalAddress(const ProtocolAddress& addr);
+
+ 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(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ const ProtocolAddress * ServerAddress(size_t index) const;
+ bool IsReady() { return ready_; }
+
+ // TODO(oja): Move these two methods to RelayEntry.
+ void DisposeSocket(talk_base::AsyncPacketSocket * socket);
+ void DisposeConnection(RelayConnection* connection);
+
+ // Used for testing.
+ sigslot::signal1<const ProtocolAddress*> SignalConnectFailure;
+ sigslot::signal1<const ProtocolAddress*> SignalSoftTimeout;
+
+ protected:
+ void SetReady();
+
+ virtual int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload);
+
+ // Dispatches the given packet to the port or connection as appropriate.
+ void OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr);
+
+ private:
+ friend class RelayEntry;
+
+ talk_base::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 // TALK_P2P_BASE_RELAYPORT_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/session.cc b/third_party/libjingle/source/talk/p2p/base/session.cc
new file mode 100644
index 0000000..e669336
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/session.cc
@@ -0,0 +1,672 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/session.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/helpers.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/jid.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/transport.h"
+#include "talk/p2p/base/transportchannelproxy.h"
+#include "talk/p2p/base/p2ptransport.h"
+#include "talk/p2p/base/p2ptransportchannel.h"
+
+#include "talk/p2p/base/constants.h"
+
+namespace {
+
+const uint32 MSG_TIMEOUT = 1;
+const uint32 MSG_ERROR = 2;
+const uint32 MSG_STATE = 3;
+
+} // namespace
+
+namespace cricket {
+
+bool BadMessage(const buzz::QName type,
+ const std::string& text,
+ SessionError* err) {
+ err->SetType(type);
+ err->SetText(text);
+ return false;
+}
+
+BaseSession::BaseSession(talk_base::Thread *signaling_thread)
+ : state_(STATE_INIT), error_(ERROR_NONE),
+ description_(NULL), remote_description_(NULL),
+ signaling_thread_(signaling_thread) {
+}
+
+BaseSession::~BaseSession() {
+ delete remote_description_;
+ delete description_;
+}
+
+void BaseSession::SetState(State state) {
+ ASSERT(signaling_thread_->IsCurrent());
+ if (state != state_) {
+ state_ = state;
+ SignalState(this, state_);
+ signaling_thread_->Post(this, MSG_STATE);
+ }
+}
+
+void BaseSession::SetError(Error error) {
+ ASSERT(signaling_thread_->IsCurrent());
+ if (error != error_) {
+ error_ = error;
+ SignalError(this, error);
+ if (error_ != ERROR_NONE)
+ signaling_thread_->Post(this, MSG_ERROR);
+ }
+}
+
+void BaseSession::OnMessage(talk_base::Message *pmsg) {
+ switch (pmsg->message_id) {
+ case MSG_TIMEOUT:
+ // Session timeout has occured.
+ SetError(ERROR_TIME);
+ break;
+
+ case MSG_ERROR:
+ // Any of the defined errors is most likely fatal.
+ Terminate();
+ break;
+
+ case MSG_STATE:
+ switch (state_) {
+ case STATE_SENTACCEPT:
+ case STATE_RECEIVEDACCEPT:
+ SetState(STATE_INPROGRESS);
+ break;
+
+ case STATE_SENTREJECT:
+ case STATE_RECEIVEDREJECT:
+ Terminate();
+ break;
+
+ default:
+ // Explicitly ignoring some states here.
+ break;
+ }
+ break;
+ }
+}
+
+
+Session::Session(SessionManager *session_manager, const std::string& name,
+ const SessionID& id, const std::string& session_type,
+ SessionClient* client) :
+ BaseSession(session_manager->signaling_thread()) {
+ ASSERT(session_manager->signaling_thread()->IsCurrent());
+ ASSERT(client != NULL);
+ session_manager_ = session_manager;
+ name_ = name;
+ id_ = id;
+ session_type_ = session_type;
+ client_ = client;
+ error_ = ERROR_NONE;
+ state_ = STATE_INIT;
+ initiator_ = false;
+ SetTransport(new P2PTransport(session_manager_->worker_thread(),
+ session_manager_->port_allocator()));
+ transport_negotiated_ = false;
+ current_protocol_ = PROTOCOL_GINGLE2;
+}
+
+Session::~Session() {
+ ASSERT(signaling_thread_->IsCurrent());
+
+ ASSERT(state_ != STATE_DEINIT);
+ state_ = STATE_DEINIT;
+ SignalState(this, state_);
+
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ iter->second->SignalDestroyed(iter->second);
+ delete iter->second;
+ }
+
+ delete transport_;
+}
+
+bool Session::Initiate(const std::string &to,
+ const SessionDescription *description) {
+ ASSERT(signaling_thread_->IsCurrent());
+
+ // Only from STATE_INIT
+ if (state_ != STATE_INIT)
+ return false;
+
+ // Setup for signaling.
+ remote_name_ = to;
+ initiator_ = true;
+ set_local_description(description);
+
+ SendInitiateMessage(description);
+ SetState(Session::STATE_SENTINITIATE);
+
+ // We speculatively start attempting connection of the P2P transports.
+ ConnectDefaultTransportChannels(transport_);
+ return true;
+}
+
+bool Session::Accept(const SessionDescription *description) {
+ ASSERT(signaling_thread_->IsCurrent());
+
+ // Only if just received initiate
+ if (state_ != STATE_RECEIVEDINITIATE)
+ return false;
+
+ // Setup for signaling.
+ initiator_ = false;
+ set_local_description(description);
+
+ // Wait for ChooseTransport to complete
+ if (!transport_negotiated_)
+ return true;
+
+ SendAcceptMessage();
+ SetState(Session::STATE_SENTACCEPT);
+ return true;
+}
+
+bool Session::Reject() {
+ ASSERT(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;
+
+ // Setup for signaling.
+ initiator_ = false;
+
+ SendRejectMessage();
+ SetState(STATE_SENTREJECT);
+
+ return true;
+}
+
+bool Session::Terminate() {
+ ASSERT(signaling_thread_->IsCurrent());
+
+ // Either side can terminate, at any time.
+ switch (state_) {
+ case STATE_SENTTERMINATE:
+ case STATE_RECEIVEDTERMINATE:
+ return false;
+
+ case STATE_SENTREJECT:
+ case STATE_RECEIVEDREJECT:
+ // We don't need to send terminate if we sent or received a reject...
+ // it's implicit.
+ break;
+
+ default:
+ SendTerminateMessage();
+ break;
+ }
+
+ SetState(STATE_SENTTERMINATE);
+ return true;
+}
+
+// only used by app/win32/fileshare.cc
+void Session::SendInfoMessage(const XmlElements& elems) {
+ ASSERT(signaling_thread_->IsCurrent());
+ SendMessage(ACTION_SESSION_INFO, elems);
+}
+
+void Session::SetTransport(Transport* transport) {
+ transport_ = transport;
+ transport->SignalConnecting.connect(
+ this, &Session::OnTransportConnecting);
+ transport->SignalWritableState.connect(
+ this, &Session::OnTransportWritable);
+ transport->SignalRequestSignaling.connect(
+ this, &Session::OnTransportRequestSignaling);
+ transport->SignalCandidatesReady.connect(
+ this, &Session::OnTransportCandidatesReady);
+ transport->SignalTransportError.connect(
+ this, &Session::OnTransportSendError);
+ transport->SignalChannelGone.connect(
+ this, &Session::OnTransportChannelGone);
+}
+
+void Session::ConnectDefaultTransportChannels(Transport* transport) {
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ ASSERT(!transport->HasChannel(iter->first));
+ transport->CreateChannel(iter->first, session_type());
+ }
+ transport->ConnectChannels();
+}
+
+void Session::ConnectTransportChannels(Transport* transport) {
+ ASSERT(signaling_thread_->IsCurrent());
+
+ // Create implementations for all of the channels if they don't exist.
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ TransportChannelProxy* channel = iter->second;
+ TransportChannelImpl* impl = transport->GetChannel(channel->name());
+ if (impl == NULL)
+ impl = transport->CreateChannel(channel->name(), session_type());
+ ASSERT(impl != NULL);
+ channel->SetImplementation(impl);
+ }
+
+ // Have this transport start connecting if it is not already.
+ // (We speculatively connect the most common transport right away.)
+ transport->ConnectChannels();
+}
+
+TransportParserMap Session::GetTransportParsers() {
+ TransportParserMap parsers;
+ parsers[transport_->name()] = transport_;
+ return parsers;
+}
+
+FormatParserMap Session::GetFormatParsers() {
+ FormatParserMap parsers;
+ parsers[session_type_] = client_;
+ return parsers;
+}
+
+TransportChannel* Session::CreateChannel(const std::string& name) {
+ ASSERT(channels_.find(name) == channels_.end());
+ ASSERT(!transport_->HasChannel(name));
+
+ TransportChannelProxy* channel =
+ new TransportChannelProxy(name, session_type_);
+ channels_[name] = channel;
+ if (transport_negotiated_) {
+ channel->SetImplementation(transport_->CreateChannel(name, session_type_));
+ } else if (state_ == STATE_SENTINITIATE) {
+ transport_->CreateChannel(name, session_type());
+ }
+ return channel;
+}
+
+TransportChannel* Session::GetChannel(const std::string& name) {
+ ChannelMap::iterator iter = channels_.find(name);
+ return (iter != channels_.end()) ? iter->second : NULL;
+}
+
+void Session::DestroyChannel(TransportChannel* channel) {
+ ChannelMap::iterator iter = channels_.find(channel->name());
+ ASSERT(iter != channels_.end());
+ ASSERT(channel == iter->second);
+ channels_.erase(iter);
+ channel->SignalDestroyed(channel);
+ delete channel;
+}
+
+void Session::OnSignalingReady() {
+ ASSERT(signaling_thread_->IsCurrent());
+ transport_->OnSignalingReady();
+}
+
+void Session::OnTransportConnecting(Transport* transport) {
+ // This is an indication that we should begin watching the writability
+ // state of the transport.
+ OnTransportWritable(transport);
+}
+
+void Session::OnTransportWritable(Transport* transport) {
+ ASSERT(signaling_thread_->IsCurrent());
+ ASSERT(transport == transport_);
+
+ // If the transport is not writable, start a timer to make sure that it
+ // becomes writable within a reasonable amount of time. If it does not, we
+ // terminate since we can't actually send data. If the transport is writable,
+ // cancel the timer. Note that writability transitions may occur repeatedly
+ // during the lifetime of the session.
+
+ signaling_thread_->Clear(this, MSG_TIMEOUT);
+ if (transport->HasChannels() && !transport->writable()) {
+ signaling_thread_->PostDelayed(
+ session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
+ }
+}
+
+void Session::OnTransportRequestSignaling(Transport* transport) {
+ ASSERT(signaling_thread_->IsCurrent());
+ SignalRequestSignaling(this);
+}
+
+void Session::OnTransportCandidatesReady(Transport* transport,
+ const Candidates& candidates) {
+ ASSERT(signaling_thread_->IsCurrent());
+ if (!transport_negotiated_) {
+ for (Candidates::const_iterator iter = candidates.begin();
+ iter != candidates.end();
+ ++iter) {
+ sent_candidates_.push_back(*iter);
+ }
+ }
+ SendTransportInfoMessage(candidates);
+}
+
+void Session::OnTransportSendError(Transport* transport,
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ ASSERT(signaling_thread_->IsCurrent());
+ SignalErrorMessage(this, stanza, name, type, text, extra_info);
+}
+
+void Session::OnTransportChannelGone(Transport* transport,
+ const std::string& name) {
+ ASSERT(signaling_thread_->IsCurrent());
+ SignalChannelGone(this, name);
+}
+
+void Session::OnIncomingMessage(const SessionMessage& msg) {
+ ASSERT(signaling_thread_->IsCurrent());
+ ASSERT(state_ == STATE_INIT || msg.from == remote_name_);
+
+ // PROTOCOL_GINGLE is effectively the old compatibility_mode_ which
+ // meant "talking to old client". We can flip to
+ // compatibility_mode_, but not back.
+ if (msg.protocol == PROTOCOL_GINGLE) {
+ current_protocol_ = PROTOCOL_GINGLE;
+ }
+
+ if (msg.type == ACTION_TRANSPORT_INFO &&
+ current_protocol_ == PROTOCOL_GINGLE &&
+ !transport_negotiated_) {
+ // We have sent some already (using transport-info), and we need
+ // to re-send them using the candidates message.
+ if (sent_candidates_.size() > 0) {
+ SendTransportInfoMessage(sent_candidates_);
+ }
+ sent_candidates_.clear();
+ }
+
+ bool valid = false;
+ SessionError error;
+ switch (msg.type) {
+ case ACTION_SESSION_INITIATE:
+ valid = OnInitiateMessage(msg, &error);
+ break;
+ case ACTION_SESSION_INFO:
+ valid = OnInfoMessage(msg);
+ break;
+ case ACTION_SESSION_ACCEPT:
+ valid = OnAcceptMessage(msg, &error);
+ break;
+ case ACTION_SESSION_REJECT:
+ valid = OnRejectMessage(msg, &error);
+ break;
+ case ACTION_SESSION_TERMINATE:
+ valid = OnTerminateMessage(msg, &error);
+ break;
+ case ACTION_TRANSPORT_INFO:
+ valid = OnTransportInfoMessage(msg, &error);
+ break;
+ default:
+ valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST,
+ "unknown session message type",
+ &error);
+ }
+
+ if (valid) {
+ SendAcknowledgementMessage(msg.stanza);
+ } else {
+ SignalErrorMessage(this, msg.stanza, error.type,
+ "modify", error.text, NULL);
+ }
+}
+
+void Session::OnFailedSend(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* error_stanza) {
+ ASSERT(signaling_thread_->IsCurrent());
+
+ SessionMessage msg;
+ ParseError parse_error;
+ if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) {
+ LOG(LERROR) << "Error parsing failed send: " << parse_error.text
+ << ":" << orig_stanza;
+ return;
+ }
+
+ std::string error_type = "cancel";
+
+ const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR);
+ ASSERT(error != NULL);
+ if (error) {
+ ASSERT(error->HasAttr(buzz::QN_TYPE));
+ error_type = error->Attr(buzz::QN_TYPE);
+
+ LOG(LERROR) << "Session error:\n" << error->Str() << "\n"
+ << "in response to:\n" << orig_stanza->Str();
+ }
+
+ if (msg.type == ACTION_TRANSPORT_INFO) {
+ // Transport messages frequently generate errors because they are sent right
+ // when we detect a network failure. For that reason, we ignore such
+ // errors, because if we do not establish writability again, we will
+ // terminate anyway. The exceptions are transport-specific error tags,
+ // which we pass on to the respective transport.
+ for (const buzz::XmlElement* elem = error->FirstElement();
+ NULL != elem; elem = elem->NextElement()) {
+ if (transport_->name() == elem->Name().Namespace()) {
+ transport_->OnTransportError(elem);
+ }
+ }
+ } else if ((error_type != "continue") && (error_type != "wait")) {
+ // We do not set an error if the other side said it is okay to continue
+ // (possibly after waiting). These errors can be ignored.
+ SetError(ERROR_RESPONSE);
+ }
+}
+
+bool Session::OnInitiateMessage(const SessionMessage& msg,
+ SessionError* error) {
+ if (!CheckState(STATE_INIT, error))
+ return false;
+
+ SessionInitiate init;
+ if (!ParseSessionInitiate(msg.action_elem, GetFormatParsers(), &init, error))
+ return false;
+
+ if (transport_->name() != init.transport_name)
+ return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE,
+ "no supported transport in offer",
+ error);
+
+ initiator_ = false;
+ remote_name_ = msg.from;
+ set_remote_description(init.AdoptFormat());
+ SetState(STATE_RECEIVEDINITIATE);
+
+ // User of Session may listen to state change and call Reject().
+ if (state_ != STATE_SENTREJECT && !transport_negotiated_) {
+ transport_negotiated_ = true;
+ ConnectTransportChannels(transport_);
+
+ // If the user wants to accept, allow that now
+ if (description_) {
+ Accept(description_);
+ }
+ }
+ return true;
+}
+
+bool Session::OnAcceptMessage(const SessionMessage& msg, SessionError* error) {
+ if (!CheckState(STATE_SENTINITIATE, error))
+ return false;
+
+ SessionAccept accept;
+ if (!ParseSessionAccept(msg.action_elem, GetFormatParsers(), &accept, error))
+ return false;
+
+ set_remote_description(accept.AdoptFormat());
+ SetState(STATE_RECEIVEDACCEPT);
+ return true;
+}
+
+bool Session::OnRejectMessage(const SessionMessage& msg, SessionError* error) {
+ if (!CheckState(STATE_SENTINITIATE, error))
+ return false;
+
+ SetState(STATE_RECEIVEDREJECT);
+ return true;
+}
+
+// Only used by app/win32/fileshare.cc.
+bool Session::OnInfoMessage(const SessionMessage& msg) {
+ SignalInfoMessage(this, CopyOfXmlChildren(msg.action_elem));
+ return true;
+}
+
+bool Session::OnTerminateMessage(const SessionMessage& msg,
+ SessionError* error) {
+ SessionTerminate term;
+ if (!ParseSessionTerminate(msg.action_elem, &term, error))
+ return false;
+
+ SignalReceivedTerminateReason(this, term.reason);
+ if (term.debug_reason != buzz::STR_EMPTY) {
+ LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason;
+ }
+ return true;
+}
+
+bool Session::OnTransportInfoMessage(const SessionMessage& msg,
+ SessionError* error) {
+ TransportInfo info;
+ if (!ParseTransportInfo(msg.action_elem, GetTransportParsers(), &info, error))
+ return false;
+
+ if (transport_->name() == info.transport_name) {
+ transport_->OnRemoteCandidates(info.candidates);
+ if (!transport_negotiated_) {
+ transport_negotiated_ = true;
+ ConnectTransportChannels(transport_);
+ }
+ }
+ return true;
+}
+
+bool Session::CheckState(State state, SessionError* error) {
+ ASSERT(state_ == state);
+ if (state_ != state) {
+ return BadMessage(buzz::QN_STANZA_NOT_ALLOWED,
+ "message not allowed in current state",
+ error);
+ }
+ return true;
+}
+
+void Session::OnMessage(talk_base::Message *pmsg) {
+ // preserve this because BaseSession::OnMessage may modify it
+ BaseSession::State orig_state = state_;
+
+ BaseSession::OnMessage(pmsg);
+
+ switch (pmsg->message_id) {
+ case MSG_STATE:
+ switch (orig_state) {
+ case STATE_SENTTERMINATE:
+ case STATE_RECEIVEDTERMINATE:
+ session_manager_->DestroySession(this);
+ break;
+
+ default:
+ // Explicitly ignoring some states here.
+ break;
+ }
+ break;
+ }
+}
+
+void Session::SendInitiateMessage(const SessionDescription *description) {
+ SessionInitiate init(transport_->name(), session_type_, description);
+ XmlElements elems;
+ WriteSessionInitiate(init, GetFormatParsers(), current_protocol_, &elems);
+ SendMessage(ACTION_SESSION_INITIATE, elems);
+}
+
+void Session::SendAcceptMessage() {
+ // TODO(pthatcher): When we support the Jingle standard, we need to
+ // include at least an empty <transport> in the accept.
+ std::string transport_name = "";
+ SessionAccept accept(transport_name, session_type_, description_);
+
+ XmlElements elems;
+ WriteSessionAccept(accept, GetFormatParsers(), &elems);
+ SendMessage(ACTION_SESSION_ACCEPT, elems);
+}
+
+void Session::SendRejectMessage() {
+ XmlElements elems;
+ SendMessage(ACTION_SESSION_REJECT, elems);
+}
+
+void Session::SendTerminateMessage() {
+ XmlElements elems;
+ SendMessage(ACTION_SESSION_TERMINATE, elems);
+}
+
+void Session::SendTransportInfoMessage(const Candidates& candidates) {
+ TransportInfo info(transport_->name(), candidates);
+ XmlElements elems;
+ WriteTransportInfo(info, GetTransportParsers(), current_protocol_, &elems);
+ SendMessage(ACTION_TRANSPORT_INFO, elems);
+}
+
+void Session::SendMessage(ActionType type, const XmlElements& action_elems) {
+ SessionMessage msg(current_protocol_, type, id_.id_str(), id_.initiator());
+ msg.to = remote_name_;
+
+ talk_base::scoped_ptr<buzz::XmlElement> stanza(
+ new buzz::XmlElement(buzz::QN_IQ));
+ WriteSessionMessage(msg, action_elems, stanza.get());
+ SignalOutgoingMessage(this, stanza.get());
+}
+
+void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) {
+ talk_base::scoped_ptr<buzz::XmlElement> ack(
+ new buzz::XmlElement(buzz::QN_IQ));
+ ack->SetAttr(buzz::QN_TO, remote_name_);
+ ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
+ ack->SetAttr(buzz::QN_TYPE, "result");
+
+ SignalOutgoingMessage(this, ack.get());
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/session.h b/third_party/libjingle/source/talk/p2p/base/session.h
new file mode 100644
index 0000000..4d861cb
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/session.h
@@ -0,0 +1,402 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_SESSION_H_
+#define TALK_P2P_BASE_SESSION_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "talk/p2p/base/sessionmessages.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/base/socketaddress.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/sessionid.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/port.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+class JingleMessageHandler;
+
+namespace cricket {
+
+class P2PTransportChannel;
+class Transport;
+class TransportChannel;
+class TransportChannelProxy;
+class TransportChannelImpl;
+
+// We add "type" to the errors because it's need for
+// SignalErrorMessage.
+struct SessionError : ParseError {
+ buzz::QName type;
+
+ // if unset, assume type is a parse error
+ SessionError() : ParseError(), type(buzz::QN_STANZA_BAD_REQUEST) {}
+
+ void SetType(const buzz::QName type) {
+ this->type = type;
+ }
+};
+
+// TODO(juberti): Consider simplifying the dependency from Voice/VideoChannel
+// on Session. Right now the Channel class requires a BaseSession, but it only
+// uses CreateChannel/DestroyChannel. Perhaps something like a
+// TransportChannelFactory could be hoisted up out of BaseSession, or maybe
+// the transports could be passed in directly.
+
+// A BaseSession manages general session state. This includes negotiation
+// of both the application-level and network-level protocols: the former
+// defines what will be sent and the latter defines how it will be sent. Each
+// network-level protocol is represented by a Transport object. Each Transport
+// participates in the network-level negotiation. The individual streams of
+// packets are represented by TransportChannels. The application-level protocol
+// is represented by SessionDecription objects.
+class BaseSession : public sigslot::has_slots<>,
+ public talk_base::MessageHandler {
+ 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 connecting transport
+ STATE_RECEIVEDACCEPT, // received accept. begin connecting transport
+ 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
+ STATE_DEINIT, // session is being destroyed
+ };
+
+ enum Error {
+ ERROR_NONE = 0, // no error
+ ERROR_TIME = 1, // no response to signaling
+ ERROR_RESPONSE = 2, // error during signaling
+ ERROR_NETWORK = 3, // network error, could not allocate network resources
+ };
+
+ explicit BaseSession(talk_base::Thread *signaling_thread);
+ virtual ~BaseSession();
+
+ // Updates the state, signaling if necessary.
+ void SetState(State state);
+
+ // Updates the error state, signaling if necessary.
+ void SetError(Error error);
+
+ // Handles messages posted to us.
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+ // Returns the current state of the session. See the enum above for details.
+ // Each time the state changes, we will fire this signal.
+ State state() const { return state_; }
+ sigslot::signal2<BaseSession *, State> SignalState;
+
+ // Returns the last error in the session. See the enum above for details.
+ // Each time the an error occurs, we will fire this signal.
+ Error error() const { return error_; }
+ sigslot::signal2<BaseSession *, Error> SignalError;
+
+ // Creates a new channel with the given name. This method may be called
+ // immediately after creating the session. However, the actual
+ // implementation may not be fixed until transport negotiation completes.
+ virtual TransportChannel* CreateChannel(const std::string& name) = 0;
+
+ // Returns the channel with the given name.
+ virtual TransportChannel* GetChannel(const std::string& name) = 0;
+
+ // Destroys the given channel.
+ virtual void DestroyChannel(TransportChannel* channel) = 0;
+
+ // Invoked when we notice that there is no matching channel on our peer.
+ sigslot::signal2<Session*, const std::string&> SignalChannelGone;
+
+ // Returns the application-level description given by our client.
+ // If we are the recipient, this will be null until we send an accept.
+ const SessionDescription *local_description() const { return description_; }
+ bool set_local_description(const SessionDescription* description) {
+ if (description != description_) {
+ delete description_;
+ description_ = description;
+ }
+ return true;
+ }
+
+ // Returns the application-level description given by the other client.
+ // If we are the initiator, this will be null until we receive an accept.
+ const SessionDescription *remote_description() const {
+ return remote_description_;
+ }
+ bool set_remote_description(const SessionDescription* description) {
+ if (description != remote_description_) {
+ delete remote_description_;
+ remote_description_ = description;
+ }
+ return true;
+ }
+
+ // When we receive a session initiation from another client, we create a
+ // session in the RECEIVEDINITIATE state. We respond by accepting,
+ // rejecting, or redirecting the session somewhere else.
+ virtual bool Accept(const SessionDescription *description) = 0;
+ virtual bool Reject() = 0;
+
+ // At any time, we may terminate an outstanding session.
+ virtual bool Terminate() = 0;
+
+ // The worker thread used by the session manager
+ virtual talk_base::Thread *worker_thread() = 0;
+
+ // Returns the JID of this client.
+ const std::string &name() const { return name_; }
+
+ // Returns the JID of the other peer in this session.
+ const std::string &remote_name() const { return remote_name_; }
+
+ // Holds the ID of this session, which should be unique across the world.
+ const SessionID& id() const { return id_; }
+
+ protected:
+ State state_;
+ Error error_;
+ // Descriptions also known as "apps". See comment in
+ // constants.h about it.
+ const SessionDescription *description_;
+ const SessionDescription *remote_description_;
+ SessionID id_;
+ // We don't use buzz::Jid because changing to buzz:Jid here has a
+ // cascading effect that requires an enormous number places to
+ // change to buzz::Jid as well.
+ std::string name_;
+ std::string remote_name_;
+ talk_base::Thread *signaling_thread_;
+};
+
+// A specific Session created by the SessionManager, using XMPP for protocol.
+class Session : public BaseSession {
+ public:
+ // Returns the manager that created and owns this session.
+ SessionManager* session_manager() const { return session_manager_; }
+
+ // the worker thread used by the session manager
+ talk_base::Thread *worker_thread() {
+ return session_manager_->worker_thread();
+ }
+
+ // Returns the XML namespace identifying the type of this session.
+ const std::string& session_type() const { return session_type_; }
+
+ // Returns the client that is handling the application data of this session.
+ SessionClient* client() const { return client_; }
+
+ // Indicates whether we initiated this session.
+ bool initiator() const { return initiator_; }
+
+ // Fired whenever we receive a terminate message along with a reason
+ sigslot::signal2<Session*, const std::string&> SignalReceivedTerminateReason;
+
+ // Returns the transport that has been negotiated or NULL if negotiation is
+ // still in progress.
+ Transport* transport() const { return transport_; }
+
+ // When a session was created by us, we are the initiator, and we send the
+ // initiate message when this method is invoked. The extra_xml parameter is
+ // a list of elements that will get inserted inside <Session> ... </Session>
+ bool Initiate(const std::string& to,
+ const SessionDescription *description);
+
+ // When we receive a session initiation from another client, we create a
+ // session in the RECEIVEDINITIATE state. We respond by accepting,
+ // rejecting, or redirecting the session somewhere else.
+ virtual bool Accept(const SessionDescription *description);
+ virtual bool Reject();
+
+ // At any time, we may terminate an outstanding session.
+ virtual bool Terminate();
+
+ // The two clients in the session may also send one another arbitrary XML
+ // messages, which are called "info" messages. Both of these functions take
+ // ownership of the XmlElements and delete them when done.
+ void SendInfoMessage(const XmlElements& elems);
+ sigslot::signal2<Session*, const XmlElements&> SignalInfoMessage;
+
+ // Maps passed to serialization functions.
+ TransportParserMap GetTransportParsers();
+ FormatParserMap GetFormatParsers();
+
+ // Creates a new channel with the given name. This method may be called
+ // immediately after creating the session. However, the actual
+ // implementation may not be fixed until transport negotiation completes.
+ virtual TransportChannel* CreateChannel(const std::string& name);
+
+ // Returns the channel with the given name.
+ virtual TransportChannel* GetChannel(const std::string& name);
+
+ // Destroys the given channel.
+ virtual void DestroyChannel(TransportChannel* channel);
+
+ // Handles messages posted to us.
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+ private:
+ typedef std::map<std::string, TransportChannelProxy*> ChannelMap;
+
+ SessionManager *session_manager_;
+ bool initiator_;
+ // TODO(pthatcher): Support multiple session types and call them
+ // "format names".
+ std::string session_type_;
+ SessionClient* client_;
+ // TODO(pthatcher): reenable redirect the Jingle way
+ // std::string redirect_target_;
+
+ Transport* transport_;
+ bool transport_negotiated_;
+ // in order to resend candidates, we need to know what we sent.
+ Candidates sent_candidates_;
+ ChannelMap channels_;
+ // Keeps track of what protocol we are speaking. This was
+ // previously done using "compatibility_mode_". Now
+ // "compatibility_mode_" is when the protocol is PROTOCOL_GINGLE.
+ // But, it's no longer a binary value, since we can have
+ // PROTOCOL_JINGLE and PROTOCOL_HYBRID.
+ SignalingProtocol current_protocol_;
+
+ // Creates or destroys a session. (These are called only SessionManager.)
+ Session(SessionManager *session_manager,
+ const std::string& name,
+ const SessionID& id,
+ const std::string& session_type,
+ SessionClient* client);
+ ~Session();
+
+ // To improve connection time, this creates the channels on the most common
+ // transport type and initiates connection.
+ void ConnectDefaultTransportChannels(Transport* transport);
+ void ConnectTransportChannels(Transport* transport);
+
+ void SetTransport(Transport* transport);
+
+ // Called when the first channel of a transport begins connecting. We use
+ // this to start a timer, to make sure that the connection completes in a
+ // reasonable amount of time.
+ void OnTransportConnecting(Transport* transport);
+
+ // Called when a transport changes its writable state. We track this to make
+ // sure that the transport becomes writable within a reasonable amount of
+ // time. If this does not occur, we signal an error.
+ void OnTransportWritable(Transport* transport);
+
+ // Called when a transport requests signaling.
+ void OnTransportRequestSignaling(Transport* transport);
+
+ // Called when a transport signals that it has a message to send. Note that
+ // these messages are just the transport part of the stanza; they need to be
+ // wrapped in the appropriate session tags.
+ void OnTransportCandidatesReady(Transport* transport,
+ const Candidates& candidates);
+
+ // Called when a transport signals that it found an error in an incoming
+ // message.
+ void OnTransportSendError(Transport* transport,
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info);
+
+ // Called when we notice that one of our local channels has no peer, so it
+ // should be destroyed.
+ void OnTransportChannelGone(Transport* transport, const std::string& name);
+
+ // When the session needs to send signaling messages, it beings by requesting
+ // signaling. The client should handle this by calling OnSignalingReady once
+ // it is ready to send the messages.
+ // (These are called only by SessionManager.)
+ sigslot::signal1<Session*> SignalRequestSignaling;
+ void OnSignalingReady();
+
+ // Send various kinds of session messages.
+ void SendInitiateMessage(const SessionDescription *description);
+ void SendAcceptMessage();
+ void SendRejectMessage();
+ void SendTerminateMessage();
+ void SendTransportInfoMessage(const Candidates& candidates);
+
+ // Sends a message of the given type to the other client.
+ void SendMessage(ActionType type, const XmlElements& action_elems);
+
+ // Sends a message back to the other client indicating that we have received
+ // and accepted their message.
+ void SendAcknowledgementMessage(const buzz::XmlElement* stanza);
+
+ // Once signaling is ready, the session will use this signal to request the
+ // sending of each message. When messages are received by the other client,
+ // they should be handed to OnIncomingMessage.
+ // (These are called only by SessionManager.)
+ sigslot::signal2<Session *, const buzz::XmlElement*> SignalOutgoingMessage;
+ void OnIncomingMessage(const SessionMessage& msg);
+
+ void OnFailedSend(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* error_stanza);
+
+ // Invoked when an error is found in an incoming message. This is translated
+ // into the appropriate XMPP response by SessionManager.
+ sigslot::signal6<BaseSession*,
+ const buzz::XmlElement*,
+ const buzz::QName&,
+ const std::string&,
+ const std::string&,
+ const buzz::XmlElement*> SignalErrorMessage;
+
+ // Handlers for the various types of messages. These functions may take
+ // pointers to the whole stanza or to just the session element.
+ bool OnInitiateMessage(const SessionMessage& msg, SessionError* error);
+ bool OnAcceptMessage(const SessionMessage& msg, SessionError* error);
+ bool OnRejectMessage(const SessionMessage& msg, SessionError* error);
+ bool OnInfoMessage(const SessionMessage& msg);
+ bool OnTerminateMessage(const SessionMessage& msg, SessionError* error);
+ bool OnTransportInfoMessage(const SessionMessage& msg, SessionError* error);
+
+ // Verifies that we are in the appropriate state to receive this message.
+ bool CheckState(State state, SessionError* error);
+
+ friend class SessionManager; // For access to constructor, destructor,
+ // and signaling related methods.
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_SESSION_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/sessionclient.h b/third_party/libjingle/source/talk/p2p/base/sessionclient.h
new file mode 100644
index 0000000..c340dce
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/sessionclient.h
@@ -0,0 +1,103 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_SESSIONCLIENT_H_
+#define TALK_P2P_BASE_SESSIONCLIENT_H_
+
+namespace buzz {
+class XmlElement;
+}
+
+namespace cricket {
+
+class Session;
+class SessionDescription;
+
+// TODO(pthatcher): For Jingle migration, we're calling what was
+// previously "SessionDescription" now "FormatDescription", since we can
+// have multiple contents. But for backwards compatibility of the
+// code, we keep the SessionDescription name around. When we're ready
+// to break backwards compatibilty or make a nicer API for
+// SessionClients, we should remove the SessionDescription name
+// entirely.
+typedef SessionDescription FormatDescription;
+
+class FormatParser {
+ public:
+ // TODO(pthatcher): We decided "bool Parse(in, out*, error*)" was
+ // generally the best parse signature for parsing. However, in
+ // order to keep backwards compatibility with exisisting
+ // SessionClients, we are keeping the signatures like "out
+ // Parse(in)". Someday when we're willing to break backwards
+ // compatibility, we should change this.
+ virtual const FormatDescription* ParseFormat(
+ const buzz::XmlElement* element) = 0;
+ virtual buzz::XmlElement* WriteFormat(
+ const FormatDescription* format) = 0;
+
+ virtual ~FormatParser() {}
+};
+
+// A SessionClient exists in 1-1 relation with each session. The implementor
+// of this interface is the one that understands *what* the two sides are
+// trying to send to one another. The lower-level layers only know how to send
+// data; they do not know what is being sent.
+class SessionClient : public FormatParser {
+ public:
+ // Notifies the client of the creation / destruction of sessions of this type.
+ //
+ // IMPORTANT: The SessionClient, in its handling of OnSessionCreate, must
+ // create whatever channels are indicate in the description. This is because
+ // the remote client may already be attempting to connect those channels. If
+ // we do not create our channel right away, then connection may fail or be
+ // delayed.
+ virtual void OnSessionCreate(Session* session, bool received_initiate) = 0;
+ virtual void OnSessionDestroy(Session* session) = 0;
+
+ // For backwards compability. Old ones work with Create/Translate.
+ // In the future, use Parse/Write
+ virtual const SessionDescription* CreateSessionDescription(
+ const buzz::XmlElement* element) {return NULL;}
+ virtual buzz::XmlElement* TranslateSessionDescription(
+ const SessionDescription* description) {return NULL;}
+ virtual const FormatDescription* ParseFormat(
+ const buzz::XmlElement* element) {
+ return CreateSessionDescription(element);
+ }
+ virtual buzz::XmlElement* WriteFormat(
+ const FormatDescription* format) {
+ return TranslateSessionDescription(format);
+ }
+
+ protected:
+ // The SessionClient interface explicitly does not include destructor
+ virtual ~SessionClient() { }
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_SESSIONCLIENT_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/sessiondescription.h b/third_party/libjingle/source/talk/p2p/base/sessiondescription.h
new file mode 100644
index 0000000..2145b17
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/sessiondescription.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_P2P_BASE_SESSIONDESCRIPTION_H_
+#define TALK_P2P_BASE_SESSIONDESCRIPTION_H_
+
+namespace cricket {
+
+// Describes a session. Individual session types can override this as needed.
+class SessionDescription {
+ public:
+ virtual ~SessionDescription() {}
+};
+
+// Indicates whether a SessionDescription was an offer or an answer, as
+// described in http://www.ietf.org/rfc/rfc3264.txt. DT_UPDATE
+// indicates a jingle update message which contains a subset of a full
+// session description
+enum DescriptionType {
+ DT_OFFER, DT_ANSWER, DT_UPDATE
+};
+
+// Indicates whether a SessionDescription was sent by the local client
+// or received from the remote client.
+enum DescriptionSource {
+ DS_LOCAL, DS_REMOTE
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_SESSIONDESCRIPTION_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/sessionid.h b/third_party/libjingle/source/talk/p2p/base/sessionid.h
new file mode 100644
index 0000000..7efa235
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/sessionid.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 TALK_P2P_BASE_SESSIONID_H_
+#define TALK_P2P_BASE_SESSIONID_H_
+
+#include <string>
+#include <sstream>
+#include "talk/base/basictypes.h"
+
+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 std::string& initiator, const std::string& id_str)
+ : id_str_(id_str), initiator_(initiator) {
+ }
+ 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 // TALK_P2P_BASE_SESSIONID_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/sessionmanager.cc b/third_party/libjingle/source/talk/p2p/base/sessionmanager.cc
new file mode 100644
index 0000000..f7650d3
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/sessionmanager.cc
@@ -0,0 +1,299 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/sessionmanager.h"
+
+#include <vector>
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionmessages.h"
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/jid.h"
+
+namespace cricket {
+
+SessionManager::SessionManager(PortAllocator *allocator,
+ talk_base::Thread *worker) {
+ allocator_ = allocator;
+ signaling_thread_ = talk_base::Thread::Current();
+ if (worker == NULL) {
+ worker_thread_ = talk_base::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();
+}
+
+void SessionManager::AddClient(const std::string& session_type,
+ SessionClient* client) {
+ ASSERT(client_map_.find(session_type) == client_map_.end());
+ client_map_[session_type] = client;
+}
+
+void SessionManager::RemoveClient(const std::string& session_type) {
+ ClientMap::iterator iter = client_map_.find(session_type);
+ ASSERT(iter != client_map_.end());
+ client_map_.erase(iter);
+}
+
+SessionClient* SessionManager::GetClient(const std::string& session_type) {
+ ClientMap::iterator iter = client_map_.find(session_type);
+ return (iter != client_map_.end()) ? iter->second : NULL;
+}
+
+Session *SessionManager::CreateSession(const std::string& name,
+ const std::string& session_type) {
+ return CreateSession(name, SessionID(name, talk_base::CreateRandomId()),
+ session_type, false);
+}
+
+Session *SessionManager::CreateSession(
+ const std::string &name, const SessionID& id,
+ const std::string& session_type, bool received_initiate) {
+ SessionClient* client = GetClient(session_type);
+ ASSERT(client != NULL);
+
+ Session *session = new Session(this, name, id, session_type, client);
+ session_map_[session->id()] = session;
+ session->SignalRequestSignaling.connect(
+ this, &SessionManager::OnRequestSignaling);
+ session->SignalOutgoingMessage.connect(
+ this, &SessionManager::OnOutgoingMessage);
+ session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage);
+ SignalSessionCreate(session, received_initiate);
+ session->client()->OnSessionCreate(session, received_initiate);
+ return session;
+}
+
+void SessionManager::DestroySession(Session *session) {
+ if (session != NULL) {
+ SessionMap::iterator it = session_map_.find(session->id());
+ if (it != session_map_.end()) {
+ SignalSessionDestroy(session);
+ session->client()->OnSessionDestroy(session);
+ session_map_.erase(it);
+ delete session;
+ }
+ }
+}
+
+Session *SessionManager::GetSession(const SessionID& id) {
+ SessionMap::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();
+ }
+}
+
+bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
+ return cricket::IsSessionMessage(stanza);
+}
+
+Session* SessionManager::FindSession(const std::string& sid,
+ const std::string& initiator,
+ const std::string& remote_name) {
+ SessionMap::iterator iter = session_map_.find(SessionID(initiator, sid));
+ if (iter == session_map_.end())
+ return NULL;
+
+ Session* session = iter->second;
+ if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name()))
+ return NULL;
+
+ return session;
+}
+
+void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) {
+ SessionMessage msg;
+ ParseError error;
+
+ if (!ParseSessionMessage(stanza, &msg, &error)) {
+ SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ error.text, NULL);
+ return;
+ }
+
+ Session* session = FindSession(msg.sid, msg.initiator, msg.from);
+ if (session) {
+ session->OnIncomingMessage(msg);
+ return;
+ }
+
+ if (msg.type != ACTION_SESSION_INITIATE) {
+ SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ "unknown session", NULL);
+ return;
+ }
+
+ std::string format_name;
+ if (!ParseFormatName(msg.action_elem, &format_name, &error)) {
+ SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ error.text, NULL);
+ return;
+ }
+
+ if (!GetClient(format_name)) {
+ SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
+ "unknown session description type", NULL);
+ return;
+ }
+
+ session = CreateSession(msg.to,
+ SessionID(msg.initiator, msg.sid),
+ format_name, true);
+ session->OnIncomingMessage(msg);
+}
+
+void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* response_stanza) {
+ // We don't do anything with the response now. If we need to we can forward
+ // it to the session.
+ return;
+}
+
+void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* error_stanza) {
+ SessionMessage msg;
+ ParseError error;
+ if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
+ return; // TODO(pthatcher): log somewhere?
+ }
+
+ Session* session = FindSession(msg.sid, msg.initiator, msg.to);
+ if (session) {
+ talk_base::scoped_ptr<buzz::XmlElement> synthetic_error;
+ if (!error_stanza) {
+ // A failed send is semantically equivalent to an error response, so we
+ // can just turn the former into the latter.
+ synthetic_error.reset(
+ CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND,
+ "cancel", "Recipient did not respond", NULL));
+ error_stanza = synthetic_error.get();
+ }
+
+ session->OnFailedSend(orig_stanza, error_stanza);
+ }
+}
+
+void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ talk_base::scoped_ptr<buzz::XmlElement> msg(
+ CreateErrorMessage(stanza, name, type, text, extra_info));
+ SignalOutgoingMessage(this, msg.get());
+}
+
+buzz::XmlElement* SessionManager::CreateErrorMessage(
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ);
+ iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM));
+ iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
+ iq->SetAttr(buzz::QN_TYPE, "error");
+
+ for (const buzz::XmlElement* elem = stanza->FirstElement();
+ elem != NULL;
+ elem = elem->NextElement()) {
+ iq->AddElement(new buzz::XmlElement(*elem));
+ }
+
+ buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR);
+ error->SetAttr(buzz::QN_TYPE, type);
+ iq->AddElement(error);
+
+ // If the error name is not in the standard namespace, we have to first add
+ // some error from that namespace.
+ if (name.Namespace() != buzz::NS_STANZA) {
+ error->AddElement(
+ new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION));
+ }
+ error->AddElement(new buzz::XmlElement(name));
+
+ if (extra_info)
+ error->AddElement(new buzz::XmlElement(*extra_info));
+
+ if (text.size() > 0) {
+ // It's okay to always use English here. This text is for debugging
+ // purposes only.
+ buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT);
+ text_elem->SetAttr(buzz::QN_XML_LANG, "en");
+ text_elem->SetBodyText(text);
+ error->AddElement(text_elem);
+ }
+
+ // TODO: Should we include error codes as well for SIP compatibility?
+
+ return iq;
+}
+
+void SessionManager::OnOutgoingMessage(Session* session,
+ const buzz::XmlElement* stanza) {
+ SignalOutgoingMessage(this, stanza);
+}
+
+void SessionManager::OnErrorMessage(BaseSession* session,
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info) {
+ SendErrorMessage(stanza, name, type, text, extra_info);
+}
+
+void SessionManager::OnSignalingReady() {
+ for (SessionMap::iterator it = session_map_.begin();
+ it != session_map_.end();
+ ++it) {
+ it->second->OnSignalingReady();
+ }
+}
+
+void SessionManager::OnRequestSignaling(Session* session) {
+ SignalRequestSignaling();
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/sessionmanager.h b/third_party/libjingle/source/talk/p2p/base/sessionmanager.h
new file mode 100644
index 0000000..4f273cd
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/sessionmanager.h
@@ -0,0 +1,187 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_SESSIONMANAGER_H_
+#define TALK_P2P_BASE_SESSIONMANAGER_H_
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+#include "talk/base/thread.h"
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/sessionid.h"
+#include "talk/base/sigslot.h"
+
+namespace buzz {
+class QName;
+class XmlElement;
+}
+
+namespace cricket {
+
+class Session;
+class BaseSession;
+class SessionClient;
+
+// SessionManager manages session instances
+
+class SessionManager : public sigslot::has_slots<> {
+ public:
+ SessionManager(PortAllocator *allocator,
+ talk_base::Thread *worker_thread = NULL);
+ virtual ~SessionManager();
+
+ PortAllocator *port_allocator() const { return allocator_; }
+ talk_base::Thread *worker_thread() const { return worker_thread_; }
+ talk_base::Thread *signaling_thread() const { return signaling_thread_; }
+
+ int session_timeout() const { return timeout_; }
+ void set_session_timeout(int timeout) { timeout_ = timeout; }
+
+ // Registers support for the given client. If we receive an initiate
+ // describing a session of the given type, we will automatically create a
+ // Session object and notify this client. The client may then accept or
+ // reject the session.
+ void AddClient(const std::string& session_type, SessionClient* client);
+ void RemoveClient(const std::string& session_type);
+ SessionClient* GetClient(const std::string& session_type);
+
+ // Creates a new session. The given name is the JID of the client on whose
+ // behalf we initiate the session.
+ Session *CreateSession(const std::string& name,
+ const std::string& session_type);
+
+ // Destroys the given session.
+ void DestroySession(Session *session);
+
+ // Returns the session with the given ID or NULL if none exists.
+ Session *GetSession(const SessionID& id);
+
+ // Terminates all of the sessions created by this manager.
+ void TerminateAll();
+
+ // These are signaled whenever the set of existing sessions changes.
+ sigslot::signal2<Session *, bool> SignalSessionCreate;
+ sigslot::signal1<Session *> SignalSessionDestroy;
+
+ // Determines whether the given stanza is intended for some session.
+ bool IsSessionMessage(const buzz::XmlElement* stanza);
+
+ // Given a sid, initiator, and remote_name, this finds the matching Session
+ Session* FindSession(const std::string& sid,
+ const std::string& initiator,
+ const std::string& remote_name);
+
+ // Called when we receive a stanza for which IsSessionMessage is true.
+ void OnIncomingMessage(const buzz::XmlElement* stanza);
+
+ // Called when we get a response to a message that we sent.
+ void OnIncomingResponse(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* response_stanza);
+
+ // Called if an attempted to send times out or an error is returned. In the
+ // timeout case error_stanza will be NULL
+ void OnFailedSend(const buzz::XmlElement* orig_stanza,
+ const buzz::XmlElement* error_stanza);
+
+ // Signalled each time a session generates a signaling message to send.
+ // Also signalled on errors, but with a NULL session.
+ sigslot::signal2<SessionManager*,
+ const buzz::XmlElement*> SignalOutgoingMessage;
+
+ // Signaled before sessions try to send certain signaling messages. The
+ // client should call OnSignalingReady once it is safe to send them. These
+ // steps are taken so that we don't send signaling messages trying to
+ // re-establish the connectivity of a session when the client cannot send
+ // the messages (and would probably just drop them on the floor).
+ //
+ // Note: you can connect this directly to OnSignalingReady(), if a signalling
+ // check is not supported.
+ sigslot::signal0<> SignalRequestSignaling;
+ void OnSignalingReady();
+
+ private:
+ typedef std::map<SessionID, Session*> SessionMap;
+ typedef std::map<std::string, SessionClient*> ClientMap;
+
+ PortAllocator *allocator_;
+ talk_base::Thread *signaling_thread_;
+ talk_base::Thread *worker_thread_;
+ int timeout_;
+ SessionMap session_map_;
+ ClientMap client_map_;
+
+ // Helper function for CreateSession. This is also invoked when we receive
+ // a message attempting to initiate a session with this client.
+ Session *CreateSession(const std::string& name,
+ const SessionID& id,
+ const std::string& session_type,
+ bool received_initiate);
+
+ // Attempts to find a registered session type whose description appears as
+ // a child of the session element. Such a child should be present indicating
+ // the application they hope to initiate.
+ std::string FindClient(const buzz::XmlElement* session);
+
+ // Sends a message back to the other client indicating that we found an error
+ // in the stanza they sent. name identifies the error, type is one of the
+ // standard XMPP types (cancel, continue, modify, auth, wait), and text is a
+ // description for debugging purposes.
+ void SendErrorMessage(const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info);
+
+ // Creates and returns an error message from the given components. The
+ // caller is responsible for deleting this.
+ buzz::XmlElement* CreateErrorMessage(
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info);
+
+ // Called each time a session requests signaling.
+ void OnRequestSignaling(Session* session);
+
+ // Called each time a session has an outgoing message.
+ void OnOutgoingMessage(Session* session, const buzz::XmlElement* stanza);
+
+ // Called each time a session has an error to send.
+ void OnErrorMessage(BaseSession* session,
+ const buzz::XmlElement* stanza,
+ const buzz::QName& name,
+ const std::string& type,
+ const std::string& text,
+ const buzz::XmlElement* extra_info);
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_SESSIONMANAGER_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc b/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc
new file mode 100644
index 0000000..de41be2
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc
@@ -0,0 +1,375 @@
+/*
+ * libjingle
+ * Copyright 2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/sessionmessages.h"
+#include "talk/xmpp/constants.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/p2ptransport.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/transport.h"
+
+namespace cricket {
+
+ActionType ToActionType(const std::string& type) {
+ if (type == GINGLE_ACTION_INITIATE)
+ return ACTION_SESSION_INITIATE;
+ if (type == GINGLE_ACTION_INFO)
+ return ACTION_SESSION_INFO;
+ if (type == GINGLE_ACTION_ACCEPT)
+ return ACTION_SESSION_ACCEPT;
+ if (type == GINGLE_ACTION_REJECT)
+ return ACTION_SESSION_REJECT;
+ if (type == GINGLE_ACTION_TERMINATE)
+ return ACTION_SESSION_TERMINATE;
+ if (type == GINGLE_ACTION_CANDIDATES)
+ return ACTION_TRANSPORT_INFO;
+ if (type == JINGLE_ACTION_TRANSPORT_INFO)
+ return ACTION_TRANSPORT_INFO;
+
+ return ACTION_UNKNOWN;
+}
+
+std::string ToString(ActionType type, SignalingProtocol protocol) {
+ switch (type) {
+ case ACTION_SESSION_INITIATE:
+ return GINGLE_ACTION_INITIATE;
+ case ACTION_SESSION_INFO:
+ return GINGLE_ACTION_INFO;
+ case ACTION_SESSION_ACCEPT:
+ return GINGLE_ACTION_ACCEPT;
+ case ACTION_SESSION_REJECT:
+ return GINGLE_ACTION_REJECT;
+ case ACTION_SESSION_TERMINATE:
+ return GINGLE_ACTION_TERMINATE;
+ case ACTION_TRANSPORT_INFO:
+ if (protocol == PROTOCOL_GINGLE2)
+ return JINGLE_ACTION_TRANSPORT_INFO;
+ else
+ return GINGLE_ACTION_CANDIDATES;
+ default:
+ return "";
+ }
+}
+
+bool IsSessionMessage(const buzz::XmlElement* stanza) {
+ if (stanza->Name() != buzz::QN_IQ ||
+ stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET)
+ return false;
+
+ const buzz::XmlElement* action = stanza->FirstNamed(QN_GINGLE_SESSION);
+ if (action == NULL)
+ return false;
+
+ return (action->HasAttr(buzz::QN_TYPE) &&
+ action->HasAttr(buzz::QN_ID) &&
+ action->HasAttr(QN_INITIATOR));
+}
+
+bool ParseSessionMessage(const buzz::XmlElement* stanza,
+ SessionMessage* msg,
+ ParseError* error) {
+ const buzz::XmlElement* action;
+ if (!RequireXmlChild(stanza, QN_GINGLE_SESSION.LocalPart(), &action, error))
+ return false;
+
+ std::string type_string;
+ if (!RequireXmlAttr(action, buzz::QN_TYPE, &type_string, error))
+ return false;
+
+ msg->id = stanza->Attr(buzz::QN_ID);
+ msg->from = stanza->Attr(buzz::QN_FROM);
+ msg->to = stanza->Attr(buzz::QN_TO);
+ msg->stanza = stanza;
+
+ msg->sid = action->Attr(buzz::QN_ID);
+ msg->initiator = action->Attr(QN_INITIATOR);
+ msg->type = ToActionType(type_string);
+ if (msg->type == ACTION_UNKNOWN)
+ return BadParse("unknown action: " + type_string, error);
+ msg->action_elem = action;
+
+ if (type_string == GINGLE_ACTION_CANDIDATES ||
+ (msg->type == ACTION_SESSION_INITIATE &&
+ action->FirstNamed(QN_GINGLE_P2P_TRANSPORT) == NULL)) {
+ msg->protocol = PROTOCOL_GINGLE;
+ } else {
+ msg->protocol = PROTOCOL_GINGLE2;
+ }
+
+ return true;
+}
+
+void WriteSessionMessage(const SessionMessage& msg,
+ const XmlElements& action_elems,
+ buzz::XmlElement* stanza) {
+ stanza->SetAttr(buzz::QN_TO, msg.to);
+ stanza->SetAttr(buzz::QN_TYPE, buzz::STR_SET);
+
+ buzz::XmlElement* action = new buzz::XmlElement(QN_GINGLE_SESSION, true);
+ action->AddAttr(buzz::QN_TYPE,
+ ToString(msg.type, msg.protocol));
+ action->AddAttr(buzz::QN_ID, msg.sid);
+ action->AddAttr(QN_INITIATOR, msg.initiator);
+
+ AddXmlChildren(action, action_elems);
+
+ stanza->AddElement(action);
+}
+
+
+TransportParser* GetTransportParser(const TransportParserMap& trans_parsers,
+ const std::string& name) {
+ TransportParserMap::const_iterator map = trans_parsers.find(name);
+ if (map == trans_parsers.end()) {
+ return NULL;
+ } else {
+ return map->second;
+ }
+}
+
+bool ParseCandidates(const buzz::XmlElement* candidates_elem,
+ const TransportParserMap& trans_parsers,
+ const std::string& trans_name,
+ Candidates* candidates,
+ ParseError* error) {
+ TransportParser* trans_parser = GetTransportParser(trans_parsers, trans_name);
+ if (trans_parser == NULL)
+ return BadParse("unknown transport type: " + trans_name, error);
+
+ return trans_parser->ParseCandidates(candidates_elem, candidates, error);
+}
+
+// Pass in NULL candidates if you don't want them parsed.
+bool ParseGingleTransport(const buzz::XmlElement* action_elem,
+ const TransportParserMap& trans_parsers,
+ std::string* name,
+ Candidates* candidates,
+ ParseError* error) {
+ const buzz::XmlElement* candidates_elem;
+ const buzz::XmlElement* trans_elem = GetXmlChild(action_elem, LN_TRANSPORT);
+ if (trans_elem == NULL) { // PROTCOL_GINGLE
+ *name = NS_GINGLE_P2P;
+ candidates_elem = action_elem;
+ } else { // PROTOCOL_GINGLE2
+ *name = trans_elem->Name().Namespace();
+ candidates_elem = trans_elem;
+ }
+
+ if (candidates != NULL) {
+ return ParseCandidates(candidates_elem, trans_parsers, *name,
+ candidates, error);
+ }
+ return true;
+}
+
+bool ParseGingleTransportName(const buzz::XmlElement* action_elem,
+ std::string* name,
+ ParseError* error) {
+ return ParseGingleTransport(action_elem, TransportParserMap(),
+ name, NULL, error);
+}
+
+buzz::XmlElement* NewTransportElement(const std::string& name) {
+ return new buzz::XmlElement(buzz::QName(true, name, LN_TRANSPORT), true);
+}
+
+void WriteGingleTransport(const std::string& trans_name,
+ const Candidates& candidates,
+ const TransportParserMap& trans_parsers,
+ SignalingProtocol protocol,
+ XmlElements* elems) {
+ TransportParser* trans_parser = GetTransportParser(trans_parsers, trans_name);
+ if (trans_parser == NULL)
+ // TODO(pthatcher): should we handle errors from writes?
+ // return BadParse("unknown transport type: " + trans_name, error);
+ return;
+
+ if (protocol == PROTOCOL_GINGLE2) {
+ buzz::XmlElement* trans_elem = NewTransportElement(trans_name);
+ XmlElements cand_elems;
+ trans_parser->WriteCandidates(candidates, protocol, &cand_elems);
+ AddXmlChildren(trans_elem, cand_elems);
+ elems->push_back(trans_elem);
+ } else {
+ trans_parser->WriteCandidates(candidates, protocol, elems);
+ }
+}
+
+void WriteGingleTransportWithoutCandidates(const std::string& trans_name,
+ SignalingProtocol protocol,
+ XmlElements* elems) {
+ if (protocol == PROTOCOL_GINGLE2) {
+ elems->push_back(NewTransportElement(trans_name));
+ }
+}
+
+FormatParser* GetFormatParser(const FormatParserMap& format_parsers,
+ const std::string& name) {
+ FormatParserMap::const_iterator map = format_parsers.find(name);
+ if (map == format_parsers.end()) {
+ return NULL;
+ } else {
+ return map->second;
+ }
+}
+
+// Pass in a NULL format to disable format parsing (so you only get the name).
+bool ParseFormat(const buzz::XmlElement* format_elem,
+ const FormatParserMap& format_parsers,
+ std::string* format_name,
+ const FormatDescription** format,
+ ParseError* error) {
+ *format_name = format_elem->Name().Namespace();
+ if (format != NULL) {
+ FormatParser* format_parser = GetFormatParser(format_parsers, *format_name);
+ if (format_parser == NULL)
+ return BadParse("unknown application format: " + *format_name, error);
+ *format = format_parser->ParseFormat(format_elem);
+ }
+ return true;
+}
+
+bool ParseGingleFormat(const buzz::XmlElement* action_elem,
+ const FormatParserMap& format_parsers,
+ std::string* format_name,
+ const FormatDescription** format,
+ ParseError* error) {
+ const buzz::XmlElement* format_elem;
+ if (!RequireXmlChild(action_elem, LN_DESCRIPTION, &format_elem, error))
+ return false;
+
+ return ParseFormat(format_elem, format_parsers,
+ format_name, format, error);
+}
+
+void WriteFormat(const std::string& format_name,
+ const FormatDescription* format,
+ const FormatParserMap& format_parsers,
+ XmlElements* elems) {
+ FormatParser* format_parser =
+ GetFormatParser(format_parsers, format_name);
+ if (format_parser == NULL)
+ // TODO(pthatcher): should we handle errors from writes?
+ // return error->Set("unknown application format: " + init.format_name);
+ return;
+
+ elems->push_back(format_parser->WriteFormat(format));
+}
+
+bool ParseFormatName(const buzz::XmlElement* action_elem,
+ std::string* format_name,
+ ParseError* error) {
+ return ParseGingleFormat(action_elem, FormatParserMap(),
+ format_name, NULL, error);
+}
+
+bool ParseSessionInitiate(const buzz::XmlElement* action_elem,
+ const FormatParserMap& format_parsers,
+ SessionInitiate* init, ParseError* error) {
+ std::string format_name;
+ const FormatDescription* format;
+ if (!ParseGingleFormat(action_elem, format_parsers,
+ &format_name, &format, error))
+ return false;
+ init->SetFormat(format_name, format);
+
+ if (!ParseGingleTransportName(action_elem, &(init->transport_name), error))
+ return false;
+
+ return true;
+}
+
+void WriteSessionInitiate(const SessionInitiate& init,
+ const FormatParserMap& format_parsers,
+ SignalingProtocol protocol,
+ XmlElements* elems) {
+ WriteFormat(init.format_name, init.format, format_parsers, elems);
+ // We don't have any candidates yet, so only send the transport
+ // name. Send candidates asynchronously later with transport-info
+ // or candidates messages.
+ WriteGingleTransportWithoutCandidates(init.transport_name, protocol, elems);
+}
+
+bool ParseSessionAccept(const buzz::XmlElement* action_elem,
+ const FormatParserMap& format_parsers,
+ SessionAccept* accept, ParseError* error) {
+ std::string format_name;
+ const FormatDescription* format;
+ if (!ParseGingleFormat(action_elem, format_parsers,
+ &format_name, &format, error))
+ return false;
+ accept->SetFormat(format_name, format);
+
+ return true;
+}
+
+void WriteSessionAccept(const SessionAccept& accept,
+ const FormatParserMap& format_parsers,
+ XmlElements* elems) {
+ WriteFormat(accept.format_name, accept.format, format_parsers,
+ elems);
+}
+
+bool ParseSessionTerminate(const buzz::XmlElement* action_elem,
+ SessionTerminate* term, ParseError* error) {
+ const buzz::XmlElement* reason_elem = action_elem->FirstElement();
+ if (reason_elem != NULL) {
+ term->reason = reason_elem->Name().LocalPart();
+ const buzz::XmlElement *debug_elem = reason_elem->FirstElement();
+ if (debug_elem != NULL) {
+ term->debug_reason = debug_elem->Name().LocalPart();
+ }
+ }
+ return true;
+}
+
+void WriteSessionTerminate(const SessionTerminate& term,
+ XmlElements* elems) {
+ elems->push_back(new buzz::XmlElement(
+ buzz::QName(true, NS_EMPTY, term.reason)));
+}
+
+bool ParseTransportInfo(const buzz::XmlElement* action_elem,
+ const TransportParserMap& trans_parsers,
+ TransportInfo* info, ParseError* error) {
+ return ParseGingleTransport(action_elem, trans_parsers,
+ &(info->transport_name), &(info->candidates),
+ error);
+}
+
+void WriteTransportInfo(const TransportInfo& info,
+ const TransportParserMap& trans_parsers,
+ SignalingProtocol protocol,
+ XmlElements* elems) {
+ WriteGingleTransport(info.transport_name, info.candidates, trans_parsers,
+ protocol, elems);
+}
+
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/sessionmessages.h b/third_party/libjingle/source/talk/p2p/base/sessionmessages.h
new file mode 100644
index 0000000..7895805
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/sessionmessages.h
@@ -0,0 +1,204 @@
+/*
+ * libjingle
+ * Copyright 2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_SESSIONMESSAGES_H_
+#define TALK_P2P_BASE_SESSIONMESSAGES_H_
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "talk/xmllite/xmlelement.h"
+#include "talk/p2p/base/constants.h"
+// Needed to delete SessionInitiate.format.
+#include "talk/p2p/base/sessiondescription.h"
+
+namespace cricket {
+
+struct ParseError;
+class Candidate;
+class FormatParser;
+class TransportParser;
+
+// see comment in constants.h about FormatDescription and
+// SessionDescription being the same. SessionDescription is the old
+// word. FormatDescription is the new word.
+typedef SessionDescription FormatDescription;
+typedef std::vector<buzz::XmlElement*> XmlElements;
+typedef std::vector<Candidate> Candidates;
+typedef std::map<std::string, FormatParser*> FormatParserMap;
+typedef std::map<std::string, TransportParser*> TransportParserMap;
+
+enum ActionType {
+ ACTION_UNKNOWN,
+
+ ACTION_SESSION_INITIATE,
+ ACTION_SESSION_INFO,
+ ACTION_SESSION_ACCEPT,
+ ACTION_SESSION_REJECT,
+ ACTION_SESSION_TERMINATE,
+
+ ACTION_TRANSPORT_INFO,
+ ACTION_TRANSPORT_ACCEPT,
+};
+
+// Abstraction of a <jingle> element within an <iq> stanza, per XMPP
+// standard XEP-166. Can be serialized into multiple protocols,
+// including the standard (Jingle) and the draft standard (Gingle).
+// In general, used to communicate actions related to a p2p session,
+// such accept, initiate, terminate, etc.
+
+struct SessionMessage {
+ SessionMessage() : action_elem(NULL), stanza(NULL) {}
+
+ SessionMessage(SignalingProtocol protocol, ActionType type,
+ const std::string& sid, const std::string& initiator) :
+ protocol(protocol), type(type), sid(sid), initiator(initiator),
+ action_elem(NULL), stanza(NULL) {}
+
+ std::string id;
+ std::string from;
+ std::string to;
+ SignalingProtocol protocol;
+ ActionType type;
+ std::string sid; // session id
+ std::string initiator;
+
+ // Used for further parsing when necessary.
+ // Represents <session> or <jingle>.
+ const buzz::XmlElement* action_elem;
+ // Mostly used for debugging.
+ const buzz::XmlElement* stanza;
+};
+
+// These are different "action"s found in <jingle> or <session> The
+// Jingle specs allows actions to have multiple "contents", where each
+// content is a pair of format+transport. our Sessions can only
+// handle one such content. It will be a long time before we can
+// support multiple contents in the session. So, our actions only
+// supports one.
+
+// TODO(pthatcher): switch to jingle-style contents (Session has
+// multiple Contents, which each are a pairs of (format, transport)
+
+struct SessionInitiate {
+ SessionInitiate() : format(NULL), owns_format(false) {}
+
+ SessionInitiate(const std::string& transport_name,
+ const std::string& format_name,
+ const FormatDescription* format) :
+ transport_name(transport_name),
+ format_name(format_name), format(format), owns_format(false) {}
+
+ ~SessionInitiate() {
+ if (owns_format) {
+ delete format;
+ }
+ }
+
+ // Object takes ownership of format.
+ void SetFormat(const std::string& format_name,
+ const FormatDescription* format) {
+ this->format_name = format_name;
+ this->format = format;
+ this->owns_format = true;
+ }
+
+ // Caller takes ownership of format.
+ const FormatDescription* AdoptFormat() {
+ const FormatDescription* out = format;
+ format = NULL;
+ owns_format = false;
+ return out;
+ }
+
+ std::string transport_name; // xmlns of <transport>
+ // TODO(pthatcher): Jingle spec allows candidates to be in the
+ // initiate. We should support receiving them.
+ std::string format_name; // xmlns of <description>
+ const FormatDescription* format;
+ bool owns_format;
+};
+
+typedef SessionInitiate SessionAccept;
+
+struct SessionTerminate {
+ std::string reason;
+ std::string debug_reason;
+};
+
+struct TransportInfo {
+ TransportInfo() {}
+
+ TransportInfo(const std::string& transport_name,
+ const Candidates& candidates) :
+ transport_name(transport_name), candidates(candidates) {}
+
+ std::string transport_name; // xmlns of <transport>
+ Candidates candidates;
+};
+
+struct SessionReject {
+};
+
+bool IsSessionMessage(const buzz::XmlElement* stanza);
+bool ParseSessionMessage(const buzz::XmlElement* stanza,
+ SessionMessage* msg,
+ ParseError* error);
+bool ParseFormatName(const buzz::XmlElement* action_elem,
+ std::string* format_name,
+ ParseError* error);
+void WriteSessionMessage(const SessionMessage& msg,
+ const XmlElements& action_elems,
+ buzz::XmlElement* stanza);
+bool ParseSessionInitiate(const buzz::XmlElement* action_elem,
+ const FormatParserMap& format_parsers,
+ SessionInitiate* init, ParseError* error);
+void WriteSessionInitiate(const SessionInitiate& init,
+ const FormatParserMap& format_parsers,
+ SignalingProtocol protocol,
+ XmlElements* elems);
+bool ParseSessionAccept(const buzz::XmlElement* action_elem,
+ const FormatParserMap& format_parsers,
+ SessionAccept* accept, ParseError* error);
+void WriteSessionAccept(const SessionAccept& accept,
+ const FormatParserMap& format_parsers,
+ XmlElements* elems);
+bool ParseSessionTerminate(const buzz::XmlElement* action_elem,
+ SessionTerminate* term, ParseError* error);
+void WriteSessionTerminate(const SessionAccept& term,
+ XmlElements* elems);
+bool ParseTransportInfo(const buzz::XmlElement* action_elem,
+ const TransportParserMap& trans_parsers,
+ TransportInfo* info, ParseError* error);
+void WriteTransportInfo(const TransportInfo& info,
+ const TransportParserMap& trans_parsers,
+ SignalingProtocol protocol,
+ XmlElements* elems);
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_SESSIONMESSAGES_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/stun.cc b/third_party/libjingle/source/talk/p2p/base/stun.cc
new file mode 100644
index 0000000..c62f70e
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/stun.cc
@@ -0,0 +1,580 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/stun.h"
+
+#include <cstring>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+
+using talk_base::ByteBuffer;
+
+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 (type_ & 0x8000) {
+ // rtp and rtcp set MSB of first byte, since first two bits are version,
+ // and version is always 2 (10). If set, this is not a stun packet.
+ 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 (" << rest << " != " << 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, static_cast<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_ != NULL);
+ ASSERT((0 <= index) && (index < length()));
+ return static_cast<uint8>(bytes_[index]);
+}
+
+void StunByteStringAttribute::SetByte(int index, uint8 value) {
+ ASSERT(bytes_ != NULL);
+ 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 + static_cast<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(static_cast<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/third_party/libjingle/source/talk/p2p/base/stun.h b/third_party/libjingle/source/talk/p2p/base/stun.h
new file mode 100644
index 0000000..2282fed
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/stun.h
@@ -0,0 +1,365 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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 <string>
+#include <vector>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/bytebuffer.h"
+
+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(talk_base::ByteBuffer* buf);
+
+ // Writes this object into a STUN/TURN packet. Return value is true if
+ // successful.
+ void Write(talk_base::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(talk_base::ByteBuffer* buf) = 0;
+
+ // Writes the body (not the type or length) to the given buffer. Return
+ // value is true if successful.
+ virtual void Write(talk_base::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(talk_base::ByteBuffer* buf);
+ void Write(talk_base::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(talk_base::ByteBuffer* buf);
+ void Write(talk_base::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(talk_base::ByteBuffer* buf);
+ void Write(talk_base::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(talk_base::ByteBuffer* buf);
+ void Write(talk_base::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(talk_base::ByteBuffer* buf);
+ void Write(talk_base::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(talk_base::ByteBuffer* buf);
+ void Write(talk_base::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/third_party/libjingle/source/talk/p2p/base/stunport.cc b/third_party/libjingle/source/talk/p2p/base/stunport.cc
new file mode 100644
index 0000000..412a661
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/stunport.cc
@@ -0,0 +1,248 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/stunport.h"
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/helpers.h"
+#include "talk/base/nethelpers.h"
+#include "talk/p2p/base/common.h"
+
+namespace cricket {
+
+// TODO(juberti): Move these to a common place (used in relayport too)
+const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts
+const int RETRY_DELAY = 50; // 50ms, from ICE spec
+const int 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, bool keep_alive,
+ const talk_base::SocketAddress& addr)
+ : port_(port), keep_alive_(keep_alive), server_addr_(addr) {
+ start_time_ = talk_base::Time();
+ }
+
+ virtual ~StunPortBindingRequest() {
+ }
+
+ const talk_base::SocketAddress& server_addr() const { return server_addr_; }
+
+ 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(LS_ERROR) << "Binding response missing mapped address.";
+ } else if (addr_attr->family() != 1) {
+ LOG(LS_ERROR) << "Binding address has bad family";
+ } else {
+ talk_base::SocketAddress addr(addr_attr->ip(), addr_attr->port());
+ port_->AddAddress(addr, "udp", true);
+ }
+
+ // We will do a keep-alive regardless of whether this request suceeds.
+ // This should have almost no impact on network usage.
+ if (keep_alive_) {
+ port_->requests_.SendDelayed(
+ new StunPortBindingRequest(port_, true, server_addr_),
+ KEEPALIVE_DELAY);
+ }
+ }
+
+ virtual void OnErrorResponse(StunMessage* response) {
+ const StunErrorCodeAttribute* attr = response->GetErrorCode();
+ if (!attr) {
+ LOG(LS_ERROR) << "Bad allocate response error code";
+ } else {
+ LOG(LS_ERROR) << "Binding error response:"
+ << " class=" << attr->error_class()
+ << " number=" << attr->number()
+ << " reason='" << attr->reason() << "'";
+ }
+
+ port_->SignalAddressError(port_);
+
+ if (keep_alive_
+ && (talk_base::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
+ port_->requests_.SendDelayed(
+ new StunPortBindingRequest(port_, true, server_addr_),
+ KEEPALIVE_DELAY);
+ }
+ }
+
+ virtual void OnTimeout() {
+ LOG(LS_ERROR) << "Binding request timed out from "
+ << port_->GetLocalAddress().ToString()
+ << " (" << port_->network()->name() << ")";
+
+ port_->SignalAddressError(port_);
+
+ if (keep_alive_
+ && (talk_base::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
+ port_->requests_.SendDelayed(
+ new StunPortBindingRequest(port_, true, server_addr_),
+ RETRY_DELAY);
+ }
+ }
+
+ private:
+ StunPort* port_;
+ bool keep_alive_;
+ talk_base::SocketAddress server_addr_;
+ uint32 start_time_;
+};
+
+const std::string STUN_PORT_TYPE("stun");
+
+StunPort::StunPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& server_addr)
+ : Port(thread, STUN_PORT_TYPE, factory, network),
+ server_addr_(server_addr), requests_(thread), socket_(NULL), error_(0),
+ resolver_(NULL) {
+ requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket);
+}
+
+bool StunPort::Init(const talk_base::SocketAddress& local_addr) {
+ socket_ = CreatePacketSocket(PROTO_UDP);
+ if (!socket_) {
+ LOG_J(LS_WARNING, this) << "UDP socket creation failed";
+ return false;
+ }
+ if (socket_->Bind(local_addr) < 0) {
+ LOG_J(LS_WARNING, this) << "UDP bind failed with error "
+ << socket_->GetError();
+ return false;
+ }
+ socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket);
+ return true;
+}
+
+StunPort::~StunPort() {
+ if (resolver_) {
+ resolver_->Destroy(false);
+ }
+ delete socket_;
+}
+
+void StunPort::PrepareAddress() {
+ // We will keep pinging the stun server to make sure our NAT pin-hole stays
+ // open during the call.
+ if (server_addr_.IsUnresolved()) {
+ ResolveStunAddress();
+ } else {
+ requests_.Send(new StunPortBindingRequest(this, true, server_addr_));
+ }
+}
+
+void StunPort::PrepareSecondaryAddress() {
+ // DNS resolution of the secondary address is not currently supported.
+ ASSERT(!server_addr2_.IsAny());
+ requests_.Send(new StunPortBindingRequest(this, false, server_addr2_));
+}
+
+Connection* StunPort::CreateConnection(const Candidate& address,
+ CandidateOrigin origin) {
+ if (address.protocol() != "udp")
+ return NULL;
+
+ Connection* conn = new ProxyConnection(this, 0, address);
+ AddConnection(conn);
+ return conn;
+}
+
+int StunPort::SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload) {
+ int sent = socket_->SendTo(data, size, addr);
+ if (sent < 0) {
+ error_ = socket_->GetError();
+ LOG_J(LS_ERROR, this) << "UDP send of " << size
+ << " bytes failed with error " << error_;
+ }
+ return sent;
+}
+
+int StunPort::SetOption(talk_base::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 talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ ASSERT(socket == socket_);
+
+ // Look for a response to a binding request.
+ if (requests_.CheckResponse(data, size))
+ return;
+
+ if (Connection* conn = GetConnection(remote_addr)) {
+ conn->OnReadPacket(data, size);
+ } else {
+ Port::OnReadPacket(data, size, remote_addr);
+ }
+}
+
+void StunPort::ResolveStunAddress() {
+ if (resolver_)
+ return;
+
+ resolver_ = new talk_base::AsyncResolver();
+ resolver_->SignalWorkDone.connect(this, &StunPort::OnResolveResult);
+ resolver_->set_address(server_addr_);
+ resolver_->Start();
+}
+
+void StunPort::OnResolveResult(talk_base::SignalThread* t) {
+ ASSERT(t == resolver_);
+ if (resolver_->error() != 0) {
+ LOG_J(LS_WARNING, this) << "StunPort: stun host lookup received error "
+ << resolver_->error();
+ SignalAddressError(this);
+ }
+
+ server_addr_ = resolver_->address();
+ PrepareAddress();
+}
+
+// TODO(juberti): merge this with SendTo above.
+void StunPort::OnSendPacket(const void* data, size_t size, StunRequest* req) {
+ StunPortBindingRequest* sreq = static_cast<StunPortBindingRequest*>(req);
+ if (socket_->SendTo(data, size, sreq->server_addr()) < 0)
+ PLOG(LERROR, socket_->GetError()) << "sendto";
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/stunport.h b/third_party/libjingle/source/talk/p2p/base/stunport.h
new file mode 100644
index 0000000..569e229
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/stunport.h
@@ -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.
+ */
+
+#ifndef TALK_P2P_BASE_STUNPORT_H_
+#define TALK_P2P_BASE_STUNPORT_H_
+
+#include <string>
+#include "talk/base/asyncudpsocket.h"
+#include "talk/p2p/base/udpport.h"
+#include "talk/p2p/base/stunrequest.h"
+
+namespace talk_base {
+class AsyncResolver;
+class SignalThread;
+}
+
+namespace cricket {
+
+extern const std::string STUN_PORT_TYPE;
+
+// Communicates using the address on the outside of a NAT.
+class StunPort : public Port {
+ public:
+ static StunPort* Create(talk_base::Thread* thread,
+ talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& local_addr,
+ const talk_base::SocketAddress& server_addr) {
+ StunPort* port = new StunPort(thread, factory, network, server_addr);
+ if (!port->Init(local_addr)) {
+ delete port;
+ port = NULL;
+ }
+ return port;
+ }
+ StunPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& server_addr);
+ bool Init(const talk_base::SocketAddress& local_addr);
+ virtual ~StunPort();
+
+ talk_base::SocketAddress GetLocalAddress() const {
+ return socket_->GetLocalAddress();
+ }
+
+ const talk_base::SocketAddress& server_addr() const { return server_addr_; }
+ void set_server_addr(const talk_base::SocketAddress& addr) {
+ server_addr_ = addr;
+ }
+
+ const talk_base::SocketAddress& server_addr2() const { return server_addr2_; }
+ void set_server_addr2(const talk_base::SocketAddress& addr) {
+ server_addr2_ = addr;
+ }
+
+ virtual void PrepareAddress();
+
+ // This will contact the secondary server and signal another candidate
+ // address for this port (which may be the same as the first address).
+ void PrepareSecondaryAddress();
+
+ virtual Connection* CreateConnection(const Candidate& address,
+ CandidateOrigin origin);
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ protected:
+ virtual int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload);
+
+ void OnReadPacket(
+ const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ private:
+ // DNS resolution of the STUN server.
+ void ResolveStunAddress();
+ void OnResolveResult(talk_base::SignalThread* thread);
+ // Sends STUN requests to the server.
+ void OnSendPacket(const void* data, size_t size, StunRequest* req);
+
+ talk_base::SocketAddress server_addr_;
+ talk_base::SocketAddress server_addr2_;
+ StunRequestManager requests_;
+ talk_base::AsyncPacketSocket* socket_;
+ int error_;
+ talk_base::AsyncResolver* resolver_;
+
+ friend class StunPortBindingRequest;
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_STUNPORT_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/stunrequest.cc b/third_party/libjingle/source/talk/p2p/base/stunrequest.cc
new file mode 100644
index 0000000..b93408d
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/stunrequest.cc
@@ -0,0 +1,200 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/stunrequest.h"
+
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+
+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(talk_base::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());
+ request->Construct();
+ 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.
+
+ talk_base::ByteBuffer buf(data, size);
+ StunMessage msg;
+ if (!msg.Read(&buf))
+ return false;
+
+ return CheckResponse(&msg);
+}
+
+StunRequest::StunRequest()
+ : count_(0), timeout_(false), manager_(0),
+ id_(talk_base::CreateRandomString(16)), msg_(new StunMessage()),
+ tstamp_(0) {
+ msg_->SetTransactionID(id_);
+}
+
+StunRequest::StunRequest(StunMessage* request)
+ : count_(0), timeout_(false), manager_(0),
+ id_(request->transaction_id()), msg_(request) {
+}
+
+StunRequest::~StunRequest() {
+ ASSERT(manager_ != NULL);
+ if (manager_) {
+ manager_->Remove(this);
+ manager_->thread_->Clear(this);
+ }
+ delete msg_;
+}
+
+void StunRequest::Construct() {
+ if (msg_->type() == 0) {
+ Prepare(msg_);
+ ASSERT(msg_->transaction_id() == id_);
+ ASSERT(msg_->type() != 0);
+ }
+}
+
+const StunMessageType StunRequest::type() {
+ ASSERT(msg_ != NULL);
+ return msg_->type();
+}
+
+void StunRequest::set_manager(StunRequestManager* manager) {
+ ASSERT(!manager_);
+ manager_ = manager;
+}
+
+void StunRequest::OnMessage(talk_base::Message* pmsg) {
+ ASSERT(manager_ != NULL);
+ ASSERT(pmsg->message_id == MSG_STUN_SEND);
+
+ if (timeout_) {
+ OnTimeout();
+ delete this;
+ return;
+ }
+
+ tstamp_ = talk_base::Time();
+
+ talk_base::ByteBuffer buf;
+ msg_->Write(&buf);
+ manager_->SignalSendPacket(buf.Data(), buf.Length(), this);
+
+ int delay = GetNextDelay();
+ manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL);
+}
+
+uint32 StunRequest::Elapsed() const {
+ return talk_base::TimeSince(tstamp_);
+}
+
+int StunRequest::GetNextDelay() {
+ int delay = DELAY_UNIT * talk_base::_min(1 << count_, DELAY_MAX_FACTOR);
+ count_ += 1;
+ if (count_ == MAX_SENDS)
+ timeout_ = true;
+ return delay;
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/stunrequest.h b/third_party/libjingle/source/talk/p2p/base/stunrequest.h
new file mode 100644
index 0000000..93fd044
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/stunrequest.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 TALK_P2P_BASE_STUNREQUEST_H_
+#define TALK_P2P_BASE_STUNREQUEST_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(talk_base::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::signal3<const void*, size_t, StunRequest*> SignalSendPacket;
+
+private:
+ typedef std::map<std::string, StunRequest*> RequestMap;
+
+ talk_base::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 talk_base::MessageHandler {
+public:
+ StunRequest();
+ StunRequest(StunMessage* request);
+ virtual ~StunRequest();
+
+ // Causes our wrapped StunMessage to be Prepared
+ void Construct();
+
+ // 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(talk_base::Message* pmsg);
+
+ // Time elapsed since last send (in ms)
+ uint32 Elapsed() const;
+
+protected:
+ int count_;
+ bool timeout_;
+
+ // Fills in a request object to be sent. Note that request's 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 // TALK_P2P_BASE_STUNREQUEST_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/tcpport.cc b/third_party/libjingle/source/talk/p2p/base/tcpport.cc
new file mode 100644
index 0000000..3dfb838
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/tcpport.cc
@@ -0,0 +1,256 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/common.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/common.h"
+
+namespace cricket {
+
+TCPPort::TCPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& address,
+ bool allow_listen)
+ : Port(thread, LOCAL_PORT_TYPE, factory, network), address_(address),
+ incoming_only_(address_.port() != 0), allow_listen_(allow_listen),
+ socket_(NULL), error_(0) {
+}
+
+bool TCPPort::Init() {
+ // We don't use CreatePacketSocket here since we're creating a listen socket.
+ // However we will treat failure to create or bind a TCP socket as fatal.
+ // This should never happen.
+ socket_ = factory_->CreateAsyncSocket(SOCK_STREAM);
+ if (!socket_) {
+ LOG_J(LS_ERROR, this) << "TCP socket creation failed.";
+ return false;
+ }
+ if (socket_->Bind(address_) < 0) {
+ LOG_J(LS_ERROR, this) << "TCP bind failed with error "
+ << socket_->GetError();
+ return false;
+ }
+ socket_->SignalReadEvent.connect(this, &TCPPort::OnAcceptEvent);
+ return true;
+}
+
+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 NULL;
+
+ // We can't accept TCP connections incoming on other ports
+ if (origin == ORIGIN_OTHER_PORT)
+ return NULL;
+
+ // Check if we are allowed to make outgoing TCP connections
+ if (incoming_only_ && (origin == ORIGIN_MESSAGE))
+ return NULL;
+
+ // We don't know how to act as an ssl server yet
+ if ((address.protocol() == "ssltcp") && (origin == ORIGIN_THIS_PORT))
+ return NULL;
+
+ TCPConnection* conn = NULL;
+ if (talk_base::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() {
+ if (!allow_listen_) {
+ LOG_J(LS_INFO, this) << "Not listening due to firewall restrictions.";
+ } else if (socket_->Listen(5) < 0) {
+ LOG_J(LS_WARNING, this) << "TCP listen failed with error "
+ << socket_->GetError();
+ }
+ // Note: We still add the address, since otherwise the remote side won't
+ // recognize our incoming TCP connections.
+ AddAddress(socket_->GetLocalAddress(), "tcp", true);
+}
+
+int TCPPort::SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload) {
+ talk_base::AsyncTCPSocket * socket = NULL;
+ if (TCPConnection * conn = static_cast<TCPConnection*>(GetConnection(addr))) {
+ socket = conn->socket();
+ } else {
+ socket = GetIncoming(addr);
+ }
+ if (!socket) {
+ LOG_J(LS_ERROR, this) << "Attempted to send to an unknown destination, "
+ << addr.ToString();
+ return -1; // TODO(juberti): Set error_
+ }
+
+ int sent = socket->Send(data, size);
+ if (sent < 0) {
+ error_ = socket->GetError();
+ LOG_J(LS_ERROR, this) << "TCP send of " << size
+ << " bytes failed with error " << error_;
+ }
+ return sent;
+}
+
+int TCPPort::SetOption(talk_base::Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int TCPPort::GetError() {
+ return error_;
+}
+
+void TCPPort::OnAcceptEvent(talk_base::AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+
+ Incoming incoming;
+ talk_base::AsyncSocket* newsocket = socket->Accept(&incoming.addr);
+ if (!newsocket) {
+ // TODO(juberti): Do something better like forwarding the error to the user.
+ LOG_J(LS_ERROR, this) << "TCP accept failed with error "
+ << socket_->GetError();
+ return;
+ }
+ incoming.socket = new talk_base::AsyncTCPSocket(newsocket);
+ incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket);
+
+ LOG_J(LS_VERBOSE, this) << "Accepted connection from "
+ << incoming.addr.ToString();
+ incoming_.push_back(incoming);
+
+ // Prime a read event in case data is waiting
+ newsocket->SignalReadEvent(newsocket);
+}
+
+talk_base::AsyncTCPSocket* TCPPort::GetIncoming(
+ const talk_base::SocketAddress& addr, bool remove) {
+ talk_base::AsyncTCPSocket* socket = NULL;
+ 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 talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ Port::OnReadPacket(data, size, remote_addr);
+}
+
+TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate,
+ talk_base::AsyncTCPSocket* socket)
+ : Connection(port, 0, candidate), socket_(socket), error_(0) {
+ bool outgoing = (socket_ == NULL);
+ if (outgoing) {
+ // TODO(juberti): Handle failures here (unlikely since TCP)
+ socket_ = static_cast<talk_base::AsyncTCPSocket*>(port->CreatePacketSocket(
+ (candidate.protocol() == "ssltcp") ? PROTO_SSLTCP : PROTO_TCP));
+ } else {
+ // Incoming connections should match the network address
+ ASSERT(socket_->GetLocalAddress().EqualIPs(port->address_));
+ }
+ socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket);
+ socket_->SignalClose.connect(this, &TCPConnection::OnClose);
+ if (outgoing) {
+ set_connected(false);
+ talk_base::SocketAddress local_address(port->address_.ip(), 0);
+ socket_->SignalConnect.connect(this, &TCPConnection::OnConnect);
+ socket_->Bind(local_address);
+ socket_->Connect(candidate.address());
+ LOG_J(LS_VERBOSE, this) << "Connecting from " << local_address.ToString()
+ << " to " << candidate.address().ToString();
+ }
+}
+
+TCPConnection::~TCPConnection() {
+ delete socket_;
+}
+
+int TCPConnection::Send(const void* data, size_t size) {
+ if (write_state() != STATE_WRITABLE) {
+ // TODO(bpm): Should STATE_WRITE_TIMEOUT return a non-blocking error?
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ int sent = socket_->Send(data, size);
+ if (sent < 0) {
+ error_ = socket_->GetError();
+ } else {
+ send_rate_tracker_.Update(sent);
+ }
+ return sent;
+}
+
+int TCPConnection::GetError() {
+ return error_;
+}
+
+void TCPConnection::OnConnect(talk_base::AsyncTCPSocket* socket) {
+ ASSERT(socket == socket_);
+ LOG_J(LS_VERBOSE, this) << "Connection established to "
+ << socket->GetRemoteAddress().ToString();
+ set_connected(true);
+}
+
+void TCPConnection::OnClose(talk_base::AsyncTCPSocket* socket, int error) {
+ ASSERT(socket == socket_);
+ LOG_J(LS_VERBOSE, this) << "Connection closed with error " << error;
+ set_connected(false);
+ set_write_state(STATE_WRITE_TIMEOUT);
+}
+
+void TCPConnection::OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ ASSERT(socket == socket_);
+ Connection::OnReadPacket(data, size);
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/tcpport.h b/third_party/libjingle/source/talk/p2p/base/tcpport.h
new file mode 100644
index 0000000..d4ce575
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/tcpport.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.
+ */
+
+#ifndef TALK_P2P_BASE_TCPPORT_H_
+#define TALK_P2P_BASE_TCPPORT_H_
+
+#include <string>
+#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:
+ static TCPPort* Create(talk_base::Thread* thread,
+ talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& local_addr,
+ bool allow_listen) {
+ TCPPort* port = new TCPPort(thread, factory, network, local_addr,
+ allow_listen);
+ if (!port->Init()) {
+ delete port;
+ port = NULL;
+ }
+ return port;
+ }
+ bool Init();
+ virtual ~TCPPort();
+
+ virtual Connection* CreateConnection(const Candidate& address,
+ CandidateOrigin origin);
+
+ virtual void PrepareAddress();
+
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ protected:
+ TCPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network, const talk_base::SocketAddress& address,
+ bool allow_listen);
+
+ // Handles sending using the local TCP socket.
+ virtual int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload);
+
+ // Creates TCPConnection for incoming sockets
+ void OnAcceptEvent(talk_base::AsyncSocket* socket);
+
+ private:
+ struct Incoming {
+ talk_base::SocketAddress addr;
+ talk_base::AsyncTCPSocket * socket;
+ };
+
+ talk_base::AsyncTCPSocket* GetIncoming(const talk_base::SocketAddress& addr,
+ bool remove = false);
+
+ // Receives packet signal from the local TCP Socket.
+ void OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ // Note: use this until Network ips are stable, then use network->ip
+ talk_base::SocketAddress address_;
+ bool incoming_only_;
+ bool allow_listen_;
+ talk_base::AsyncSocket* socket_;
+ int error_;
+ std::list<Incoming> incoming_;
+
+ friend class TCPConnection;
+};
+
+class TCPConnection : public Connection {
+ public:
+ // Connection is outgoing unless socket is specified
+ TCPConnection(TCPPort* port, const Candidate& candidate,
+ talk_base::AsyncTCPSocket* socket = 0);
+ virtual ~TCPConnection();
+
+ virtual int Send(const void* data, size_t size);
+ virtual int GetError();
+
+ talk_base::AsyncTCPSocket * socket() { return socket_; }
+
+ private:
+ void OnConnect(talk_base::AsyncTCPSocket* socket);
+ void OnClose(talk_base::AsyncTCPSocket* socket, int error);
+ void OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ talk_base::AsyncTCPSocket* socket_;
+ int error_;
+
+ friend class TCPPort;
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_TCPPORT_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/transport.cc b/third_party/libjingle/source/talk/p2p/base/transport.cc
new file mode 100644
index 0000000..6be6d6e
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/transport.cc
@@ -0,0 +1,443 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/transport.h"
+
+#include "talk/base/common.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/transportchannelimpl.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace cricket {
+
+struct ChannelParams {
+ ChannelParams() : channel(NULL), candidate(NULL) {}
+ explicit ChannelParams(const std::string& name)
+ : name(name), channel(NULL), candidate(NULL) {}
+ ChannelParams(const std::string& name,
+ const std::string& session_type)
+ : name(name), session_type(session_type),
+ channel(NULL), candidate(NULL) {}
+ explicit ChannelParams(cricket::Candidate* candidate) :
+ channel(NULL), candidate(candidate) {
+ name = candidate->name();
+ }
+
+ ~ChannelParams() {
+ delete candidate;
+ }
+
+ std::string name;
+ std::string session_type;
+ cricket::TransportChannelImpl* channel;
+ cricket::Candidate* candidate;
+};
+typedef talk_base::TypedMessageData<ChannelParams*> ChannelMessage;
+
+enum {
+ MSG_CREATECHANNEL = 1,
+ MSG_DESTROYCHANNEL = 2,
+ MSG_DESTROYALLCHANNELS = 3,
+ MSG_CONNECTCHANNELS = 4,
+ MSG_RESETCHANNELS = 5,
+ MSG_ONSIGNALINGREADY = 6,
+ MSG_ONREMOTECANDIDATE = 7,
+ MSG_READSTATE = 8,
+ MSG_WRITESTATE = 9,
+ MSG_REQUESTSIGNALING = 10,
+ MSG_ONCHANNELCANDIDATEREADY = 11,
+ MSG_CONNECTING = 12,
+};
+
+Transport::Transport(talk_base::Thread* worker_thread, const std::string& name,
+ PortAllocator* allocator)
+ : signaling_thread_(talk_base::Thread::Current()),
+ worker_thread_(worker_thread), name_(name), allocator_(allocator),
+ destroyed_(false), readable_(false), writable_(false),
+ connect_requested_(false), allow_local_ips_(false) {
+}
+
+Transport::~Transport() {
+ ASSERT(signaling_thread_->IsCurrent());
+ ASSERT(destroyed_);
+}
+
+TransportChannelImpl* Transport::CreateChannel(
+ const std::string& name, const std::string& session_type) {
+ ChannelParams params(name, session_type);
+ ChannelMessage msg(&params);
+ worker_thread()->Send(this, MSG_CREATECHANNEL, &msg);
+ return msg.data()->channel;
+}
+
+TransportChannelImpl* Transport::CreateChannel_w(
+ const std::string& name, const std::string& session_type) {
+ ASSERT(worker_thread()->IsCurrent());
+
+ TransportChannelImpl* impl = CreateTransportChannel(name, session_type);
+ impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState);
+ impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState);
+ impl->SignalRequestSignaling.connect(
+ this, &Transport::OnChannelRequestSignaling);
+ impl->SignalCandidateReady.connect(this, &Transport::OnChannelCandidateReady);
+
+ talk_base::CritScope cs(&crit_);
+ ASSERT(channels_.find(name) == channels_.end());
+ channels_[name] = impl;
+ destroyed_ = false;
+ if (connect_requested_) {
+ impl->Connect();
+ if (channels_.size() == 1) {
+ // If this is the first channel, then indicate that we have started
+ // connecting.
+ signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+ }
+ }
+ return impl;
+}
+
+TransportChannelImpl* Transport::GetChannel(const std::string& name) {
+ talk_base::CritScope cs(&crit_);
+ ChannelMap::iterator iter = channels_.find(name);
+ return (iter != channels_.end()) ? iter->second : NULL;
+}
+
+bool Transport::HasChannels() {
+ talk_base::CritScope cs(&crit_);
+ return !channels_.empty();
+}
+
+void Transport::DestroyChannel(const std::string& name) {
+ ChannelParams params(name);
+ ChannelMessage msg(&params);
+ worker_thread()->Send(this, MSG_DESTROYCHANNEL, &msg);
+}
+
+void Transport::DestroyChannel_w(const std::string& name) {
+ ASSERT(worker_thread()->IsCurrent());
+ TransportChannelImpl* impl = NULL;
+ {
+ talk_base::CritScope cs(&crit_);
+ ChannelMap::iterator iter = channels_.find(name);
+ ASSERT(iter != channels_.end());
+ impl = iter->second;
+ channels_.erase(iter);
+ }
+
+ if (connect_requested_ && channels_.empty()) {
+ // We're not longer attempting to connect.
+ signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+ }
+
+ if (impl)
+ DestroyTransportChannel(impl);
+}
+
+void Transport::ConnectChannels() {
+ ASSERT(signaling_thread()->IsCurrent());
+ worker_thread()->Post(this, MSG_CONNECTCHANNELS, NULL);
+}
+
+void Transport::ConnectChannels_w() {
+ ASSERT(worker_thread()->IsCurrent());
+ if (connect_requested_)
+ return;
+ connect_requested_ = true;
+ signaling_thread()->Post(
+ this, MSG_ONCHANNELCANDIDATEREADY, NULL);
+ CallChannels_w(&TransportChannelImpl::Connect);
+ if (!channels_.empty()) {
+ signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+ }
+}
+
+void Transport::OnConnecting_s() {
+ ASSERT(signaling_thread()->IsCurrent());
+ SignalConnecting(this);
+}
+
+void Transport::DestroyAllChannels() {
+ ASSERT(signaling_thread()->IsCurrent());
+ worker_thread()->Send(this, MSG_DESTROYALLCHANNELS, NULL);
+ destroyed_ = true;
+}
+
+void Transport::DestroyAllChannels_w() {
+ ASSERT(worker_thread()->IsCurrent());
+ std::vector<TransportChannelImpl*> impls;
+ {
+ talk_base::CritScope cs(&crit_);
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ impls.push_back(iter->second);
+ }
+ channels_.clear();
+ }
+
+ for (size_t i = 0; i < impls.size(); ++i)
+ DestroyTransportChannel(impls[i]);
+}
+
+void Transport::ResetChannels() {
+ ASSERT(signaling_thread()->IsCurrent());
+ worker_thread()->Post(this, MSG_RESETCHANNELS, NULL);
+}
+
+void Transport::ResetChannels_w() {
+ ASSERT(worker_thread()->IsCurrent());
+
+ // We are no longer attempting to connect
+ connect_requested_ = false;
+
+ // Clear out the old messages, they aren't relevant
+ talk_base::CritScope cs(&crit_);
+ ready_candidates_.clear();
+
+ // Reset all of the channels
+ CallChannels_w(&TransportChannelImpl::Reset);
+}
+
+void Transport::OnSignalingReady() {
+ ASSERT(signaling_thread()->IsCurrent());
+ worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL);
+
+ // Notify the subclass.
+ OnTransportSignalingReady();
+}
+
+void Transport::CallChannels_w(TransportChannelFunc func) {
+ ASSERT(worker_thread()->IsCurrent());
+ talk_base::CritScope cs(&crit_);
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ ((iter->second)->*func)();
+ }
+}
+
+void Transport::OnRemoteCandidates(const std::vector<Candidate>& candidates) {
+ for (std::vector<Candidate>::const_iterator iter = candidates.begin();
+ iter != candidates.end();
+ ++iter) {
+ OnRemoteCandidate(*iter);
+ }
+}
+
+void Transport::OnRemoteCandidate(const Candidate& candidate) {
+ ASSERT(signaling_thread()->IsCurrent());
+ ASSERT(HasChannel(candidate.name()));
+ // new candidate deleted when params is deleted
+ ChannelParams* params = new ChannelParams(new Candidate(candidate));
+ ChannelMessage* msg = new ChannelMessage(params);
+ worker_thread()->Post(this, MSG_ONREMOTECANDIDATE, msg);
+}
+
+void Transport::OnRemoteCandidate_w(const Candidate& candidate) {
+ ASSERT(worker_thread()->IsCurrent());
+ ChannelMap::iterator iter = channels_.find(candidate.name());
+ // It's ok for a channel to go away while this message is in transit.
+ if (iter != channels_.end()) {
+ iter->second->OnCandidate(candidate);
+ }
+}
+
+void Transport::OnChannelReadableState(TransportChannel* channel) {
+ ASSERT(worker_thread()->IsCurrent());
+ signaling_thread()->Post(this, MSG_READSTATE, NULL);
+}
+
+void Transport::OnChannelReadableState_s() {
+ ASSERT(signaling_thread()->IsCurrent());
+ bool readable = GetTransportState_s(true);
+ if (readable_ != readable) {
+ readable_ = readable;
+ SignalReadableState(this);
+ }
+}
+
+void Transport::OnChannelWritableState(TransportChannel* channel) {
+ ASSERT(worker_thread()->IsCurrent());
+ signaling_thread()->Post(this, MSG_WRITESTATE, NULL);
+}
+
+void Transport::OnChannelWritableState_s() {
+ ASSERT(signaling_thread()->IsCurrent());
+ bool writable = GetTransportState_s(false);
+ if (writable_ != writable) {
+ writable_ = writable;
+ SignalWritableState(this);
+ }
+}
+
+bool Transport::GetTransportState_s(bool read) {
+ ASSERT(signaling_thread()->IsCurrent());
+ bool result = false;
+ talk_base::CritScope cs(&crit_);
+ for (ChannelMap::iterator iter = channels_.begin();
+ iter != channels_.end();
+ ++iter) {
+ bool b = (read ? iter->second->readable() : iter->second->writable());
+ result = result || b;
+ }
+ return result;
+}
+
+void Transport::OnChannelRequestSignaling() {
+ ASSERT(worker_thread()->IsCurrent());
+ signaling_thread()->Post(this, MSG_REQUESTSIGNALING, NULL);
+}
+
+void Transport::OnChannelRequestSignaling_s() {
+ ASSERT(signaling_thread()->IsCurrent());
+ SignalRequestSignaling(this);
+}
+
+void Transport::OnChannelCandidateReady(TransportChannelImpl* channel,
+ const Candidate& candidate) {
+ ASSERT(worker_thread()->IsCurrent());
+ talk_base::CritScope cs(&crit_);
+ ready_candidates_.push_back(candidate);
+
+ // We hold any messages until the client lets us connect.
+ if (connect_requested_) {
+ signaling_thread()->Post(
+ this, MSG_ONCHANNELCANDIDATEREADY, NULL);
+ }
+}
+
+void Transport::OnChannelCandidateReady_s() {
+ ASSERT(signaling_thread()->IsCurrent());
+ ASSERT(connect_requested_);
+
+ std::vector<Candidate> candidates;
+ {
+ talk_base::CritScope cs(&crit_);
+ candidates.swap(ready_candidates_);
+ }
+
+ // we do the deleting of Candidate* here to keep the new above and
+ // delete below close to each other
+ if (!candidates.empty()) {
+ SignalCandidatesReady(this, candidates);
+ }
+}
+
+void Transport::OnMessage(talk_base::Message* msg) {
+ switch (msg->message_id) {
+ case MSG_CREATECHANNEL:
+ {
+ ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data();
+ params->channel = CreateChannel_w(params->name, params->session_type);
+ }
+ break;
+ case MSG_DESTROYCHANNEL:
+ {
+ ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data();
+ DestroyChannel_w(params->name);
+ }
+ break;
+ case MSG_CONNECTCHANNELS:
+ ConnectChannels_w();
+ break;
+ case MSG_RESETCHANNELS:
+ ResetChannels_w();
+ break;
+ case MSG_DESTROYALLCHANNELS:
+ DestroyAllChannels_w();
+ break;
+ case MSG_ONSIGNALINGREADY:
+ CallChannels_w(&TransportChannelImpl::OnSignalingReady);
+ break;
+ case MSG_ONREMOTECANDIDATE:
+ {
+ ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data();
+ OnRemoteCandidate_w(*(params->candidate));
+ delete params;
+ }
+ break;
+ case MSG_CONNECTING:
+ OnConnecting_s();
+ break;
+ case MSG_READSTATE:
+ OnChannelReadableState_s();
+ break;
+ case MSG_WRITESTATE:
+ OnChannelWritableState_s();
+ break;
+ case MSG_REQUESTSIGNALING:
+ OnChannelRequestSignaling_s();
+ break;
+ case MSG_ONCHANNELCANDIDATEREADY:
+ OnChannelCandidateReady_s();
+ break;
+ }
+}
+
+bool Transport::ParseAddress(const buzz::XmlElement* elem,
+ const buzz::QName& address_name,
+ const buzz::QName& port_name,
+ talk_base::SocketAddress* address,
+ ParseError* error) {
+ ASSERT(elem->HasAttr(address_name));
+ ASSERT(elem->HasAttr(port_name));
+
+ // Record the parts of the address.
+ address->SetIP(elem->Attr(address_name));
+ std::istringstream ist(elem->Attr(port_name));
+ int port;
+ ist >> port;
+ address->SetPort(port);
+
+ // No address zero.
+ if (address->IsAny()) {
+ return BadParse("candidate has address of zero", error);
+ }
+
+ // Always disallow addresses that refer to the local host.
+ if (address->IsLocalIP() && !allow_local_ips_)
+ return BadParse("candidate has local IP address", error);
+
+ // Disallow all ports below 1024, except for 80 and 443 on public addresses.
+ if (port < 1024) {
+ if ((port != 80) && (port != 443))
+ return BadParse(
+ "candidate has port below 1024, but not 80 or 443", error);
+ if (address->IsPrivateIP()) {
+ return BadParse(
+ "candidate has port of 80 or 443 with private IP address", error);
+ }
+ }
+
+ return true;
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/transport.h b/third_party/libjingle/source/talk/p2p/base/transport.h
new file mode 100644
index 0000000..afe84d6
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/transport.h
@@ -0,0 +1,265 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// A Transport manages a set of named channels of the same type.
+//
+// Subclasses choose the appropriate class to instantiate for each channel;
+// however, this base class keeps track of the channels by name, watches their
+// state changes (in order to update the manager's state), and forwards
+// requests to begin connecting or to reset to each of the channels.
+//
+// On Threading: Transport performs work on both the signaling and worker
+// threads. For subclasses, the rule is that all signaling related calls will
+// be made on the signaling thread and all channel related calls (including
+// signaling for a channel) will be made on the worker thread. When
+// information needs to be sent between the two threads, this class should do
+// the work (e.g., OnRemoteCandidate).
+//
+// Note: Subclasses must call DestroyChannels() in their own constructors.
+// It is not possible to do so here because the subclass constructor will
+// already have run.
+
+#ifndef TALK_P2P_BASE_TRANSPORT_H_
+#define TALK_P2P_BASE_TRANSPORT_H_
+
+#include <string>
+#include <map>
+#include <vector>
+#include "talk/base/criticalsection.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/constants.h"
+
+namespace talk_base {
+class Thread;
+}
+
+namespace buzz {
+class QName;
+class XmlElement;
+}
+
+namespace cricket {
+
+struct ParseError;
+class PortAllocator;
+class SessionManager;
+class Session;
+class TransportChannel;
+class TransportChannelImpl;
+
+typedef std::vector<buzz::XmlElement*> XmlElements;
+typedef std::vector<Candidate> Candidates;
+
+// Used to parse and serialize (write) transport candidates. For
+// convenience of old code, Transports will implement TransportParser.
+// Parse/Write seems better than Serialize/Deserialize or
+// Create/Translate.
+class TransportParser {
+ public:
+ // see comment in parsing.h about parsing and unparsing function
+ // signatures
+ virtual bool ParseCandidates(const buzz::XmlElement* elem,
+ Candidates* candidates,
+ ParseError* error) = 0;
+ virtual void WriteCandidates(const Candidates& candidates,
+ SignalingProtocol protocol,
+ XmlElements* candidate_elems) = 0;
+ virtual ~TransportParser() {}
+};
+
+class Transport : public talk_base::MessageHandler, public sigslot::has_slots<>,
+ public TransportParser {
+ public:
+ Transport(talk_base::Thread* worker_thread, const std::string& name,
+ PortAllocator* allocator);
+ virtual ~Transport();
+
+ // Returns the signaling thread. The app talks to Transport on this thread.
+ talk_base::Thread* signaling_thread() { return signaling_thread_; }
+ // Returns the worker thread. The actual networking is done on this thread.
+ talk_base::Thread* worker_thread() { return worker_thread_; }
+
+ // Returns the name of this transport.
+ const std::string& name() const { return name_; }
+
+ // Returns the port allocator object for this transport.
+ PortAllocator* port_allocator() { return allocator_; }
+
+ // Returns the readable and states of this manager. These bits are the ORs
+ // of the corresponding bits on the managed channels. Each time one of these
+ // states changes, a signal is raised.
+ bool readable() const { return readable_; }
+ bool writable() const { return writable_; }
+ sigslot::signal1<Transport*> SignalReadableState;
+ sigslot::signal1<Transport*> SignalWritableState;
+
+ // Returns whether the client has requested the channels to connect.
+ bool connect_requested() const { return connect_requested_; }
+
+ // Create, destroy, and lookup the channels of this type by their names.
+ TransportChannelImpl* CreateChannel(const std::string& name,
+ const std::string& session_type);
+ // Note: GetChannel may lead to race conditions, since the mutex is not held
+ // after the pointer is returned.
+ TransportChannelImpl* GetChannel(const std::string& name);
+ // Note: HasChannel does not lead to race conditions, unlike GetChannel.
+ bool HasChannel(const std::string& name) {
+ return (NULL != GetChannel(name));
+ }
+ bool HasChannels();
+ void DestroyChannel(const std::string& name);
+
+ // Tells all current and future channels to start connecting. When the first
+ // channel begins connecting, the following signal is raised.
+ void ConnectChannels();
+ sigslot::signal1<Transport*> SignalConnecting;
+
+ // Resets all of the channels back to their initial state. They are no
+ // longer connecting.
+ void ResetChannels();
+
+ // Destroys every channel created so far.
+ void DestroyAllChannels();
+
+ // Before any stanza is sent, the manager will request signaling. Once
+ // signaling is available, the client should call OnSignalingReady. Once
+ // this occurs, the transport (or its channels) can send any waiting stanzas.
+ // OnSignalingReady invokes OnTransportSignalingReady and then forwards this
+ // signal to each channel.
+ sigslot::signal1<Transport*> SignalRequestSignaling;
+ void OnSignalingReady();
+
+ // Handles sending of ready candidates and receiving of remote candidates.
+ sigslot::signal2<Transport*,
+ const std::vector<Candidate>&> SignalCandidatesReady;
+ void OnRemoteCandidates(const std::vector<Candidate>& candidates);
+
+ // A transport message has generated an transport-specific error. The
+ // stanza that caused the error is available in session_msg. If false is
+ // returned, the error is considered unrecoverable, and the session is
+ // terminated.
+ // TODO(pthatcher): Make OnTransportError take an abstract data type
+ // rather than an XmlElement. It isn't needed yet, but it might be
+ // later for Jingle compliance.
+ virtual void OnTransportError(const buzz::XmlElement* error) {}
+ sigslot::signal6<Transport*, const buzz::XmlElement*, const buzz::QName&,
+ const std::string&, const std::string&,
+ const buzz::XmlElement*>
+ SignalTransportError;
+
+ sigslot::signal2<Transport*, const std::string&> SignalChannelGone;
+
+ // (For testing purposes only.) This indicates whether we will allow local
+ // IPs (e.g. 127.*) to be used as addresses for P2P.
+ bool allow_local_ips() const { return allow_local_ips_; }
+ void set_allow_local_ips(bool value) { allow_local_ips_ = value; }
+
+ protected:
+ // These are called by Create/DestroyChannel above in order to create or
+ // destroy the appropriate type of channel.
+ virtual TransportChannelImpl* CreateTransportChannel(
+ const std::string& name, const std::string &session_type) = 0;
+ virtual void DestroyTransportChannel(TransportChannelImpl* channel) = 0;
+
+ // Informs the subclass that we received the signaling ready message.
+ virtual void OnTransportSignalingReady() {}
+
+ // Helper function to parse an element describing an address. This
+ // retrieves the IP and port from the given element and verifies
+ // that they look like plausible values.
+ bool ParseAddress(const buzz::XmlElement* elem,
+ const buzz::QName& address_name,
+ const buzz::QName& port_name,
+ talk_base::SocketAddress* address,
+ ParseError* error);
+ private:
+ typedef std::map<std::string, TransportChannelImpl*> ChannelMap;
+
+ // Called when the state of a channel changes.
+ void OnChannelReadableState(TransportChannel* channel);
+ void OnChannelWritableState(TransportChannel* channel);
+
+ // Called when a channel requests signaling.
+ void OnChannelRequestSignaling();
+
+ // Called when a candidate is ready from remote peer.
+ void OnRemoteCandidate(const Candidate& candidate);
+ // Called when a candidate is ready from channel.
+ void OnChannelCandidateReady(TransportChannelImpl* channel,
+ const Candidate& candidate);
+
+ // Dispatches messages to the appropriate handler (below).
+ void OnMessage(talk_base::Message* msg);
+
+ // These are versions of the above methods that are called only on a
+ // particular thread (s = signaling, w = worker). The above methods post or
+ // send a message to invoke this version.
+ TransportChannelImpl* CreateChannel_w(const std::string& name,
+ const std::string& session_type);
+ void DestroyChannel_w(const std::string& name);
+ void ConnectChannels_w();
+ void ResetChannels_w();
+ void DestroyAllChannels_w();
+ void OnRemoteCandidate_w(const Candidate& candidate);
+ void OnChannelReadableState_s();
+ void OnChannelWritableState_s();
+ void OnChannelRequestSignaling_s();
+ void OnConnecting_s();
+
+ // Helper function that invokes the given function on every channel.
+ typedef void (TransportChannelImpl::* TransportChannelFunc)();
+ void CallChannels_w(TransportChannelFunc func);
+
+ // Computes the OR of the channel's read or write state (argument picks).
+ bool GetTransportState_s(bool read);
+
+ void OnChannelCandidateReady_s();
+
+ talk_base::Thread* signaling_thread_;
+ talk_base::Thread* worker_thread_;
+ std::string name_;
+ PortAllocator* allocator_;
+ bool destroyed_;
+ bool readable_;
+ bool writable_;
+ bool connect_requested_;
+ ChannelMap channels_;
+ // Buffers the ready_candidates so that SignalCanidatesReady can
+ // provide them in multiples.
+ std::vector<Candidate> ready_candidates_;
+ // Protects changes to channels and messages
+ talk_base::CriticalSection crit_;
+ bool allow_local_ips_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Transport);
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_TRANSPORT_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/transportchannel.cc b/third_party/libjingle/source/talk/p2p/base/transportchannel.cc
new file mode 100644
index 0000000..ba076ac
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/transportchannel.cc
@@ -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.
+ */
+
+#include <sstream>
+#include "talk/p2p/base/transportchannel.h"
+
+namespace cricket {
+
+std::string TransportChannel::ToString() const {
+ const char READABLE_ABBREV[2] = { '_', 'R' };
+ const char WRITABLE_ABBREV[2] = { '_', 'W' };
+ std::stringstream ss;
+ ss << "Channel[" << name_ << "|" << READABLE_ABBREV[readable_]
+ << WRITABLE_ABBREV[writable_] << "]";
+ return ss.str();
+}
+
+void TransportChannel::set_readable(bool readable) {
+ if (readable_ != readable) {
+ readable_ = readable;
+ SignalReadableState(this);
+ }
+}
+
+void TransportChannel::set_writable(bool writable) {
+ if (writable_ != writable) {
+ writable_ = writable;
+ SignalWritableState(this);
+ }
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/transportchannel.h b/third_party/libjingle/source/talk/p2p/base/transportchannel.h
new file mode 100644
index 0000000..8196847
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/transportchannel.h
@@ -0,0 +1,110 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_TRANSPORTCHANNEL_H_
+#define TALK_P2P_BASE_TRANSPORTCHANNEL_H_
+
+#include <string>
+#include "talk/base/basictypes.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socket.h"
+
+namespace cricket {
+
+class P2PTransportChannel;
+
+// A TransportChannel represents one logical stream of packets that are sent
+// between the two sides of a session.
+class TransportChannel: public sigslot::has_slots<> {
+ public:
+ TransportChannel(const std::string& name, const std::string &session_type)
+ : name_(name), session_type_(session_type),
+ readable_(false), writable_(false) {}
+ virtual ~TransportChannel() {}
+
+ // Returns the name of this channel.
+ const std::string& name() const { return name_; }
+ const std::string& session_type() const { return session_type_; }
+
+ // Returns the readable and states of this channel. Each time one of these
+ // states changes, a signal is raised. These states are aggregated by the
+ // TransportManager.
+ bool readable() const { return readable_; }
+ bool writable() const { return writable_; }
+ sigslot::signal1<TransportChannel*> SignalReadableState;
+ sigslot::signal1<TransportChannel*> SignalWritableState;
+
+ // Attempts to send the given packet. The return value is < 0 on failure.
+ virtual int SendPacket(const char *data, size_t len) = 0;
+
+ // Sets a socket option on this channel. Note that not all options are
+ // supported by all transport types.
+ virtual int SetOption(talk_base::Socket::Option opt, int value) = 0;
+
+ // Returns the most recent error that occurred on this channel.
+ virtual int GetError() = 0;
+
+ // This hack is here to allow the SocketMonitor to downcast to the
+ // P2PTransportChannel safely.
+ // TODO(juberti): Generalize network monitoring.
+ virtual P2PTransportChannel* GetP2PChannel() { return NULL; }
+
+ // Signalled each time a packet is received on this channel.
+ sigslot::signal3<TransportChannel*, const char*, size_t> SignalReadPacket;
+
+ // This signal occurs when there is a change in the way that packets are
+ // being routed. The address indicates the address of the first hop in the
+ // new route, if this is known. If this cannot be determined or is not well-
+ // defined, then the channel may give an address of 0.
+ sigslot::signal2<TransportChannel*, const talk_base::SocketAddress&>
+ SignalRouteChange;
+
+ // Invoked when the channel is being destroyed.
+ sigslot::signal1<TransportChannel*> SignalDestroyed;
+
+ // Debugging description of this transport channel.
+ std::string ToString() const;
+
+ protected:
+ // Sets the readable state, signaling if necessary.
+ void set_readable(bool readable);
+
+ // Sets the writable state, signaling if necessary.
+ void set_writable(bool writable);
+
+ private:
+ std::string name_;
+ std::string session_type_;
+ bool readable_;
+ bool writable_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TransportChannel);
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_TRANSPORTCHANNEL_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h b/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h
new file mode 100644
index 0000000..a84b4bf
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.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 TALK_P2P_BASE_TRANSPORTCHANNELIMPL_H_
+#define TALK_P2P_BASE_TRANSPORTCHANNELIMPL_H_
+
+#include <string>
+#include "talk/p2p/base/transportchannel.h"
+
+namespace buzz { class XmlElement; }
+
+namespace cricket {
+
+class Transport;
+class Candidate;
+
+// Base class for real implementations of TransportChannel. This includes some
+// methods called only by Transport, which do not need to be exposed to the
+// client.
+class TransportChannelImpl : public TransportChannel {
+ public:
+ TransportChannelImpl(const std::string& name, const std::string& session_type)
+ : TransportChannel(name, session_type) {}
+
+ // Returns the transport that created this channel.
+ virtual Transport* GetTransport() = 0;
+
+ // Begins the process of attempting to make a connection to the other client.
+ virtual void Connect() = 0;
+
+ // Resets this channel back to the initial state (i.e., not connecting).
+ virtual void Reset() = 0;
+
+ // Allows an individual channel to request signaling and be notified when it
+ // is ready. This is useful if the individual named channels have need to
+ // send their own transport-info stanzas.
+ sigslot::signal0<> SignalRequestSignaling;
+ virtual void OnSignalingReady() = 0;
+
+ // Handles sending and receiving of candidates. The Transport
+ // receives the candidates and may forward them to the relevant
+ // channel.
+ //
+ // Note: Since candidates are delivered asynchronously to the
+ // channel, they cannot return an error if the message is invalid.
+ // It is assumed that the Transport will have checked validity
+ // before forwarding.
+ sigslot::signal2<TransportChannelImpl*,
+ const Candidate&> SignalCandidateReady;
+ virtual void OnCandidate(const Candidate& candidate) = 0;
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TransportChannelImpl);
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_TRANSPORTCHANNELIMPL_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc b/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc
new file mode 100644
index 0000000..e2d11ed
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc
@@ -0,0 +1,100 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/transportchannelproxy.h"
+#include "talk/base/common.h"
+#include "talk/p2p/base/transport.h"
+#include "talk/p2p/base/transportchannelimpl.h"
+
+namespace cricket {
+
+TransportChannelProxy::TransportChannelProxy(const std::string& name,
+ const std::string &session_type)
+ : TransportChannel(name, session_type), impl_(NULL) {
+}
+
+TransportChannelProxy::~TransportChannelProxy() {
+ if (impl_)
+ impl_->GetTransport()->DestroyChannel(impl_->name());
+}
+
+void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) {
+ impl_ = impl;
+ impl_->SignalReadableState.connect(
+ this, &TransportChannelProxy::OnReadableState);
+ impl_->SignalWritableState.connect(
+ this, &TransportChannelProxy::OnWritableState);
+ impl_->SignalReadPacket.connect(this, &TransportChannelProxy::OnReadPacket);
+ impl_->SignalRouteChange.connect(this, &TransportChannelProxy::OnRouteChange);
+ for (OptionList::iterator it = pending_options_.begin();
+ it != pending_options_.end();
+ ++it) {
+ impl_->SetOption(it->first, it->second);
+ }
+ pending_options_.clear();
+}
+
+int TransportChannelProxy::SendPacket(const char *data, size_t len) {
+ // Fail if we don't have an impl yet.
+ return (impl_) ? impl_->SendPacket(data, len) : -1;
+}
+
+int TransportChannelProxy::SetOption(talk_base::Socket::Option opt, int value) {
+ if (impl_)
+ return impl_->SetOption(opt, value);
+ pending_options_.push_back(OptionPair(opt, value));
+ return 0;
+}
+
+int TransportChannelProxy::GetError() {
+ ASSERT(impl_ != NULL); // should not be used until channel is writable
+ return impl_->GetError();
+}
+
+void TransportChannelProxy::OnReadableState(TransportChannel* channel) {
+ ASSERT(channel == impl_);
+ set_readable(impl_->readable());
+}
+
+void TransportChannelProxy::OnWritableState(TransportChannel* channel) {
+ ASSERT(channel == impl_);
+ set_writable(impl_->writable());
+}
+
+void TransportChannelProxy::OnReadPacket(TransportChannel* channel,
+ const char* data, size_t size) {
+ ASSERT(channel == impl_);
+ SignalReadPacket(this, data, size);
+}
+
+void TransportChannelProxy::OnRouteChange(TransportChannel* channel,
+ const talk_base::SocketAddress& address) {
+ ASSERT(channel == impl_);
+ SignalRouteChange(this, address);
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h b/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h
new file mode 100644
index 0000000..46d8a44
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h
@@ -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.
+ */
+
+#ifndef _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_
+#define _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_
+
+#include <string>
+#include <vector>
+#include "talk/p2p/base/transportchannel.h"
+
+namespace cricket {
+
+class TransportChannelImpl;
+
+// Proxies calls between the client and the transport channel implementation.
+// This is needed because clients are allowed to create channels before the
+// network negotiation is complete. Hence, we create a proxy up front, and
+// when negotiation completes, connect the proxy to the implementaiton.
+class TransportChannelProxy: public TransportChannel {
+ public:
+ TransportChannelProxy(const std::string& name, const std::string &session_type);
+ virtual ~TransportChannelProxy();
+
+ TransportChannelImpl* impl() const { return impl_; }
+
+ // Sets the implementation to which we will proxy.
+ void SetImplementation(TransportChannelImpl* impl);
+
+ // Implementation of the TransportChannel interface. These simply forward to
+ // the implementation.
+ virtual int SendPacket(const char *data, size_t len);
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ private:
+ typedef std::pair<talk_base::Socket::Option, int> OptionPair;
+ typedef std::vector<OptionPair> OptionList;
+ TransportChannelImpl* impl_;
+ OptionList pending_options_;
+
+ // Catch signals from the implementation channel. These just forward to the
+ // client (after updating our state to match).
+ void OnReadableState(TransportChannel* channel);
+ void OnWritableState(TransportChannel* channel);
+ void OnReadPacket(TransportChannel* channel, const char* data, size_t size);
+ void OnRouteChange(TransportChannel* channel, const talk_base::SocketAddress& address);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TransportChannelProxy);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_
diff --git a/third_party/libjingle/source/talk/p2p/base/udpport.cc b/third_party/libjingle/source/talk/p2p/base/udpport.cc
new file mode 100644
index 0000000..a34c17e
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/udpport.cc
@@ -0,0 +1,106 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/udpport.h"
+
+#include "talk/base/logging.h"
+#include "talk/p2p/base/common.h"
+
+namespace cricket {
+
+const std::string LOCAL_PORT_TYPE("local");
+
+UDPPort::UDPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network)
+ : Port(thread, LOCAL_PORT_TYPE, factory, network),
+ socket_(NULL), error_(0) {
+}
+
+bool UDPPort::Init(const talk_base::SocketAddress& local_addr) {
+ socket_ = CreatePacketSocket(PROTO_UDP);
+ if (!socket_) {
+ LOG_J(LS_WARNING, this) << "UDP socket creation failed";
+ return false;
+ }
+ if (socket_->Bind(local_addr) < 0) {
+ LOG_J(LS_WARNING, this) << "UDP bind failed with error "
+ << socket_->GetError();
+ return false;
+ }
+ socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket);
+ return true;
+}
+
+UDPPort::~UDPPort() {
+ delete socket_;
+}
+
+void UDPPort::PrepareAddress() {
+ AddAddress(socket_->GetLocalAddress(), "udp", true);
+}
+
+Connection* UDPPort::CreateConnection(const Candidate& address,
+ CandidateOrigin origin) {
+ if (address.protocol() != "udp")
+ return NULL;
+
+ Connection* conn = new ProxyConnection(this, 0, address);
+ AddConnection(conn);
+ return conn;
+}
+
+int UDPPort::SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& addr, bool payload) {
+ int sent = socket_->SendTo(data, size, addr);
+ if (sent < 0) {
+ error_ = socket_->GetError();
+ LOG_J(LS_ERROR, this) << "UDP send of " << size
+ << " bytes failed with error " << error_;
+ }
+ return sent;
+}
+
+int UDPPort::SetOption(talk_base::Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int UDPPort::GetError() {
+ return error_;
+}
+
+void UDPPort::OnReadPacket(
+ const char* data, size_t size, const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket) {
+ ASSERT(socket == socket_);
+ if (Connection* conn = GetConnection(remote_addr)) {
+ conn->OnReadPacket(data, size);
+ } else {
+ Port::OnReadPacket(data, size, remote_addr);
+ }
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/base/udpport.h b/third_party/libjingle/source/talk/p2p/base/udpport.h
new file mode 100644
index 0000000..7fcad6c
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/base/udpport.h
@@ -0,0 +1,89 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_BASE_UDPPORT_H_
+#define TALK_P2P_BASE_UDPPORT_H_
+
+#include <string>
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/p2p/base/port.h"
+
+namespace talk_base {
+class Thread;
+class Network;
+class SocketAddress;
+}
+
+namespace cricket {
+
+extern const std::string LOCAL_PORT_TYPE; // type of UDP ports
+
+// Communicates using a local UDP port.
+class UDPPort : public Port {
+ public:
+ static UDPPort* Create(talk_base::Thread* thread,
+ talk_base::SocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::SocketAddress& local_addr) {
+ UDPPort* port = new UDPPort(thread, factory, network);
+ if (!port->Init(local_addr)) {
+ delete port;
+ port = NULL;
+ }
+ return port;
+ }
+ UDPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory,
+ talk_base::Network* network);
+ bool Init(const talk_base::SocketAddress& local_addr);
+ virtual ~UDPPort();
+
+ virtual void PrepareAddress();
+ virtual Connection* CreateConnection(const Candidate& address,
+ CandidateOrigin origin);
+
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ protected:
+ // Handles sending using the local UDP socket.
+ virtual int SendTo(const void* data, size_t size,
+ const talk_base::SocketAddress& remote_addr, bool payload);
+
+ // Dispatches the given packet to the port or connection as appropriate.
+ void OnReadPacket(const char* data, size_t size,
+ const talk_base::SocketAddress& remote_addr,
+ talk_base::AsyncPacketSocket* socket);
+
+ private:
+ talk_base::AsyncPacketSocket* socket_;
+ int error_;
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_UDPPORT_H_
diff --git a/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc b/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc
new file mode 100644
index 0000000..0a5e163
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc
@@ -0,0 +1,784 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/base/common.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/p2p/base/tcpport.h"
+#include "talk/p2p/base/udpport.h"
+
+using talk_base::CreateRandomId;
+using talk_base::CreateRandomString;
+
+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;
+
+// Modifiers of the above constants
+const float RELAY_PRIMARY_PREF_MODIFIER = 0.0f;
+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 + CreateRandomId() % range;
+}
+
+} // namespace
+
+namespace cricket {
+
+const uint32 DISABLE_ALL_PHASES =
+ PORTALLOCATOR_DISABLE_UDP
+ | PORTALLOCATOR_DISABLE_TCP
+ | PORTALLOCATOR_DISABLE_STUN
+ | PORTALLOCATOR_DISABLE_RELAY;
+
+// Performs the allocation of ports, in a sequenced (timed) manner, for a given
+// network and IP address.
+class AllocationSequence : public talk_base::MessageHandler {
+ public:
+ AllocationSequence(BasicPortAllocatorSession* session,
+ talk_base::Network* network,
+ PortConfiguration* config,
+ uint32 flags);
+ ~AllocationSequence();
+
+ // Disables the phases for a new sequence that this one already covers for an
+ // equivalent network setup.
+ void DisableEquivalentPhases(talk_base::Network* network,
+ PortConfiguration* config, uint32* flags);
+
+ // 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(talk_base::Message* msg);
+
+ void EnableProtocol(ProtocolType proto);
+ bool ProtocolEnabled(ProtocolType proto) const;
+
+ private:
+ typedef std::vector<ProtocolType> ProtocolList;
+
+ void CreateUDPPorts();
+ void CreateTCPPorts();
+ void CreateStunPorts();
+ void CreateRelayPorts();
+
+ BasicPortAllocatorSession* session_;
+ talk_base::Network* network_;
+ uint32 ip_;
+ PortConfiguration* config_;
+ bool running_;
+ int step_;
+ int step_of_phase_[kNumPhases];
+ uint32 flags_;
+ ProtocolList protocols_;
+};
+
+
+// BasicPortAllocator
+
+BasicPortAllocator::BasicPortAllocator(
+ talk_base::NetworkManager* network_manager)
+ : network_manager_(network_manager), best_writable_phase_(-1) {
+}
+
+BasicPortAllocator::BasicPortAllocator(
+ talk_base::NetworkManager* network_manager,
+ const talk_base::SocketAddress& stun_address,
+ const talk_base::SocketAddress& relay_address_udp,
+ const talk_base::SocketAddress& relay_address_tcp,
+ const talk_base::SocketAddress& relay_address_ssl)
+ : network_manager_(network_manager),
+ stun_address_(stun_address),
+ relay_address_udp_(relay_address_udp),
+ relay_address_tcp_(relay_address_tcp),
+ relay_address_ssl_(relay_address_ssl),
+ best_writable_phase_(-1),
+ allow_tcp_listen_(true) {
+}
+
+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 == talk_base::PROXY_HTTPS)
+ || (proxy().type == talk_base::PROXY_UNKNOWN))) {
+ return PHASE_RELAY;
+ }
+ return best_writable_phase_;
+}
+
+PortAllocatorSession *BasicPortAllocator::CreateSession(
+ const std::string &name, const std::string &session_type) {
+ return new BasicPortAllocatorSession(this, name, session_type);
+}
+
+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,
+ const std::string &session_type)
+ : PortAllocatorSession(allocator->flags()), allocator_(allocator),
+ name_(name), session_type_(session_type), network_thread_(NULL),
+ allocation_started_(false), running_(false) {
+}
+
+BasicPortAllocatorSession::~BasicPortAllocatorSession() {
+ 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_ = talk_base::Thread::Current();
+
+ network_thread_->Post(this, MSG_CONFIG_START);
+
+ if (flags() & PORTALLOCATOR_ENABLE_SHAKER)
+ network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE);
+}
+
+void BasicPortAllocatorSession::StartGetAllPorts() {
+ ASSERT(talk_base::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(talk_base::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(talk_base::Message *message) {
+ switch (message->message_id) {
+ case MSG_CONFIG_START:
+ ASSERT(talk_base::Thread::Current() == network_thread_);
+ GetPortConfigurations();
+ break;
+
+ case MSG_CONFIG_READY:
+ ASSERT(talk_base::Thread::Current() == network_thread_);
+ OnConfigReady(static_cast<PortConfiguration*>(message->pdata));
+ break;
+
+ case MSG_ALLOCATE:
+ ASSERT(talk_base::Thread::Current() == network_thread_);
+ OnAllocate();
+ break;
+
+ case MSG_SHAKE:
+ ASSERT(talk_base::Thread::Current() == network_thread_);
+ OnShake();
+ break;
+
+ default:
+ ASSERT(false);
+ }
+}
+
+void BasicPortAllocatorSession::GetPortConfigurations() {
+ PortConfiguration* config = new PortConfiguration(allocator_->stun_address(),
+ CreateRandomString(16),
+ CreateRandomString(16),
+ "");
+ PortConfiguration::PortList ports;
+ if (!allocator_->relay_address_udp().IsAny())
+ ports.push_back(ProtocolAddress(
+ allocator_->relay_address_udp(), PROTO_UDP));
+ if (!allocator_->relay_address_tcp().IsAny())
+ ports.push_back(ProtocolAddress(
+ allocator_->relay_address_tcp(), PROTO_TCP));
+ if (!allocator_->relay_address_ssl().IsAny())
+ ports.push_back(ProtocolAddress(
+ allocator_->relay_address_ssl(), PROTO_SSLTCP));
+ 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(talk_base::Thread::Current() == network_thread_);
+ 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<talk_base::Network*> networks;
+
+ if (!allocator_->network_manager()->GetNetworks(&networks)) {
+ LOG(LS_ERROR) << "Failed to enumerate networks";
+ } else if (networks.empty()) {
+ LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated";
+ } else {
+ for (uint32 i = 0; i < networks.size(); ++i) {
+ PortConfiguration* config = NULL;
+ if (configs_.size() > 0)
+ config = configs_.back();
+
+ uint32 sequence_flags = flags();
+
+ // Disables phases that are not specified in this config.
+ if (!config || config->stun_address.IsNil()) {
+ // No STUN ports specified in this config.
+ sequence_flags |= PORTALLOCATOR_DISABLE_STUN;
+ }
+ if (!config || config->relays.empty()) {
+ // No relay ports specified in this config.
+ sequence_flags |= PORTALLOCATOR_DISABLE_RELAY;
+ }
+
+ // Disable phases that would only create ports equivalent to ones that we
+ // have already made.
+ DisableEquivalentPhases(networks[i], config, &sequence_flags);
+
+ if ((sequence_flags & DISABLE_ALL_PHASES) == DISABLE_ALL_PHASES) {
+ // New AllocationSequence would have nothing to do, so don't make it.
+ continue;
+ }
+
+ AllocationSequence* sequence =
+ new AllocationSequence(this, networks[i], config, sequence_flags);
+ if (running_)
+ sequence->Start();
+
+ sequences_.push_back(sequence);
+ }
+ }
+
+ allocation_started_ = true;
+ if (running_)
+ network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+}
+
+void BasicPortAllocatorSession::DisableEquivalentPhases(
+ talk_base::Network* network, PortConfiguration* config, uint32* flags) {
+ for (uint32 i = 0; i < sequences_.size() &&
+ (*flags & DISABLE_ALL_PHASES) != DISABLE_ALL_PHASES; ++i) {
+ sequences_[i]->DisableEquivalentPhases(network, config, flags);
+ }
+}
+
+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());
+ if (allocator_->proxy().type != talk_base::PROXY_NONE)
+ port->set_proxy(allocator_->user_agent(), allocator_->proxy());
+
+ 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);
+ LOG_J(LS_INFO, port) << "Added port to allocator";
+
+ if (prepare_address)
+ port->PrepareAddress();
+ if (running_)
+ port->Start();
+}
+
+void BasicPortAllocatorSession::OnAddressReady(Port *port) {
+ ASSERT(talk_base::Thread::Current() == network_thread_);
+ std::vector<PortData>::iterator it
+ = std::find(ports_.begin(), ports_.end(), port);
+ ASSERT(it != ports_.end());
+ if (it->ready)
+ return;
+ 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(talk_base::Thread::Current() == network_thread_);
+ std::vector<PortData>::iterator iter =
+ std::find(ports_.begin(), ports_.end(), port);
+ ASSERT(iter != ports_.end());
+ ports_.erase(iter);
+
+ LOG_J(LS_INFO, port) << "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 " << ports.size() << " ports and "
+ << 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,
+ talk_base::Network* network,
+ PortConfiguration* config,
+ uint32 flags)
+ : session_(session), network_(network), ip_(network->ip()), config_(config),
+ running_(false), step_(0), flags_(flags) {
+ // 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 =
+ talk_base::_max(0, session->allocator()->best_writable_phase());
+ for (int phase = 0; phase < kNumPhases; ++phase)
+ step_of_phase_[phase] = talk_base::_max(0, phase - last_phase_in_step_zero);
+
+ // Immediately perform phase 0.
+ OnMessage(NULL);
+}
+
+AllocationSequence::~AllocationSequence() {
+ session_->network_thread()->Clear(this);
+}
+
+void AllocationSequence::DisableEquivalentPhases(talk_base::Network* network,
+ PortConfiguration* config, uint32* flags) {
+ if (!((network == network_) && (ip_ == network->ip()))) {
+ // Different network setup; nothing is equivalent.
+ return;
+ }
+
+ // Else turn off the stuff that we've already got covered.
+
+ // Every config implicitly specifies local, so turn that off right away.
+ *flags |= PORTALLOCATOR_DISABLE_UDP;
+ *flags |= PORTALLOCATOR_DISABLE_TCP;
+
+ if (config_ && config) {
+ if (config_->stun_address == config->stun_address) {
+ // Already got this STUN server covered.
+ *flags |= PORTALLOCATOR_DISABLE_STUN;
+ }
+ if (!config_->relays.empty()) {
+ // Already got relays covered.
+ // NOTE: This will even skip a _different_ set of relay servers if we
+ // were to be given one, but that never happens in our codebase. Should
+ // probably get rid of the list in PortConfiguration and just keep a
+ // single relay server in each one.
+ *flags |= PORTALLOCATOR_DISABLE_RELAY;
+ }
+ }
+}
+
+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(talk_base::Message* msg) {
+ ASSERT(talk_base::Thread::Current() == session_->network_thread());
+ if (msg)
+ ASSERT(msg->message_id == MSG_ALLOCATION_PHASE);
+
+ const char* const PHASE_NAMES[kNumPhases] = {
+ "Udp", "Relay", "Tcp", "SslTcp"
+ };
+
+ // Perform all of the phases in the current step.
+ for (int phase = 0; phase < kNumPhases; phase++) {
+ if (step_of_phase_[phase] != step_)
+ continue;
+
+ LOG_J(LS_INFO, network_) << "Allocation Phase=" << PHASE_NAMES[phase]
+ << " (Step=" << step_ << ")";
+
+ switch (phase) {
+ case PHASE_UDP:
+ CreateUDPPorts();
+ CreateStunPorts();
+ EnableProtocol(PROTO_UDP);
+ break;
+
+ case PHASE_RELAY:
+ CreateRelayPorts();
+ break;
+
+ case PHASE_TCP:
+ CreateTCPPorts();
+ EnableProtocol(PROTO_TCP);
+ break;
+
+ case PHASE_SSLTCP:
+ EnableProtocol(PROTO_SSLTCP);
+ break;
+
+ default:
+ ASSERT(false);
+ }
+ }
+
+ // TODO(juberti): 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 (flags_ & PORTALLOCATOR_DISABLE_UDP) {
+ LOG(LS_VERBOSE) << "AllocationSequence: UDP ports disabled, skipping.";
+ return;
+ }
+
+ Port* port = UDPPort::Create(session_->network_thread(), NULL, network_,
+ talk_base::SocketAddress(ip_, 0));
+ if (port)
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP);
+}
+
+void AllocationSequence::CreateTCPPorts() {
+ if (flags_ & PORTALLOCATOR_DISABLE_TCP) {
+ LOG(LS_VERBOSE) << "AllocationSequence: TCP ports disabled, skipping.";
+ return;
+ }
+
+ Port* port = TCPPort::Create(session_->network_thread(), NULL, network_,
+ talk_base::SocketAddress(ip_, 0),
+ session_->allocator()->allow_tcp_listen());
+ if (port)
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP);
+}
+
+void AllocationSequence::CreateStunPorts() {
+ if (flags_ & PORTALLOCATOR_DISABLE_STUN) {
+ LOG(LS_VERBOSE) << "AllocationSequence: STUN ports disabled, skipping.";
+ return;
+ }
+
+ // If BasicPortAllocatorSession::OnAllocate left STUN ports enabled then we
+ // ought to have an address for them here.
+ ASSERT(config_ && !config_->stun_address.IsNil());
+ if (!(config_ && !config_->stun_address.IsNil())) {
+ LOG(LS_WARNING)
+ << "AllocationSequence: No STUN server configured, skipping.";
+ return;
+ }
+
+ Port* port = StunPort::Create(session_->network_thread(), NULL, network_,
+ talk_base::SocketAddress(ip_, 0),
+ config_->stun_address);
+ if (port)
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN);
+}
+
+void AllocationSequence::CreateRelayPorts() {
+ if (flags_ & PORTALLOCATOR_DISABLE_RELAY) {
+ LOG(LS_VERBOSE) << "AllocationSequence: Relay ports disabled, skipping.";
+ return;
+ }
+
+ // If BasicPortAllocatorSession::OnAllocate left relay ports enabled then we
+ // ought to have a relay list for them here.
+ ASSERT(config_ && !config_->relays.empty());
+ if (!(config_ && !config_->relays.empty())) {
+ LOG(LS_WARNING)
+ << "AllocationSequence: No relay server configured, skipping.";
+ return;
+ }
+
+ PortConfiguration::RelayList::const_iterator relay;
+ for (relay = config_->relays.begin();
+ relay != config_->relays.end(); ++relay) {
+ RelayPort* port = RelayPort::Create(session_->network_thread(), NULL,
+ network_,
+ talk_base::SocketAddress(ip_, 0),
+ config_->username, config_->password,
+ config_->magic_cookie);
+ if (port) {
+ // 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 talk_base::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::ResolveStunAddress() {
+ int err = 0;
+ if (!stun_address.ResolveIP(true, &err)) {
+ LOG(LS_ERROR) << "Unable to resolve STUN host "
+ << stun_address.hostname() << ". Error " << err;
+ return false;
+ }
+ return true;
+}
+
+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/third_party/libjingle/source/talk/p2p/client/basicportallocator.h b/third_party/libjingle/source/talk/p2p/client/basicportallocator.h
new file mode 100644
index 0000000..0891e86
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/client/basicportallocator.h
@@ -0,0 +1,199 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_CLIENT_BASICPORTALLOCATOR_H_
+#define TALK_P2P_CLIENT_BASICPORTALLOCATOR_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/base/messagequeue.h"
+#include "talk/base/network.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/portallocator.h"
+
+namespace cricket {
+
+class BasicPortAllocator : public PortAllocator {
+ public:
+ explicit BasicPortAllocator(talk_base::NetworkManager* network_manager);
+ BasicPortAllocator(talk_base::NetworkManager* network_manager,
+ const talk_base::SocketAddress& stun_server,
+ const talk_base::SocketAddress& relay_server_udp,
+ const talk_base::SocketAddress& relay_server_tcp,
+ const talk_base::SocketAddress& relay_server_ssl);
+ virtual ~BasicPortAllocator();
+
+ talk_base::NetworkManager* network_manager() { return network_manager_; }
+
+ const talk_base::SocketAddress& stun_address() const {
+ return stun_address_;
+ }
+ const talk_base::SocketAddress& relay_address_udp() const {
+ return relay_address_udp_;
+ }
+ const talk_base::SocketAddress& relay_address_tcp() const {
+ return relay_address_tcp_;
+ }
+ const talk_base::SocketAddress& relay_address_ssl() const {
+ return relay_address_ssl_;
+ }
+
+ // 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,
+ const std::string& session_type);
+
+ // Called whenever a connection becomes writable with the argument being the
+ // phase that the corresponding port was created in.
+ void AddWritablePhase(int phase);
+
+ bool allow_tcp_listen() const {
+ return allow_tcp_listen_;
+ }
+ void set_allow_tcp_listen(bool allow_tcp_listen) {
+ allow_tcp_listen_ = allow_tcp_listen;
+ }
+
+ private:
+ talk_base::NetworkManager* network_manager_;
+ const talk_base::SocketAddress stun_address_;
+ const talk_base::SocketAddress relay_address_udp_;
+ const talk_base::SocketAddress relay_address_tcp_;
+ const talk_base::SocketAddress relay_address_ssl_;
+ int best_writable_phase_;
+ bool allow_tcp_listen_;
+};
+
+struct PortConfiguration;
+class AllocationSequence;
+
+class BasicPortAllocatorSession : public PortAllocatorSession,
+ public talk_base::MessageHandler {
+ public:
+ BasicPortAllocatorSession(BasicPortAllocator* allocator,
+ const std::string& name,
+ const std::string& session_type);
+ ~BasicPortAllocatorSession();
+
+ virtual BasicPortAllocator* allocator() { return allocator_; }
+ const std::string& name() const { return name_; }
+ const std::string& session_type() const { return session_type_; }
+ talk_base::Thread* network_thread() { return network_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(talk_base::Message *message);
+
+ private:
+ void OnConfigReady(PortConfiguration* config);
+ void OnConfigTimeout();
+ void AllocatePorts();
+ void OnAllocate();
+ void DisableEquivalentPhases(talk_base::Network* network,
+ PortConfiguration* config, uint32* flags);
+ 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_;
+ std::string session_type_;
+ talk_base::Thread* network_thread_;
+ bool configuration_done_;
+ bool allocation_started_;
+ bool running_; // set when StartGetAllPorts is called
+ std::vector<PortConfiguration*> configs_;
+ std::vector<AllocationSequence*> sequences_;
+
+ 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 talk_base::MessageData {
+ talk_base::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 talk_base::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);
+
+ bool ResolveStunAddress();
+
+ // Determines whether the given relay server supports the given protocol.
+ static bool SupportsProtocol(const PortConfiguration::RelayServer& relay,
+ ProtocolType type);
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_CLIENT_BASICPORTALLOCATOR_H_
diff --git a/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc b/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc
new file mode 100644
index 0000000..c9cce12
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc
@@ -0,0 +1,243 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/client/httpportallocator.h"
+
+#include <map>
+
+#include "talk/base/asynchttprequest.h"
+#include "talk/base/basicdefs.h"
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/nethelpers.h"
+#include "talk/base/signalthread.h"
+
+namespace {
+
+const uint32 MSG_TIMEOUT = 100; // must not conflict
+ // with BasicPortAllocator.cpp
+
+// Helper routine to remove whitespace from the ends of a string.
+void Trim(std::string& str) {
+ size_t first = str.find_first_not_of(" \t\r\n");
+ if (first == std::string::npos) {
+ str.clear();
+ return;
+ }
+
+ ASSERT(str.find_last_not_of(" \t\r\n") != std::string::npos);
+}
+
+// Parses the lines in the result of the HTTP request that are of the form
+// 'a=b' and returns them in a map.
+typedef std::map<std::string, std::string> StringMap;
+void ParseMap(const std::string& string, StringMap& map) {
+ size_t start_of_line = 0;
+ size_t end_of_line = 0;
+
+ for (;;) { // for each line
+ start_of_line = string.find_first_not_of("\r\n", end_of_line);
+ if (start_of_line == std::string::npos)
+ break;
+
+ end_of_line = string.find_first_of("\r\n", start_of_line);
+ if (end_of_line == std::string::npos) {
+ end_of_line = string.length();
+ }
+
+ size_t equals = string.find('=', start_of_line);
+ if ((equals >= end_of_line) || (equals == std::string::npos))
+ continue;
+
+ std::string key(string, start_of_line, equals - start_of_line);
+ std::string value(string, equals + 1, end_of_line - equals - 1);
+
+ Trim(key);
+ Trim(value);
+
+ if ((key.size() > 0) && (value.size() > 0))
+ map[key] = value;
+ }
+}
+
+} // namespace
+
+namespace cricket {
+
+// HttpPortAllocator
+
+const int HttpPortAllocator::kHostPort = 80;
+const int HttpPortAllocator::kNumRetries = 5;
+
+const std::string HttpPortAllocator::kCreateSessionURL = "/create_session";
+
+HttpPortAllocator::HttpPortAllocator(talk_base::NetworkManager* network_manager,
+ const std::string &user_agent)
+ : BasicPortAllocator(network_manager), agent_(user_agent) {
+ relay_hosts_.push_back("relay.google.com");
+ stun_hosts_.push_back(
+ talk_base::SocketAddress("stun.l.google.com", 19302));
+}
+
+HttpPortAllocator::~HttpPortAllocator() {
+}
+
+PortAllocatorSession *HttpPortAllocator::CreateSession(
+ const std::string& name, const std::string& session_type) {
+ return new HttpPortAllocatorSession(this, name, session_type, stun_hosts_,
+ relay_hosts_, relay_token_, agent_);
+}
+
+// HttpPortAllocatorSession
+
+HttpPortAllocatorSession::HttpPortAllocatorSession(
+ HttpPortAllocator* allocator, const std::string &name,
+ const std::string& session_type,
+ const std::vector<talk_base::SocketAddress>& stun_hosts,
+ const std::vector<std::string>& relay_hosts,
+ const std::string& relay_token,
+ const std::string& user_agent)
+ : BasicPortAllocatorSession(allocator, name, session_type),
+ relay_hosts_(relay_hosts), stun_hosts_(stun_hosts),
+ relay_token_(relay_token), agent_(user_agent), attempts_(0) {
+}
+
+void HttpPortAllocatorSession::GetPortConfigurations() {
+ // Creating relay sessions can take time and is done asynchronously.
+ // Creating stun sessions could also take time and could be done aysnc also,
+ // but for now is done here and added to the initial config. Note any later
+ // configs will have unresolved stun ips and will be discarded by the
+ // AllocationSequence.
+ PortConfiguration* config = new PortConfiguration(stun_hosts_[0], "", "", "");
+ ConfigReady(config);
+ TryCreateRelaySession();
+}
+
+void HttpPortAllocatorSession::TryCreateRelaySession() {
+ if (attempts_ == HttpPortAllocator::kNumRetries) {
+ LOG(LS_ERROR) << "HttpPortAllocator: maximum number of requests reached; "
+ << "giving up on relay.";
+ return;
+ }
+
+ if (relay_hosts_.size() == 0) {
+ LOG(LS_ERROR) << "HttpPortAllocator: no relay hosts configured.";
+ return;
+ }
+
+ // Choose the next host to try.
+ std::string host = relay_hosts_[attempts_ % relay_hosts_.size()];
+ attempts_++;
+ LOG(LS_INFO) << "HTTPPortAllocator: sending to relay host " << host;
+ if (relay_token_.empty()) {
+ LOG(LS_WARNING) << "No relay auth token found.";
+ }
+
+ SendSessionRequest(host, HttpPortAllocator::kHostPort);
+}
+
+void HttpPortAllocatorSession::SendSessionRequest(const std::string& host,
+ int port) {
+ // Initiate an HTTP request to create a session through the chosen host.
+ talk_base::AsyncHttpRequest* request =
+ new talk_base::AsyncHttpRequest(agent_);
+ request->SignalWorkDone.connect(this,
+ &HttpPortAllocatorSession::OnRequestDone);
+
+ request->set_proxy(allocator()->proxy());
+ request->response().document.reset(new talk_base::MemoryStream);
+ request->request().verb = talk_base::HV_GET;
+ request->request().path = HttpPortAllocator::kCreateSessionURL;
+ request->request().addHeader("X-Talk-Google-Relay-Auth", relay_token_, true);
+ request->request().addHeader("X-Google-Relay-Auth", relay_token_, true);
+ request->request().addHeader("X-Session-Type", session_type(), true);
+ request->request().addHeader("X-Stream-Type", name(), true);
+ request->set_host(host);
+ request->set_port(port);
+ request->Start();
+ request->Release();
+}
+
+void HttpPortAllocatorSession::OnRequestDone(talk_base::SignalThread* data) {
+ talk_base::AsyncHttpRequest* request =
+ static_cast<talk_base::AsyncHttpRequest*>(data);
+ if (request->response().scode != 200) {
+ LOG(LS_WARNING) << "HTTPPortAllocator: request "
+ << " received error " << request->response().scode;
+ TryCreateRelaySession();
+ return;
+ }
+ LOG(LS_INFO) << "HTTPPortAllocator: request succeeded";
+
+ talk_base::MemoryStream* stream =
+ static_cast<talk_base::MemoryStream*>(request->response().document.get());
+ stream->Rewind();
+ size_t length;
+ stream->GetSize(&length);
+ std::string resp = std::string(stream->GetBuffer(), length);
+ ReceiveSessionResponse(resp);
+}
+
+void HttpPortAllocatorSession::ReceiveSessionResponse(
+ const std::string& response) {
+
+ StringMap map;
+ ParseMap(response, map);
+
+ std::string username = map["username"];
+ std::string password = map["password"];
+ std::string magic_cookie = map["magic_cookie"];
+
+ std::string relay_ip = map["relay.ip"];
+ std::string relay_udp_port = map["relay.udp_port"];
+ std::string relay_tcp_port = map["relay.tcp_port"];
+ std::string relay_ssltcp_port = map["relay.ssltcp_port"];
+
+ PortConfiguration* config = new PortConfiguration(stun_hosts_[0],
+ username,
+ password,
+ magic_cookie);
+
+ PortConfiguration::PortList ports;
+ if (!relay_udp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str()));
+ ports.push_back(ProtocolAddress(address, PROTO_UDP));
+ }
+ if (!relay_tcp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str()));
+ ports.push_back(ProtocolAddress(address, PROTO_TCP));
+ }
+ if (!relay_ssltcp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str()));
+ ports.push_back(ProtocolAddress(address, PROTO_SSLTCP));
+ }
+ config->AddRelay(ports, 0.0f);
+ ConfigReady(config);
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/p2p/client/httpportallocator.h b/third_party/libjingle/source/talk/p2p/client/httpportallocator.h
new file mode 100644
index 0000000..d695062
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/client/httpportallocator.h
@@ -0,0 +1,134 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_CLIENT_HTTPPORTALLOCATOR_H_
+#define TALK_P2P_CLIENT_HTTPPORTALLOCATOR_H_
+
+#include <string>
+#include <vector>
+#include "talk/p2p/client/basicportallocator.h"
+
+namespace talk_base {
+class SignalThread;
+}
+
+namespace cricket {
+
+class HttpPortAllocator : public BasicPortAllocator {
+ public:
+ // Records the port on the hosts that will receive HTTP requests.
+ static const int kHostPort;
+
+ // The number of HTTP requests we should attempt before giving up.
+ static const int kNumRetries;
+
+ // Records the URL that we will GET in order to create a session.
+ static const std::string kCreateSessionURL;
+
+ HttpPortAllocator(talk_base::NetworkManager* network_manager,
+ const std::string& user_agent);
+ virtual ~HttpPortAllocator();
+
+ virtual PortAllocatorSession* CreateSession(const std::string& name,
+ const std::string& session_type);
+ void SetStunHosts(const std::vector<talk_base::SocketAddress>& hosts) {
+ if (!hosts.empty()) {
+ stun_hosts_ = hosts;
+ }
+ }
+ void SetRelayHosts(const std::vector<std::string>& hosts) {
+ if (!hosts.empty()) {
+ relay_hosts_ = hosts;
+ }
+ }
+ void SetRelayToken(const std::string& relay) { relay_token_ = relay; }
+
+ const std::vector<talk_base::SocketAddress>& stun_hosts() const {
+ return stun_hosts_;
+ }
+
+ const std::vector<std::string>& relay_hosts() const {
+ return relay_hosts_;
+ }
+
+ const std::string& relay_token() const {
+ return relay_token_;
+ }
+
+ const std::string& user_agent() const {
+ return agent_;
+ }
+
+ private:
+ std::vector<talk_base::SocketAddress> stun_hosts_;
+ std::vector<std::string> relay_hosts_;
+ std::string relay_token_;
+ std::string agent_;
+};
+
+class RequestData;
+
+class HttpPortAllocatorSession : public BasicPortAllocatorSession {
+ public:
+ HttpPortAllocatorSession(
+ HttpPortAllocator* allocator,
+ const std::string& name,
+ const std::string& session_type,
+ const std::vector<talk_base::SocketAddress>& stun_hosts,
+ const std::vector<std::string>& relay_hosts,
+ const std::string& relay,
+ const std::string& agent);
+ virtual ~HttpPortAllocatorSession() {}
+
+ const std::string& relay_token() const {
+ return relay_token_;
+ }
+ virtual void SendSessionRequest(const std::string& host, int port);
+ virtual void ReceiveSessionResponse(const std::string& response);
+
+ protected:
+ virtual void GetPortConfigurations();
+ void TryCreateRelaySession();
+
+ private:
+ virtual HttpPortAllocator* allocator() {
+ return static_cast<HttpPortAllocator*>(
+ BasicPortAllocatorSession::allocator());
+ }
+
+ void OnRequestDone(talk_base::SignalThread* request);
+
+ std::vector<std::string> relay_hosts_;
+ std::vector<talk_base::SocketAddress> stun_hosts_;
+ std::string relay_token_;
+ std::string agent_;
+ int attempts_;
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_CLIENT_HTTPPORTALLOCATOR_H_
diff --git a/third_party/libjingle/source/talk/p2p/client/sessionmanagertask.h b/third_party/libjingle/source/talk/p2p/client/sessionmanagertask.h
new file mode 100644
index 0000000..2026bf6
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/client/sessionmanagertask.h
@@ -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.
+ */
+
+#ifndef _SESSIONMANAGERTASK_H_
+#define _SESSIONMANAGERTASK_H_
+
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/client/sessionsendtask.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+
+namespace cricket {
+
+// This class handles sending and receiving XMPP messages on behalf of the
+// SessionManager. The sending part is handed over to SessionSendTask.
+
+class SessionManagerTask : public buzz::XmppTask {
+ public:
+ SessionManagerTask(TaskParent *parent, SessionManager *session_manager)
+ : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE) {
+ session_manager_ = session_manager;
+ }
+
+ ~SessionManagerTask() {
+ }
+
+ // Turns on simple support for sending messages, using SessionSendTask.
+ void EnableOutgoingMessages() {
+ session_manager_->SignalOutgoingMessage.connect(
+ this, &SessionManagerTask::OnOutgoingMessage);
+ session_manager_->SignalRequestSignaling.connect(
+ session_manager_, &SessionManager::OnSignalingReady);
+ }
+
+ virtual int ProcessStart() {
+ const buzz::XmlElement *stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+ session_manager_->OnIncomingMessage(stanza);
+ return STATE_START;
+ }
+
+ protected:
+ virtual bool HandleStanza(const buzz::XmlElement *stanza) {
+ if (!session_manager_->IsSessionMessage(stanza))
+ return false;
+ // Responses are handled by the SessionSendTask that sent the request.
+ //if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET)
+ // return false;
+ QueueStanza(stanza);
+ return true;
+ }
+
+ private:
+ SessionManager* session_manager_;
+
+ void OnOutgoingMessage(SessionManager* manager,
+ const buzz::XmlElement* stanza) {
+ cricket::SessionSendTask* sender =
+ new cricket::SessionSendTask(GetParent(), session_manager_);
+ sender->Send(stanza);
+ sender->Start();
+ }
+};
+
+} // namespace cricket
+
+#endif // _SESSIONMANAGERTASK_H_
diff --git a/third_party/libjingle/source/talk/p2p/client/sessionsendtask.h b/third_party/libjingle/source/talk/p2p/client/sessionsendtask.h
new file mode 100644
index 0000000..7bdc067
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/client/sessionsendtask.h
@@ -0,0 +1,137 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_P2P_CLIENT_SESSIONSENDTASK_H_
+#define _CRICKET_P2P_CLIENT_SESSIONSENDTASK_H_
+
+#include "talk/base/common.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/p2p/base/sessionmanager.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 SessionManager. If an error
+// happens it will also signal failure. If, however, the send succeeds this
+// task will quietly go away.
+
+class SessionSendTask : public buzz::XmppTask {
+public:
+ SessionSendTask(TaskParent *parent, SessionManager *session_manager)
+ : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE),
+ session_manager_(session_manager) {
+ set_timeout_seconds(15);
+ }
+
+ virtual ~SessionSendTask() {
+ SignalDone(this);
+ }
+
+ void Send(const buzz::XmlElement* stanza) {
+ ASSERT(stanza_.get() == NULL);
+
+ // This should be an IQ of type set, result, or error. In the first case,
+ // we supply an ID. In the others, it should be present.
+ ASSERT(stanza->Name() == buzz::QN_IQ);
+ ASSERT(stanza->HasAttr(buzz::QN_TYPE));
+ if (stanza->Attr(buzz::QN_TYPE) == "set") {
+ ASSERT(!stanza->HasAttr(buzz::QN_ID));
+ } else {
+ ASSERT((stanza->Attr(buzz::QN_TYPE) == "result") ||
+ (stanza->Attr(buzz::QN_TYPE) == "error"));
+ ASSERT(stanza->HasAttr(buzz::QN_ID));
+ }
+
+ stanza_.reset(new buzz::XmlElement(*stanza));
+ if (stanza_->HasAttr(buzz::QN_ID)) {
+ set_task_id(stanza_->Attr(buzz::QN_ID));
+ } else {
+ stanza_->SetAttr(buzz::QN_ID, task_id());
+ }
+ }
+
+ sigslot::signal1<SessionSendTask *> SignalDone;
+
+protected:
+ virtual int OnTimeout() {
+ session_manager_->OnFailedSend(stanza_.get(), NULL);
+
+ return XmppTask::OnTimeout();
+ }
+
+ virtual int ProcessStart() {
+ SendStanza(stanza_.get());
+ if (stanza_->Attr(buzz::QN_TYPE) == buzz::STR_SET) {
+ return STATE_RESPONSE;
+ } else {
+ return STATE_DONE;
+ }
+ }
+
+ virtual int ProcessResponse() {
+ if (GetClient()->GetState() != buzz::XmppEngine::STATE_OPEN) {
+ return STATE_DONE;
+ }
+
+ const buzz::XmlElement* next = NextStanza();
+ if (next == NULL)
+ return STATE_BLOCKED;
+
+ if (next->Attr(buzz::QN_TYPE) == buzz::STR_RESULT) {
+ session_manager_->OnIncomingResponse(stanza_.get(), next);
+ } else {
+ session_manager_->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) == buzz::STR_RESULT ||
+ stanza->Attr(buzz::QN_TYPE) == buzz::STR_ERROR) {
+ QueueStanza(stanza);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ SessionManager *session_manager_;
+ talk_base::scoped_ptr<buzz::XmlElement> stanza_;
+ bool timed_out_;
+};
+
+}
+
+#endif // _CRICKET_P2P_CLIENT_SESSIONSENDTASK_H_
diff --git a/third_party/libjingle/source/talk/p2p/client/socketmonitor.cc b/third_party/libjingle/source/talk/p2p/client/socketmonitor.cc
new file mode 100644
index 0000000..bf32d84
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/client/socketmonitor.cc
@@ -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.
+ */
+
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/base/common.h"
+
+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(TransportChannel* channel,
+ talk_base::Thread* worker_thread,
+ talk_base::Thread* monitor_thread) {
+ channel_ = channel;
+ channel_thread_ = worker_thread;
+ monitoring_thread_ = monitor_thread;
+ monitoring_ = false;
+}
+
+SocketMonitor::~SocketMonitor() {
+ channel_thread_->Clear(this);
+ monitoring_thread_->Clear(this);
+}
+
+void SocketMonitor::Start(int milliseconds) {
+ rate_ = milliseconds;
+ if (rate_ < 250)
+ rate_ = 250;
+ channel_thread_->Post(this, MSG_MONITOR_START);
+}
+
+void SocketMonitor::Stop() {
+ channel_thread_->Post(this, MSG_MONITOR_STOP);
+}
+
+void SocketMonitor::OnMessage(talk_base::Message *message) {
+ talk_base::CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ ASSERT(talk_base::Thread::Current() == channel_thread_);
+ if (!monitoring_) {
+ monitoring_ = true;
+ if (GetP2PChannel() != NULL) {
+ GetP2PChannel()->SignalConnectionMonitor.connect(
+ this, &SocketMonitor::OnConnectionMonitor);
+ }
+ PollSocket(true);
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ ASSERT(talk_base::Thread::Current() == channel_thread_);
+ if (monitoring_) {
+ monitoring_ = false;
+ if (GetP2PChannel() != NULL)
+ GetP2PChannel()->SignalConnectionMonitor.disconnect(this);
+ channel_thread_->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ ASSERT(talk_base::Thread::Current() == channel_thread_);
+ PollSocket(true);
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ {
+ ASSERT(talk_base::Thread::Current() == monitoring_thread_);
+ std::vector<ConnectionInfo> infos = connection_infos_;
+ crit_.Leave();
+ SignalUpdate(this, infos);
+ crit_.Enter();
+ }
+ break;
+ }
+}
+
+void SocketMonitor::OnConnectionMonitor(P2PTransportChannel* channel) {
+ talk_base::CritScope cs(&crit_);
+ if (monitoring_)
+ PollSocket(false);
+}
+
+void SocketMonitor::PollSocket(bool poll) {
+ ASSERT(talk_base::Thread::Current() == channel_thread_);
+ talk_base::CritScope cs(&crit_);
+
+ // Gather connection infos
+ P2PTransportChannel* p2p_channel = GetP2PChannel();
+ if (p2p_channel != NULL) {
+ connection_infos_.clear();
+ const std::vector<Connection *> &connections = p2p_channel->connections();
+ std::vector<Connection *>::const_iterator it;
+ for (it = connections.begin(); it != connections.end(); it++) {
+ Connection *connection = *it;
+ ConnectionInfo info;
+ info.best_connection = p2p_channel->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 = !connection->reported();
+ connection->set_reported(true);
+ 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 = connection;
+ connection_infos_.push_back(info);
+ }
+ }
+
+ // Signal the monitoring thread, start another poll timer
+
+ monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL);
+ if (poll)
+ channel_thread_->PostDelayed(rate_, this, MSG_MONITOR_POLL);
+}
+
+}
diff --git a/third_party/libjingle/source/talk/p2p/client/socketmonitor.h b/third_party/libjingle/source/talk/p2p/client/socketmonitor.h
new file mode 100644
index 0000000..cf29d9d
--- /dev/null
+++ b/third_party/libjingle/source/talk/p2p/client/socketmonitor.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 TALK_P2P_CLIENT_SOCKETMONITOR_H_
+#define TALK_P2P_CLIENT_SOCKETMONITOR_H_
+
+#include <vector>
+
+#include "talk/base/criticalsection.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/p2ptransportchannel.h"
+#include "talk/p2p/base/port.h"
+
+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 talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+ public:
+ SocketMonitor(TransportChannel* channel,
+ talk_base::Thread* worker_thread,
+ talk_base::Thread* monitor_thread);
+ ~SocketMonitor();
+
+ void Start(int cms);
+ void Stop();
+
+ talk_base::Thread *monitor_thread() { return monitoring_thread_; }
+
+ sigslot::signal2<SocketMonitor *,
+ const std::vector<ConnectionInfo> &> SignalUpdate;
+
+ protected:
+ P2PTransportChannel* GetP2PChannel() { return channel_->GetP2PChannel(); }
+ void OnMessage(talk_base::Message *message);
+ void OnConnectionMonitor(P2PTransportChannel* channel);
+ void PollSocket(bool poll);
+
+ std::vector<ConnectionInfo> connection_infos_;
+ TransportChannel* channel_;
+ talk_base::Thread* channel_thread_;
+ talk_base::Thread* monitoring_thread_;
+ talk_base::CriticalSection crit_;
+ uint32 rate_;
+ bool monitoring_;
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_CLIENT_SOCKETMONITOR_H_
diff --git a/third_party/libjingle/source/talk/session/phone/audiomonitor.cc b/third_party/libjingle/source/talk/session/phone/audiomonitor.cc
new file mode 100644
index 0000000..d980ded
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/audiomonitor.cc
@@ -0,0 +1,121 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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,
+ talk_base::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(talk_base::Message *message) {
+ talk_base::CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ if (!monitoring_) {
+ monitoring_ = true;
+ PollVoiceChannel();
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ if (monitoring_) {
+ monitoring_ = false;
+ voice_channel_->worker_thread()->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ PollVoiceChannel();
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ {
+ assert(talk_base::Thread::Current() == monitoring_thread_);
+ AudioInfo info = audio_info_;
+ crit_.Leave();
+ SignalUpdate(this, info);
+ crit_.Enter();
+ }
+ break;
+ }
+}
+
+void AudioMonitor::PollVoiceChannel() {
+ talk_base::CritScope cs(&crit_);
+ assert(talk_base::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();
+ voice_channel_->GetActiveStreams_w(&audio_info_.active_streams);
+
+ // 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_;
+}
+
+talk_base::Thread *AudioMonitor::monitor_thread() {
+ return monitoring_thread_;
+}
+
+}
diff --git a/third_party/libjingle/source/talk/session/phone/audiomonitor.h b/third_party/libjingle/source/talk/session/phone/audiomonitor.h
new file mode 100644
index 0000000..e94ea68
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/audiomonitor.h
@@ -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.
+ */
+
+#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;
+ typedef std::vector<std::pair<uint32, int> > StreamList;
+ StreamList active_streams; // ssrcs contributing to output_level
+};
+
+class AudioMonitor : public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ AudioMonitor(VoiceChannel* voice_channel, talk_base::Thread *monitor_thread);
+ ~AudioMonitor();
+
+ void Start(int cms);
+ void Stop();
+
+ VoiceChannel* voice_channel();
+ talk_base::Thread *monitor_thread();
+
+ sigslot::signal2<AudioMonitor*, const AudioInfo&> SignalUpdate;
+
+protected:
+ void OnMessage(talk_base::Message *message);
+ void PollVoiceChannel();
+
+ AudioInfo audio_info_;
+ VoiceChannel* voice_channel_;
+ talk_base::Thread* monitoring_thread_;
+ talk_base::CriticalSection crit_;
+ uint32 rate_;
+ bool monitoring_;
+};
+
+}
+
+#endif // _CRICKET_PHONE_AUDIOMONITOR_H_
diff --git a/third_party/libjingle/source/talk/session/phone/call.cc b/third_party/libjingle/source/talk/session/phone/call.cc
new file mode 100644
index 0000000..e2717ce
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/call.cc
@@ -0,0 +1,443 @@
+/*
+ * libjingle
+ * Copyright 2004--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#include "talk/session/phone/call.h"
+
+namespace cricket {
+
+const uint32 MSG_CHECKAUTODESTROY = 1;
+const uint32 MSG_TERMINATECALL = 2;
+
+namespace {
+const int kSendToVoicemailTimeout = 1000*20;
+const int kNoVoicemailTimeout = 1000*180;
+const int kMediaMonitorInterval = 1000*15;
+}
+
+Call::Call(MediaSessionClient *session_client, bool video, bool mux)
+ : id_(talk_base::CreateRandomId()), session_client_(session_client),
+ local_renderer_(NULL), video_(video), mux_(mux),
+ muted_(false), send_to_voicemail_(true)
+{
+}
+
+Call::~Call() {
+ while (sessions_.begin() != sessions_.end()) {
+ Session *session = sessions_[0];
+ RemoveSession(session);
+ session_client_->session_manager()->DestroySession(session);
+ }
+ talk_base::Thread::Current()->Clear(this);
+}
+
+Session *Call::InitiateSession(const buzz::Jid &jid) {
+ Session *session = session_client_->CreateSession(this);
+ AddSession(session);
+
+ MediaSessionDescription *session_desc =
+ session_client_->CreateOfferSessionDescription(video_);
+ if (mux_) {
+ session_desc->voice().set_ssrc(0);
+ if (video_) {
+ session_desc->video().set_ssrc(0);
+ }
+ }
+ session->Initiate(jid.Str(), session_desc);
+
+ // After this timeout, terminate the call because the callee isn't
+ // answering
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ session_client_->session_manager()->signaling_thread()->PostDelayed(
+ send_to_voicemail_ ? kSendToVoicemailTimeout : kNoVoicemailTimeout,
+ this, MSG_TERMINATECALL);
+ return session;
+}
+
+void Call::AcceptSession(BaseSession *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::RejectSession(BaseSession *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(BaseSession *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() {
+ // Copy the list so that we can iterate over it in a stable way
+ std::vector<Session *> sessions = sessions_;
+
+ // There may be more than one session to terminate
+ std::vector<Session *>::iterator it;
+ for (it = sessions.begin(); it != sessions.end(); it++)
+ TerminateSession(*it);
+}
+
+void Call::SetLocalRenderer(VideoRenderer* renderer) {
+ local_renderer_ = renderer;
+ if (session_client_->GetFocus() == this) {
+ session_client_->channel_manager()->SetLocalRenderer(renderer);
+ }
+}
+
+void Call::SetVideoRenderer(BaseSession *session, uint32 ssrc,
+ VideoRenderer* renderer) {
+ VideoChannel *video_channel = GetVideoChannel(session);
+ if (video_channel) {
+ video_channel->SetRenderer(ssrc, renderer);
+ }
+}
+
+void Call::AddStream(BaseSession *session,
+ uint32 voice_ssrc, uint32 video_ssrc) {
+ VoiceChannel *voice_channel = GetVoiceChannel(session);
+ VideoChannel *video_channel = GetVideoChannel(session);
+ if (voice_channel && voice_ssrc) {
+ voice_channel->AddStream(voice_ssrc);
+ }
+ if (video_channel && video_ssrc) {
+ video_channel->AddStream(video_ssrc, voice_ssrc);
+ }
+}
+
+void Call::RemoveStream(BaseSession *session,
+ uint32 voice_ssrc, uint32 video_ssrc) {
+ VoiceChannel *voice_channel = GetVoiceChannel(session);
+ VideoChannel *video_channel = GetVideoChannel(session);
+ if (voice_channel && voice_ssrc) {
+ voice_channel->RemoveStream(voice_ssrc);
+ }
+ if (video_channel && video_ssrc) {
+ video_channel->RemoveStream(video_ssrc);
+ }
+}
+
+void Call::OnMessage(talk_base::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;
+ case MSG_TERMINATECALL:
+ // Signal to the user that a timeout has happened and the call should
+ // be sent to voicemail.
+ if (send_to_voicemail_) {
+ SignalSetupToCallVoicemail();
+ }
+
+ // Callee didn't answer - terminate call
+ Terminate();
+ break;
+ }
+}
+
+const std::vector<Session *> &Call::sessions() {
+ return sessions_;
+}
+
+bool Call::AddSession(Session *session) {
+ bool succeeded = true;
+ VoiceChannel *voice_channel = NULL;
+ VideoChannel *video_channel = NULL;
+
+ // Create voice channel and start a media monitor
+ voice_channel = session_client_->channel_manager()->CreateVoiceChannel(
+ session, video_);
+ // voice_channel can be NULL in case of NullVoiceEngine.
+ if (voice_channel) {
+ voice_channel_map_[session->id()] = voice_channel;
+
+ voice_channel->SignalMediaMonitor.connect(this, &Call::OnMediaMonitor);
+ voice_channel->StartMediaMonitor(kMediaMonitorInterval);
+ } else {
+ succeeded = false;
+ }
+
+ // If desired, create video channel and start a media monitor
+ if (video_ && succeeded) {
+ video_channel = session_client_->channel_manager()->CreateVideoChannel(
+ session, true, voice_channel);
+ // video_channel can be NULL in case of NullVideoEngine.
+ if (video_channel) {
+ video_channel_map_[session->id()] = video_channel;
+
+ video_channel->SignalMediaMonitor.connect(this, &Call::OnMediaMonitor);
+ video_channel->StartMediaMonitor(kMediaMonitorInterval);
+ } else {
+ succeeded = false;
+ }
+ }
+
+ if (succeeded) {
+ // Add session to list, create channels for this session
+ sessions_.push_back(session);
+ session->SignalState.connect(this, &Call::OnSessionState);
+ session->SignalError.connect(this, &Call::OnSessionError);
+ session->SignalReceivedTerminateReason
+ .connect(this, &Call::OnReceivedTerminateReason);
+
+ // If this call has the focus, enable this channel
+ if (session_client_->GetFocus() == this) {
+ voice_channel->Enable(true);
+ if (video_channel) {
+ video_channel->Enable(true);
+ }
+ }
+
+ // Signal client
+ SignalAddSession(this, session);
+ }
+
+ return succeeded;
+}
+
+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 video channel
+ std::map<SessionID, VideoChannel *>::iterator it_vchannel;
+ it_vchannel = video_channel_map_.find(session->id());
+ if (it_vchannel != video_channel_map_.end()) {
+ VideoChannel *video_channel = it_vchannel->second;
+ video_channel_map_.erase(it_vchannel);
+ session_client_->channel_manager()->DestroyVideoChannel(video_channel);
+ }
+
+ // Destroy voice channel
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = voice_channel_map_.find(session->id());
+ if (it_channel != voice_channel_map_.end()) {
+ VoiceChannel *voice_channel = it_channel->second;
+ voice_channel_map_.erase(it_channel);
+ session_client_->channel_manager()->DestroyVoiceChannel(voice_channel);
+ }
+
+ // Signal client
+ SignalRemoveSession(this, session);
+
+ // The call auto destroys when the last session is removed
+ talk_base::Thread::Current()->Post(this, MSG_CHECKAUTODESTROY);
+}
+
+VoiceChannel* Call::GetVoiceChannel(BaseSession* session) {
+ std::map<SessionID, VoiceChannel *>::iterator it
+ = voice_channel_map_.find(session->id());
+ return (it != voice_channel_map_.end()) ? it->second : NULL;
+}
+
+VideoChannel* Call::GetVideoChannel(BaseSession* session) {
+ std::map<SessionID, VideoChannel *>::iterator it
+ = video_channel_map_.find(session->id());
+ return (it != video_channel_map_.end()) ? it->second : NULL;
+}
+
+void Call::EnableChannels(bool enable) {
+ std::vector<Session *>::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); it++) {
+ VoiceChannel *voice_channel = GetVoiceChannel(*it);
+ VideoChannel *video_channel = GetVideoChannel(*it);
+ if (voice_channel != NULL)
+ voice_channel->Enable(enable);
+ if (video_channel != NULL)
+ video_channel->Enable(enable);
+ }
+ session_client_->channel_manager()->SetLocalRenderer(
+ (enable) ? local_renderer_ : NULL);
+}
+
+void Call::Mute(bool mute) {
+ muted_ = mute;
+ std::vector<Session *>::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); it++) {
+ VoiceChannel *voice_channel = voice_channel_map_[(*it)->id()];
+ if (voice_channel != NULL)
+ voice_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);
+ session->SignalReceivedTerminateReason
+ .connect(this, &Call::OnReceivedTerminateReason);
+
+ // Move voice channel
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = call->voice_channel_map_.find(session->id());
+ if (it_channel != call->voice_channel_map_.end()) {
+ VoiceChannel *voice_channel = (*it_channel).second;
+ call->voice_channel_map_.erase(it_channel);
+ voice_channel_map_[session->id()] = voice_channel;
+ voice_channel->Enable(enable);
+ }
+
+ // Move video channel
+ std::map<SessionID, VideoChannel *>::iterator it_vchannel;
+ it_vchannel = call->video_channel_map_.find(session->id());
+ if (it_vchannel != call->video_channel_map_.end()) {
+ VideoChannel *video_channel = (*it_vchannel).second;
+ call->video_channel_map_.erase(it_vchannel);
+ video_channel_map_[session->id()] = video_channel;
+ video_channel->Enable(enable);
+ }
+ }
+}
+
+void Call::StartConnectionMonitor(BaseSession *session, int cms) {
+ VoiceChannel *voice_channel = GetVoiceChannel(session);
+ if (voice_channel) {
+ voice_channel->SignalConnectionMonitor.connect(this,
+ &Call::OnConnectionMonitor);
+ voice_channel->StartConnectionMonitor(cms);
+ }
+
+ VideoChannel *video_channel = GetVideoChannel(session);
+ if (video_channel) {
+ video_channel->SignalConnectionMonitor.connect(this,
+ &Call::OnConnectionMonitor);
+ video_channel->StartConnectionMonitor(cms);
+ }
+}
+
+void Call::StopConnectionMonitor(BaseSession *session) {
+ VoiceChannel *voice_channel = GetVoiceChannel(session);
+ if (voice_channel) {
+ voice_channel->StopConnectionMonitor();
+ voice_channel->SignalConnectionMonitor.disconnect(this);
+ }
+
+ VideoChannel *video_channel = GetVideoChannel(session);
+ if (video_channel) {
+ video_channel->StopConnectionMonitor();
+ video_channel->SignalConnectionMonitor.disconnect(this);
+ }
+}
+
+void Call::StartAudioMonitor(BaseSession *session, int cms) {
+ VoiceChannel *voice_channel = GetVoiceChannel(session);
+ if (voice_channel) {
+ voice_channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor);
+ voice_channel->StartAudioMonitor(cms);
+ }
+}
+
+void Call::StopAudioMonitor(BaseSession *session) {
+ VoiceChannel *voice_channel = GetVoiceChannel(session);
+ if (voice_channel) {
+ voice_channel->StopAudioMonitor();
+ voice_channel->SignalAudioMonitor.disconnect(this);
+ }
+}
+
+void Call::OnConnectionMonitor(VoiceChannel *channel,
+ const std::vector<ConnectionInfo> &infos) {
+ SignalConnectionMonitor(this, infos);
+}
+
+void Call::OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info) {
+ SignalMediaMonitor(this, info);
+}
+
+void Call::OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info) {
+ SignalAudioMonitor(this, info);
+}
+
+void Call::OnConnectionMonitor(VideoChannel *channel,
+ const std::vector<ConnectionInfo> &infos) {
+ SignalVideoConnectionMonitor(this, infos);
+}
+
+void Call::OnMediaMonitor(VideoChannel *channel, const MediaInfo& info) {
+ SignalVideoMediaMonitor(this, info);
+}
+
+uint32 Call::id() {
+ return id_;
+}
+
+void Call::OnSessionState(BaseSession *session, BaseSession::State state) {
+ switch (state) {
+ case Session::STATE_RECEIVEDACCEPT:
+ case Session::STATE_RECEIVEDREJECT:
+ case Session::STATE_RECEIVEDTERMINATE:
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ break;
+ default:
+ break;
+ }
+ SignalSessionState(this, session, state);
+}
+
+void Call::OnSessionError(BaseSession *session, Session::Error error) {
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ SignalSessionError(this, session, error);
+}
+
+void Call::OnReceivedTerminateReason(Session *session,
+ const std::string &reason) {
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ SignalReceivedTerminateReason(this, session, reason);
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/call.h b/third_party/libjingle/source/talk/session/phone/call.h
new file mode 100644
index 0000000..8a1feba
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/call.h
@@ -0,0 +1,134 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CALL_H_
+#define TALK_SESSION_PHONE_CALL_H_
+
+#include <string>
+#include <map>
+#include <vector>
+#include <deque>
+#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/mediasessionclient.h"
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/audiomonitor.h"
+
+
+namespace cricket {
+
+class MediaSessionClient;
+
+class Call : public talk_base::MessageHandler, public sigslot::has_slots<> {
+ public:
+ Call(MediaSessionClient *session_client,
+ bool video = false, bool mux = false);
+ ~Call();
+
+ Session *InitiateSession(const buzz::Jid &jid);
+ void AcceptSession(BaseSession *session);
+ void RejectSession(BaseSession *session);
+ void TerminateSession(BaseSession *session);
+ void Terminate();
+ void SetLocalRenderer(VideoRenderer* renderer);
+ void SetVideoRenderer(BaseSession *session, uint32 ssrc,
+ VideoRenderer* renderer);
+ void AddStream(BaseSession *session, uint32 voice_ssrc, uint32 video_ssrc);
+ void RemoveStream(BaseSession *session, uint32 voice_ssrc, uint32 video_ssrc);
+ void StartConnectionMonitor(BaseSession *session, int cms);
+ void StopConnectionMonitor(BaseSession *session);
+ void StartAudioMonitor(BaseSession *session, int cms);
+ void StopAudioMonitor(BaseSession *session);
+ void Mute(bool mute);
+
+
+ const std::vector<Session *> &sessions();
+ uint32 id();
+ bool video() const { return video_; }
+ bool muted() const { return muted_; }
+
+ // Setting this to false will cause the call to have a longer timeout and
+ // for the SignalSetupToCallVoicemail to never fire.
+ void set_send_to_voicemail(bool send_to_voicemail) {
+ send_to_voicemail_ = send_to_voicemail;
+ }
+ bool send_to_voicemail() { return send_to_voicemail_; }
+
+ // Sets a flag on the chatapp that will redirect the call to voicemail once
+ // the call has been terminated
+ sigslot::signal0<> SignalSetupToCallVoicemail;
+ sigslot::signal2<Call *, Session *> SignalAddSession;
+ sigslot::signal2<Call *, Session *> SignalRemoveSession;
+ sigslot::signal3<Call *, BaseSession *, BaseSession::State> SignalSessionState;
+ sigslot::signal3<Call *, BaseSession *, Session::Error> SignalSessionError;
+ sigslot::signal3<Call *, Session *, const std::string &> SignalReceivedTerminateReason;
+ sigslot::signal2<Call *, const std::vector<ConnectionInfo> &> SignalConnectionMonitor;
+ sigslot::signal2<Call *, const MediaInfo&> SignalMediaMonitor;
+ sigslot::signal2<Call *, const AudioInfo&> SignalAudioMonitor;
+ sigslot::signal2<Call *, const std::vector<ConnectionInfo> &> SignalVideoConnectionMonitor;
+ sigslot::signal2<Call *, const MediaInfo&> SignalVideoMediaMonitor;
+
+ private:
+ void OnMessage(talk_base::Message *message);
+ void OnSessionState(BaseSession *session, BaseSession::State state);
+ void OnSessionError(BaseSession *session, Session::Error error);
+ void OnReceivedTerminateReason(Session *session, const std::string &reason);
+ // Returns true on success.
+ bool 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 OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info);
+ void OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info);
+ void OnConnectionMonitor(VideoChannel *channel,
+ const std::vector<ConnectionInfo> &infos);
+ void OnMediaMonitor(VideoChannel *channel, const MediaInfo& info);
+ VoiceChannel* GetVoiceChannel(BaseSession* session);
+ VideoChannel* GetVideoChannel(BaseSession* session);
+
+ uint32 id_;
+ MediaSessionClient *session_client_;
+ std::vector<Session *> sessions_;
+ std::map<SessionID, VoiceChannel *> voice_channel_map_;
+ std::map<SessionID, VideoChannel *> video_channel_map_;
+ VideoRenderer* local_renderer_;
+ bool video_;
+ bool mux_;
+ bool muted_;
+ bool send_to_voicemail_;
+
+
+ friend class MediaSessionClient;
+};
+
+}
+
+#endif // TALK_SESSION_PHONE_CALL_H_
diff --git a/third_party/libjingle/source/talk/session/phone/channel.cc b/third_party/libjingle/source/talk/session/phone/channel.cc
new file mode 100644
index 0000000..2b3e043
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/channel.cc
@@ -0,0 +1,969 @@
+/*
+ * libjingle
+ * Copyright 2004--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/channel.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/session/phone/mediasessionclient.h"
+
+namespace cricket {
+
+static const size_t kMaxPacketLen = 2048;
+
+static const char* PacketType(bool rtcp) {
+ return (!rtcp) ? "RTP" : "RTCP";
+}
+
+BaseChannel::BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine,
+ MediaChannel* media_channel, BaseSession* session,
+ TransportChannel* transport_channel)
+ : worker_thread_(thread), media_engine_(media_engine),
+ session_(session), media_channel_(media_channel),
+ transport_channel_(transport_channel), rtcp_transport_channel_(NULL),
+ enabled_(false), writable_(false), has_codec_(false), muted_(false) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ media_channel_->SetInterface(this);
+ transport_channel_->SignalWritableState.connect(
+ this, &BaseChannel::OnWritableState);
+ transport_channel_->SignalReadPacket.connect(
+ this, &BaseChannel::OnChannelRead);
+
+ LOG(LS_INFO) << "Created channel";
+
+ session->SignalState.connect(this, &BaseChannel::OnSessionState);
+ OnSessionState(session, session->state());
+}
+
+BaseChannel::~BaseChannel() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ StopConnectionMonitor();
+ Clear();
+ // We must destroy the media channel before the transport channel, otherwise
+ // the media channel may try to send on the dead transport channel. NULLing
+ // is not an effective strategy since the sends will come on another thread.
+ delete media_channel_;
+ set_rtcp_transport_channel(NULL);
+ if (transport_channel_ != NULL)
+ session_->DestroyChannel(transport_channel_);
+ LOG(LS_INFO) << "Destroyed channel";
+}
+
+bool BaseChannel::Enable(bool enable) {
+ // Can be called from thread other than worker thread
+ Send(enable ? MSG_ENABLE : MSG_DISABLE);
+ return true;
+}
+
+bool BaseChannel::Mute(bool mute) {
+ // Can be called from thread other than worker thread
+ Send(mute ? MSG_MUTE : MSG_UNMUTE);
+ return true;
+}
+
+bool BaseChannel::RemoveStream(uint32 ssrc) {
+ StreamMessageData data(ssrc, 0);
+ Send(MSG_REMOVESTREAM, &data);
+ return true;
+}
+
+bool BaseChannel::SetLocalDescription(const MediaSessionDescription& desc,
+ DescriptionType type) {
+ SetDescriptionData data(desc, type);
+ Send(MSG_SETLOCALDESCRIPTION, &data);
+ return data.result;
+}
+
+bool BaseChannel::SetRemoteDescription(const MediaSessionDescription& desc,
+ DescriptionType type) {
+ SetDescriptionData data(desc, type);
+ Send(MSG_SETREMOTEDESCRIPTION, &data);
+ return data.result;
+}
+
+bool BaseChannel::SetMaxSendBandwidth(int max_bandwidth) {
+ SetBandwidthData data(max_bandwidth);
+ Send(MSG_SETMAXSENDBANDWIDTH, &data);
+ return data.result;
+}
+
+void BaseChannel::StartConnectionMonitor(int cms) {
+ socket_monitor_.reset(new SocketMonitor(transport_channel_,
+ worker_thread(),
+ talk_base::Thread::Current()));
+ socket_monitor_->SignalUpdate.connect(
+ this, &BaseChannel::OnConnectionMonitorUpdate);
+ socket_monitor_->Start(cms);
+}
+
+void BaseChannel::StopConnectionMonitor() {
+ if (socket_monitor_.get()) {
+ socket_monitor_->Stop();
+ socket_monitor_.reset();
+ }
+}
+
+void BaseChannel::set_rtcp_transport_channel(TransportChannel* channel) {
+ if (rtcp_transport_channel_ != channel) {
+ if (rtcp_transport_channel_) {
+ session_->DestroyChannel(rtcp_transport_channel_);
+ }
+ rtcp_transport_channel_ = channel;
+ if (rtcp_transport_channel_) {
+ rtcp_transport_channel_->SignalWritableState.connect(
+ this, &BaseChannel::OnWritableState);
+ rtcp_transport_channel_->SignalReadPacket.connect(
+ this, &BaseChannel::OnChannelRead);
+ }
+ }
+}
+
+int BaseChannel::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.
+ // TODO(juberti): Actually, SendPacket cannot handle this. Need to fix ASAP.
+
+ return SendPacket(false, data, len);
+}
+
+int BaseChannel::SendRtcp(const void *data, size_t len) {
+ return SendPacket(true, data, len);
+}
+
+int BaseChannel::SetOption(SocketType type, talk_base::Socket::Option opt,
+ int value) {
+ switch (type) {
+ case ST_RTP: return transport_channel_->SetOption(opt, value);
+ case ST_RTCP: return rtcp_transport_channel_->SetOption(opt, value);
+ default: return -1;
+ }
+}
+
+void BaseChannel::OnWritableState(TransportChannel* channel) {
+ ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_);
+ if (transport_channel_->writable()
+ && (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) {
+ ChannelWritable_w();
+ } else {
+ ChannelNotWritable_w();
+ }
+}
+
+void BaseChannel::OnChannelRead(TransportChannel* channel,
+ const char* data, size_t len) {
+ // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ // When using RTCP multiplexing we might get RTCP packets on the RTP
+ // transport. We feed RTP traffic into the demuxer to determine if it is RTCP.
+ bool rtcp = (channel == rtcp_transport_channel_ ||
+ rtcp_mux_filter_.DemuxRtcp(data, len));
+ HandlePacket(rtcp, data, len);
+}
+
+int BaseChannel::SendPacket(bool rtcp, const void* data, size_t len) {
+ // Protect ourselves against crazy data.
+ if (len > kMaxPacketLen) {
+ LOG(LS_ERROR) << "Dropping outgoing large "
+ << PacketType(rtcp) << " packet, size " << len;
+ return -1;
+ }
+
+ // Make sure we have a place to send this packet before doing anything.
+ // (We might get RTCP packets that we don't intend to send.)
+ // If we've negotiated RTCP mux, send RTCP over the RTP transport.
+ TransportChannel* channel = (!rtcp || rtcp_mux_filter_.IsActive()) ?
+ transport_channel_ : rtcp_transport_channel_;
+ if (!channel) {
+ return -1;
+ }
+
+ // Protect if needed.
+ uint8 work[kMaxPacketLen];
+ const char* real_data = static_cast<const char*>(data);
+ int real_len = len;
+ if (srtp_filter_.IsActive()) {
+ bool res;
+ memcpy(work, data, len);
+ if (!rtcp) {
+ res = srtp_filter_.ProtectRtp(work, len, sizeof(work), &real_len);
+ } else {
+ res = srtp_filter_.ProtectRtcp(work, len, sizeof(work), &real_len);
+ }
+ if (!res) {
+ LOG(LS_ERROR) << "Failed to protect "
+ << PacketType(rtcp) << " packet, size " << len;
+ return -1;
+ }
+
+ real_data = reinterpret_cast<const char*>(work);
+ }
+
+ // Bon voyage. Return a number that the caller can understand.
+ return (channel->SendPacket(real_data, real_len) == real_len) ? len : -1;
+}
+
+void BaseChannel::HandlePacket(bool rtcp, const char* data, size_t len) {
+ // Protect ourselvs against crazy data.
+ if (len > kMaxPacketLen) {
+ LOG(LS_ERROR) << "Dropping incoming large "
+ << PacketType(rtcp) << " packet, size " << len;
+ return;
+ }
+
+ // Unprotect the packet, if needed.
+ uint8 work[kMaxPacketLen];
+ const char* real_data = data;
+ int real_len = len;
+ if (srtp_filter_.IsActive()) {
+ bool res;
+ memcpy(work, data, len);
+ if (!rtcp) {
+ res = srtp_filter_.UnprotectRtp(work, len, &real_len);
+ } else {
+ res = srtp_filter_.UnprotectRtcp(work, len, &real_len);
+ }
+ if (!res) {
+ LOG(LS_ERROR) << "Failed to unprotect "
+ << PacketType(rtcp) << " packet, size " << len;
+ return;
+ }
+ real_data = reinterpret_cast<const char*>(work);
+ }
+
+ // Push it down to the media channel.
+ if (!rtcp) {
+ media_channel_->OnPacketReceived(real_data, real_len);
+ } else {
+ media_channel_->OnRtcpReceived(real_data, real_len);
+ }
+}
+
+void BaseChannel::OnSessionState(BaseSession* session,
+ BaseSession::State state) {
+ // TODO(juberti): tear down the call via session->SetError() if the
+ // SetXXXXDescription calls fail.
+ switch (state) {
+ case Session::STATE_SENTINITIATE:
+ case Session::STATE_SENTACCEPT:
+ if (session->local_description()) {
+ SetLocalDescription(*static_cast<const MediaSessionDescription*>(
+ session->local_description()),
+ (state == Session::STATE_SENTINITIATE) ?
+ DT_OFFER : DT_ANSWER);
+ }
+ break;
+
+ case Session::STATE_RECEIVEDINITIATE:
+ case Session::STATE_RECEIVEDACCEPT:
+ if (session->remote_description()) {
+ SetRemoteDescription(*static_cast<const MediaSessionDescription*>(
+ session->remote_description()),
+ (state == Session::STATE_RECEIVEDINITIATE) ?
+ DT_OFFER : DT_ANSWER);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void BaseChannel::EnableMedia_w() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ if (enabled_)
+ return;
+
+ LOG(LS_INFO) << "Channel enabled";
+ enabled_ = true;
+ ChangeState();
+}
+
+void BaseChannel::DisableMedia_w() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ if (!enabled_)
+ return;
+
+ LOG(LS_INFO) << "Channel disabled";
+ enabled_ = false;
+ ChangeState();
+}
+
+void BaseChannel::MuteMedia_w() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ if (muted_)
+ return;
+
+ if (media_channel()->Mute(true)) {
+ LOG(LS_INFO) << "Channel muted";
+ muted_ = true;
+ }
+}
+
+void BaseChannel::UnmuteMedia_w() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ if (!muted_)
+ return;
+
+ if (media_channel()->Mute(false)) {
+ LOG(LS_INFO) << "Channel unmuted";
+ muted_ = false;
+ }
+}
+
+void BaseChannel::ChannelWritable_w() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ if (writable_)
+ return;
+ LOG(LS_INFO) << "Channel socket writable ("
+ << transport_channel_->name().c_str() << ")";
+ writable_ = true;
+ ChangeState();
+}
+
+void BaseChannel::ChannelNotWritable_w() {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ if (!writable_)
+ return;
+
+ LOG(LS_INFO) << "Channel socket not writable ("
+ << transport_channel_->name().c_str() << ")";
+ writable_ = false;
+ ChangeState();
+}
+
+bool BaseChannel::SetMaxSendBandwidth_w(int max_bandwidth) {
+ return media_channel()->SetMaxSendBandwidth(max_bandwidth);
+}
+
+bool BaseChannel::SetSrtp_w(const std::vector<CryptoParams>& cryptos,
+ DescriptionType type, DescriptionSource src) {
+ bool ret;
+ if (type == DT_OFFER) {
+ ret = srtp_filter_.SetOffer(cryptos, src);
+ } else if (type == DT_ANSWER) {
+ ret = srtp_filter_.SetAnswer(cryptos, src);
+ } else {
+ // DT_UPDATE, no crypto params.
+ ret = true;
+ }
+ return ret;
+}
+
+bool BaseChannel::SetRtcpMux_w(bool enable, DescriptionType type,
+ DescriptionSource src) {
+ bool ret;
+ if (type == DT_OFFER) {
+ ret = rtcp_mux_filter_.SetOffer(enable, src);
+ } else if (type == DT_ANSWER) {
+ ret = rtcp_mux_filter_.SetAnswer(enable, src);
+ if (ret && rtcp_mux_filter_.IsActive()) {
+ // We activated RTCP mux, close down the RTCP transport.
+ set_rtcp_transport_channel(NULL);
+ // If the RTP transport is already writable, then so are we.
+ if (transport_channel_->writable()) {
+ ChannelWritable_w();
+ }
+ }
+ } else {
+ // DT_UPDATE, no RTCP mux info.
+ ret = true;
+ }
+ return ret;
+}
+
+void BaseChannel::OnMessage(talk_base::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_SETLOCALDESCRIPTION: {
+ SetDescriptionData* data = static_cast<SetDescriptionData*>(pmsg->pdata);
+ data->result = SetLocalDescription_w(data->desc, data->type);
+ break;
+ }
+ case MSG_SETREMOTEDESCRIPTION: {
+ SetDescriptionData* data = static_cast<SetDescriptionData*>(pmsg->pdata);
+ data->result = SetRemoteDescription_w(data->desc, data->type);
+ break;
+ }
+
+ case MSG_REMOVESTREAM: {
+ StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
+ RemoveStream_w(data->ssrc1);
+ break;
+ }
+
+ case MSG_SETMAXSENDBANDWIDTH: {
+ SetBandwidthData* data = static_cast<SetBandwidthData*>(pmsg->pdata);
+ data->result = SetMaxSendBandwidth_w(data->value);
+ break;
+ }
+ }
+}
+
+void BaseChannel::Send(uint32 id, talk_base::MessageData *pdata) {
+ worker_thread_->Send(this, id, pdata);
+}
+
+void BaseChannel::Post(uint32 id, talk_base::MessageData *pdata) {
+ worker_thread_->Post(this, id, pdata);
+}
+
+void BaseChannel::PostDelayed(int cmsDelay, uint32 id,
+ talk_base::MessageData *pdata) {
+ worker_thread_->PostDelayed(cmsDelay, this, id, pdata);
+}
+
+void BaseChannel::Clear(uint32 id, talk_base::MessageList* removed) {
+ worker_thread_->Clear(this, id, removed);
+}
+
+VoiceChannel::VoiceChannel(talk_base::Thread* thread,
+ MediaEngine* media_engine,
+ VoiceMediaChannel* media_channel,
+ BaseSession* session,
+ bool rtcp)
+ : BaseChannel(thread, media_engine, media_channel, session,
+ session->CreateChannel("rtp")),
+ received_media_(false) {
+ if (rtcp) {
+ set_rtcp_transport_channel(session->CreateChannel("rtcp"));
+ }
+}
+
+VoiceChannel::~VoiceChannel() {
+ StopAudioMonitor();
+ StopMediaMonitor();
+ // this can't be done in the base class, since it calls a virtual
+ DisableMedia_w();
+}
+
+bool VoiceChannel::AddStream(uint32 ssrc) {
+ StreamMessageData data(ssrc, 0);
+ Send(MSG_ADDSTREAM, &data);
+ return true;
+}
+
+bool VoiceChannel::SetRingbackTone(const void* buf, int len) {
+ SetRingbackToneMessageData data(buf, len);
+ Send(MSG_SETRINGBACKTONE, &data);
+ return true;
+}
+
+// TODO(juberti): Handle early media the right way. We should get an explicit
+// ringing message telling us to start playing local ringback, which we cancel
+// if any early media actually arrives. For now, we do the opposite, which is
+// to wait 1 second for early media, and start playing local ringback if none
+// arrives.
+void VoiceChannel::SetEarlyMedia(bool enable) {
+ if (enable) {
+ // Start the early media timeout
+ PostDelayed(kEarlyMediaTimeout, MSG_EARLYMEDIATIMEOUT);
+ } else {
+ // Stop the timeout if currently going.
+ Clear(MSG_EARLYMEDIATIMEOUT);
+ }
+}
+
+bool VoiceChannel::PlayRingbackTone(bool play, bool loop) {
+ PlayRingbackToneMessageData data(play, loop);
+ Send(MSG_PLAYRINGBACKTONE, &data);
+ return data.result;
+}
+
+bool VoiceChannel::PressDTMF(int digit, bool playout) {
+ DtmfMessageData data(digit, playout);
+ Send(MSG_PRESSDTMF, &data);
+ return data.result;
+}
+
+void VoiceChannel::StartMediaMonitor(int cms) {
+ media_monitor_.reset(new VoiceMediaMonitor(media_channel(), worker_thread(),
+ talk_base::Thread::Current()));
+ media_monitor_->SignalUpdate.connect(
+ this, &VoiceChannel::OnMediaMonitorUpdate);
+ media_monitor_->Start(cms);
+}
+
+void VoiceChannel::StopMediaMonitor() {
+ if (media_monitor_.get()) {
+ media_monitor_->Stop();
+ media_monitor_->SignalUpdate.disconnect(this);
+ media_monitor_.reset();
+ }
+}
+
+void VoiceChannel::StartAudioMonitor(int cms) {
+ audio_monitor_.reset(new AudioMonitor(this, talk_base::Thread::Current()));
+ audio_monitor_
+ ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate);
+ audio_monitor_->Start(cms);
+}
+
+void VoiceChannel::StopAudioMonitor() {
+ if (audio_monitor_.get()) {
+ audio_monitor_->Stop();
+ audio_monitor_.reset();
+ }
+}
+
+int VoiceChannel::GetInputLevel_w() {
+ return media_engine()->GetInputLevel();
+}
+
+int VoiceChannel::GetOutputLevel_w() {
+ return media_channel()->GetOutputLevel();
+}
+
+void VoiceChannel::GetActiveStreams_w(AudioInfo::StreamList* actives) {
+ media_channel()->GetActiveStreams(actives);
+}
+
+void VoiceChannel::OnChannelRead(TransportChannel* channel,
+ const char* data, size_t len) {
+ BaseChannel::OnChannelRead(channel, data, len);
+
+ // Set a flag when we've received an RTP packet. If we're waiting for early
+ // media, this will disable the timeout.
+ // If we were playing out our local ringback, make sure it is stopped to
+ // prevent it from interfering with the incoming media.
+ if (!received_media_) {
+ received_media_ = false;
+ PlayRingbackTone_w(false, false);
+ }
+}
+
+void VoiceChannel::ChangeState() {
+ // render incoming data if we are the active call
+ // we receive data on the default channel and multiplexed streams
+ bool recv = enabled();
+ media_channel()->SetPlayout(recv);
+
+ // send outgoing data if we are the active call, have the
+ // remote party's codec, and have a writable transport
+ // we only send data on the default channel
+ bool send = enabled() && has_codec() && writable();
+ media_channel()->SetSend(send ? SEND_MICROPHONE : SEND_NOTHING);
+
+ LOG(LS_INFO) << "Changing voice state, recv=" << recv << " send=" << send;
+}
+
+bool VoiceChannel::SetLocalDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ LOG(LS_INFO) << "Setting local voice description";
+
+ bool ret;
+ // set SRTP
+ ret = SetSrtp_w(desc.voice().cryptos(), type, DS_LOCAL);
+ // set RTCP mux
+ if (ret) {
+ ret = SetRtcpMux_w(desc.voice().rtcp_mux(), type, DS_LOCAL);
+ }
+ // set payload type and config for voice codecs
+ if (ret) {
+ ret = media_channel()->SetRecvCodecs(desc.voice().codecs());
+ }
+ return ret;
+}
+
+bool VoiceChannel::SetRemoteDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ LOG(LS_INFO) << "Setting remote voice description";
+
+ bool ret;
+ // set the sending SSRC, if the remote side gave us one
+ if (desc.voice().ssrc_set()) {
+ media_channel()->SetSendSsrc(desc.voice().ssrc());
+ }
+ // set SRTP
+ ret = SetSrtp_w(desc.voice().cryptos(), type, DS_REMOTE);
+ // set RTCP mux
+ if (ret) {
+ ret = SetRtcpMux_w(desc.voice().rtcp_mux(), type, DS_REMOTE);
+ }
+ // set codecs and payload types
+ if (ret) {
+ ret = media_channel()->SetSendCodecs(desc.voice().codecs());
+ }
+ // update state
+ if (ret) {
+ set_has_codec(true);
+ ChangeState();
+ }
+ return ret;
+}
+
+void VoiceChannel::AddStream_w(uint32 ssrc) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ media_channel()->AddStream(ssrc);
+}
+
+void VoiceChannel::RemoveStream_w(uint32 ssrc) {
+ media_channel()->RemoveStream(ssrc);
+}
+
+void VoiceChannel::SetRingbackTone_w(const void* buf, int len) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ media_channel()->SetRingbackTone(static_cast<const char*>(buf), len);
+}
+
+bool VoiceChannel::PlayRingbackTone_w(bool play, bool loop) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ if (play) {
+ LOG(LS_INFO) << "Playing ringback tone, loop=" << loop;
+ } else {
+ LOG(LS_INFO) << "Stopping ringback tone";
+ }
+ return media_channel()->PlayRingbackTone(play, loop);
+}
+
+void VoiceChannel::HandleEarlyMediaTimeout() {
+ // This occurs on the main thread, not the worker thread.
+ if (!received_media_) {
+ LOG(LS_INFO) << "No early media received before timeout";
+ SignalEarlyMediaTimeout(this);
+ }
+}
+
+bool VoiceChannel::PressDTMF_w(int digit, bool playout) {
+ if (!enabled() || !writable()) {
+ return false;
+ }
+
+ return media_channel()->PressDTMF(digit, playout);
+}
+
+void VoiceChannel::OnMessage(talk_base::Message *pmsg) {
+ switch (pmsg->message_id) {
+ case MSG_ADDSTREAM: {
+ StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
+ AddStream_w(data->ssrc1);
+ break;
+ }
+ case MSG_SETRINGBACKTONE: {
+ SetRingbackToneMessageData* data =
+ static_cast<SetRingbackToneMessageData*>(pmsg->pdata);
+ SetRingbackTone_w(data->buf, data->len);
+ break;
+ }
+ case MSG_PLAYRINGBACKTONE: {
+ PlayRingbackToneMessageData* data =
+ static_cast<PlayRingbackToneMessageData*>(pmsg->pdata);
+ data->result = PlayRingbackTone_w(data->play, data->loop);
+ break;
+ }
+ case MSG_EARLYMEDIATIMEOUT:
+ HandleEarlyMediaTimeout();
+ break;
+ case MSG_PRESSDTMF: {
+ DtmfMessageData* data = static_cast<DtmfMessageData*>(pmsg->pdata);
+ data->result = PressDTMF_w(data->digit, data->playout);
+ break;
+ }
+
+ default:
+ BaseChannel::OnMessage(pmsg);
+ break;
+ }
+}
+
+void VoiceChannel::OnConnectionMonitorUpdate(
+ SocketMonitor* monitor, const std::vector<ConnectionInfo>& infos) {
+ SignalConnectionMonitor(this, infos);
+}
+
+void VoiceChannel::OnMediaMonitorUpdate(
+ VoiceMediaChannel* media_channel, const VoiceMediaInfo& info) {
+ ASSERT(media_channel == this->media_channel());
+ SignalMediaMonitor(this, info);
+}
+
+void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor* monitor,
+ const AudioInfo& info) {
+ SignalAudioMonitor(this, info);
+}
+
+VideoChannel::VideoChannel(talk_base::Thread* thread,
+ MediaEngine* media_engine,
+ VideoMediaChannel* media_channel,
+ BaseSession* session, bool rtcp,
+ VoiceChannel* voice_channel)
+ : BaseChannel(thread, media_engine, media_channel, session,
+ session->CreateChannel("video_rtp")),
+ voice_channel_(voice_channel), renderer_(NULL) {
+ if (rtcp) {
+ set_rtcp_transport_channel(session->CreateChannel("video_rtcp"));
+ }
+}
+
+VideoChannel::~VideoChannel() {
+ StopMediaMonitor();
+ // this can't be done in the base class, since it calls a virtual
+ DisableMedia_w();
+}
+
+bool VideoChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) {
+ StreamMessageData data(ssrc, voice_ssrc);
+ Send(MSG_ADDSTREAM, &data);
+ return true;
+}
+
+bool VideoChannel::SetRenderer(uint32 ssrc, VideoRenderer* renderer) {
+ RenderMessageData data(ssrc, renderer);
+ Send(MSG_SETRENDERER, &data);
+ return true;
+}
+
+bool VideoChannel::AddScreencast(uint32 ssrc, talk_base::WindowId id) {
+ ScreencastMessageData data(ssrc, id);
+ Send(MSG_ADDSCREENCAST, &data);
+ return true;
+}
+
+bool VideoChannel::RemoveScreencast(uint32 ssrc) {
+ ScreencastMessageData data(ssrc, 0);
+ Send(MSG_REMOVESCREENCAST, &data);
+ return true;
+}
+
+void VideoChannel::ChangeState() {
+ // render incoming data if we are the active call
+ // we receive data on the default channel and multiplexed streams
+ bool recv = enabled();
+ media_channel()->SetRender(recv);
+
+ // send outgoing data if we are the active call, have the
+ // remote party's codec, and have a writable transport
+ // we only send data on the default channel
+ bool send = enabled() && has_codec() && writable();
+ media_channel()->SetSend(send);
+
+ LOG(LS_INFO) << "Changing video state, recv=" << recv << " send=" << send;
+}
+
+void VideoChannel::StartMediaMonitor(int cms) {
+ media_monitor_.reset(new VideoMediaMonitor(media_channel(), worker_thread(),
+ talk_base::Thread::Current()));
+ media_monitor_->SignalUpdate.connect(
+ this, &VideoChannel::OnMediaMonitorUpdate);
+ media_monitor_->Start(cms);
+}
+
+void VideoChannel::StopMediaMonitor() {
+ if (media_monitor_.get()) {
+ media_monitor_->Stop();
+ media_monitor_.reset();
+ }
+}
+
+bool VideoChannel::SetLocalDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ LOG(LS_INFO) << "Setting local video description";
+
+ bool ret;
+ // set SRTP
+ ret = SetSrtp_w(desc.video().cryptos(), type, DS_LOCAL);
+ // set RTCP mux
+ if (ret) {
+ ret = SetRtcpMux_w(desc.video().rtcp_mux(), type, DS_LOCAL);
+ }
+ // set payload types and config for receiving video
+ if (ret) {
+ ret = media_channel()->SetRecvCodecs(desc.video().codecs());
+ }
+ return ret;
+}
+
+bool VideoChannel::SetRemoteDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type) {
+ ASSERT(worker_thread() == talk_base::Thread::Current());
+ LOG(LS_INFO) << "Setting remote video description";
+
+ bool ret;
+ // set the sending SSRC, if the remote side gave us one
+ // TODO(juberti): remove this, since it's not needed.
+ if (desc.video().ssrc_set()) {
+ media_channel()->SetSendSsrc(desc.video().ssrc());
+ }
+ // set SRTP
+ ret = SetSrtp_w(desc.video().cryptos(), type, DS_REMOTE);
+ // set RTCP mux
+ if (ret) {
+ ret = SetRtcpMux_w(desc.video().rtcp_mux(), type, DS_REMOTE);
+ }
+ // TODO(juberti): Set bandwidth appropriately here.
+ if (ret) {
+ ret = media_channel()->SetSendCodecs(desc.video().codecs());
+ }
+ media_channel()->SetRtpExtensionHeaders(!desc.video().rtp_headers_disabled());
+ if (ret) {
+ set_has_codec(true);
+ ChangeState();
+ }
+ return ret;
+}
+
+void VideoChannel::AddStream_w(uint32 ssrc, uint32 voice_ssrc) {
+ media_channel()->AddStream(ssrc, voice_ssrc);
+}
+
+void VideoChannel::RemoveStream_w(uint32 ssrc) {
+ media_channel()->RemoveStream(ssrc);
+}
+
+void VideoChannel::SetRenderer_w(uint32 ssrc, VideoRenderer* renderer) {
+ media_channel()->SetRenderer(ssrc, renderer);
+}
+
+void VideoChannel::AddScreencast_w(uint32 ssrc, talk_base::WindowId id) {
+ media_channel()->AddScreencast(ssrc, id);
+}
+
+void VideoChannel::RemoveScreencast_w(uint32 ssrc) {
+ media_channel()->RemoveScreencast(ssrc);
+}
+
+void VideoChannel::OnMessage(talk_base::Message *pmsg) {
+ switch (pmsg->message_id) {
+ case MSG_ADDSTREAM: {
+ StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
+ AddStream_w(data->ssrc1, data->ssrc2);
+ break;
+ }
+ case MSG_SETRENDERER: {
+ RenderMessageData* data = static_cast<RenderMessageData*>(pmsg->pdata);
+ SetRenderer_w(data->ssrc, data->renderer);
+ break;
+ }
+ case MSG_ADDSCREENCAST: {
+ ScreencastMessageData* data =
+ static_cast<ScreencastMessageData*>(pmsg->pdata);
+ AddScreencast_w(data->ssrc, data->window_id);
+ break;
+ }
+ case MSG_REMOVESCREENCAST: {
+ ScreencastMessageData* data =
+ static_cast<ScreencastMessageData*>(pmsg->pdata);
+ RemoveScreencast_w(data->ssrc);
+ break;
+ }
+ default:
+ BaseChannel::OnMessage(pmsg);
+ break;
+ }
+}
+
+void VideoChannel::OnConnectionMonitorUpdate(
+ SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos) {
+ SignalConnectionMonitor(this, infos);
+}
+
+void VideoChannel::OnMediaMonitorUpdate(
+ VideoMediaChannel* media_channel, const VideoMediaInfo &info) {
+ ASSERT(media_channel == this->media_channel());
+ SignalMediaMonitor(this, info);
+}
+
+// TODO(juberti): Move to own file in a future CL.
+// Leaving here for now to avoid having to mess with the Mac build.
+RtcpMuxFilter::RtcpMuxFilter() : state_(ST_INIT), offer_enable_(false) {
+}
+
+bool RtcpMuxFilter::IsActive() const {
+ // We can receive muxed media prior to the accept, so we have to be able to
+ // deal with that.
+ return (state_ == ST_SENTOFFER || state_ == ST_ACTIVE);
+}
+
+bool RtcpMuxFilter::SetOffer(bool offer_enable, DescriptionSource source) {
+ bool ret = false;
+ if (state_ == ST_INIT) {
+ offer_enable_ = offer_enable;
+ state_ = (source == DS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER;
+ ret = true;
+ } else {
+ LOG(LS_ERROR) << "Invalid state for RTCP mux offer";
+ }
+ return ret;
+}
+
+bool RtcpMuxFilter::SetAnswer(bool answer_enable, DescriptionSource source) {
+ bool ret = false;
+ if ((state_ == ST_SENTOFFER && source == DS_REMOTE) ||
+ (state_ == ST_RECEIVEDOFFER && source == DS_LOCAL)) {
+ if (offer_enable_) {
+ state_ = (answer_enable) ? ST_ACTIVE : ST_INIT;
+ ret = true;
+ } else {
+ // If the offer didn't specify RTCP mux, the answer shouldn't either.
+ if (!answer_enable) {
+ ret = true;
+ state_ = ST_INIT;
+ } else {
+ LOG(LS_WARNING) << "Invalid parameters in RTCP mux answer";
+ }
+ }
+ } else {
+ LOG(LS_ERROR) << "Invalid state for RTCP mux answer";
+ }
+ return ret;
+}
+
+bool RtcpMuxFilter::DemuxRtcp(const char* data, int len) {
+ // If we're muxing RTP/RTCP, we must inspect each packet delivered and
+ // determine whether it is RTP or RTCP. We do so by checking the packet type,
+ // and assuming RTP if type is 0-63 or 96-127. For additional details, see
+ // http://tools.ietf.org/html/draft-ietf-avt-rtp-and-rtcp-mux-07.
+ // Note that if we offer RTCP mux, we may receive muxed RTCP before we
+ // receive the answer, so we operate in that state too.
+ if (!IsActive()) {
+ return false;
+ }
+
+ int type = (len >= 2) ? (static_cast<uint8>(data[1]) & 0x7F) : 0;
+ return (type >= 64 && type < 96);
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/channel.h b/third_party/libjingle/source/talk/session/phone/channel.h
new file mode 100644
index 0000000..85e5074
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/channel.h
@@ -0,0 +1,400 @@
+/*
+ * libjingle
+ * Copyright 2004--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CHANNEL_H_
+#define TALK_SESSION_PHONE_CHANNEL_H_
+
+#include <vector>
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/network.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/windowpicker.h"
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/p2p/base/session.h"
+#include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/mediaengine.h"
+#include "talk/session/phone/mediachannel.h"
+#include "talk/session/phone/mediamonitor.h"
+#include "talk/session/phone/srtpfilter.h"
+
+namespace cricket {
+
+class MediaSessionDescription;
+struct CryptoParams;
+
+enum {
+ MSG_ENABLE = 1,
+ MSG_DISABLE = 2,
+ MSG_MUTE = 3,
+ MSG_UNMUTE = 4,
+ MSG_SETREMOTEDESCRIPTION = 5,
+ MSG_SETLOCALDESCRIPTION = 6,
+ MSG_EARLYMEDIATIMEOUT = 8,
+ MSG_PRESSDTMF = 9,
+ MSG_SETRENDERER = 10,
+ MSG_ADDSTREAM = 11,
+ MSG_REMOVESTREAM = 12,
+ MSG_SETRINGBACKTONE = 13,
+ MSG_PLAYRINGBACKTONE = 14,
+ MSG_SETMAXSENDBANDWIDTH = 15,
+ MSG_ADDSCREENCAST = 16,
+ MSG_REMOVESCREENCAST = 17
+};
+
+// TODO(juberti): Move to own file.
+class RtcpMuxFilter {
+ public:
+ RtcpMuxFilter();
+
+ // Whether the filter is active, i.e. has RTCP mux been properly negotiated.
+ bool IsActive() const;
+
+ // Specifies whether the offer indicates the use of RTCP mux.
+ bool SetOffer(bool offer_enable, DescriptionSource src);
+
+ // Specifies whether the answer indicates the use of RTCP mux.
+ bool SetAnswer(bool answer_enable, DescriptionSource src);
+
+ // Determines whether the specified packet is RTCP.
+ bool DemuxRtcp(const char* data, int len);
+
+ private:
+ enum State { ST_INIT, ST_SENTOFFER, ST_RECEIVEDOFFER, ST_ACTIVE };
+ State state_;
+ bool offer_enable_;
+};
+
+// BaseChannel contains logic common to voice and video, including
+// enable/mute, marshaling calls to a worker thread, and
+// connection and media monitors.
+// TODO(juberti): Break the dependency on BaseSession. The only thing we need
+// it for is to Create/Destroy TransportChannels, and set codecs, both of which
+// could be done by the calling class.
+class BaseChannel
+ : public talk_base::MessageHandler, public sigslot::has_slots<>,
+ public MediaChannel::NetworkInterface {
+ public:
+ BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine,
+ MediaChannel* channel, BaseSession* session,
+ TransportChannel* transport);
+ ~BaseChannel();
+
+ talk_base::Thread* worker_thread() const { return worker_thread_; }
+ BaseSession* session() const { return session_; }
+ TransportChannel* transport_channel() const {
+ return transport_channel_;
+ }
+ TransportChannel* rtcp_transport_channel() const {
+ return rtcp_transport_channel_;
+ }
+ bool secure() const { return srtp_filter_.IsActive(); }
+
+ // Channel control
+ bool SetLocalDescription(const MediaSessionDescription& desc,
+ DescriptionType type);
+ bool SetRemoteDescription(const MediaSessionDescription& desc,
+ DescriptionType type);
+ bool SetMaxSendBandwidth(int max_bandwidth);
+
+ bool Enable(bool enable);
+ bool Mute(bool mute);
+
+ // Multiplexing
+ bool RemoveStream(uint32 ssrc);
+
+ // Monitoring
+ void StartConnectionMonitor(int cms);
+ void StopConnectionMonitor();
+
+ protected:
+ MediaEngine* media_engine() const { return media_engine_; }
+ virtual MediaChannel* media_channel() const { return media_channel_; }
+ void set_rtcp_transport_channel(TransportChannel* transport);
+ bool enabled() const { return enabled_; }
+ bool writable() const { return writable_; }
+ bool has_codec() const { return has_codec_; }
+ void set_has_codec(bool has_codec) { has_codec_ = has_codec; }
+ bool muted() const { return muted_; }
+
+ void Send(uint32 id, talk_base::MessageData *pdata = NULL);
+ void Post(uint32 id, talk_base::MessageData *pdata = NULL);
+ void PostDelayed(int cmsDelay, uint32 id = 0,
+ talk_base::MessageData *pdata = NULL);
+ void Clear(uint32 id = talk_base::MQID_ANY,
+ talk_base::MessageList* removed = NULL);
+
+ // NetworkInterface implementation, called by MediaEngine
+ virtual int SendPacket(const void *data, size_t len);
+ virtual int SendRtcp(const void *data, size_t len);
+ virtual int SetOption(SocketType type, talk_base::Socket::Option o, int val);
+
+ // From TransportChannel
+ void OnWritableState(TransportChannel* channel);
+ void OnChannelRead(TransportChannel* channel, const char *data, size_t len);
+
+ int SendPacket(bool rtcp, const void* data, size_t len);
+ void HandlePacket(bool rtcp, const char* data, size_t len);
+
+ // Setting the send codec based on the remote description.
+ void OnSessionState(BaseSession* session, BaseSession::State state);
+
+ void EnableMedia_w();
+ void DisableMedia_w();
+ void MuteMedia_w();
+ void UnmuteMedia_w();
+ void ChannelWritable_w();
+ void ChannelNotWritable_w();
+
+ struct StreamMessageData : public talk_base::MessageData {
+ StreamMessageData(uint32 s1, uint32 s2) : ssrc1(s1), ssrc2(s2) {}
+ uint32 ssrc1;
+ uint32 ssrc2;
+ };
+ virtual void RemoveStream_w(uint32 ssrc) = 0;
+
+ virtual void ChangeState() = 0;
+ struct SetDescriptionData : public talk_base::MessageData {
+ SetDescriptionData(const MediaSessionDescription& desc,
+ DescriptionType type)
+ : desc(desc), type(type), result(false) {}
+ const MediaSessionDescription& desc;
+ DescriptionType type;
+ bool result;
+ };
+ virtual bool SetLocalDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type) = 0;
+ virtual bool SetRemoteDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type) = 0;
+
+ bool SetSrtp_w(const std::vector<CryptoParams>& params, DescriptionType type,
+ DescriptionSource src);
+ bool SetRtcpMux_w(bool enable, DescriptionType type, DescriptionSource src);
+
+ struct SetBandwidthData : public talk_base::MessageData {
+ explicit SetBandwidthData(int value) : value(value), result(false) {}
+ int value;
+ bool result;
+ };
+ bool SetMaxSendBandwidth_w(int max_bandwidth);
+
+ // From MessageHandler
+ virtual void OnMessage(talk_base::Message *pmsg);
+
+ // Handled in derived classes
+ virtual void OnConnectionMonitorUpdate(SocketMonitor *monitor,
+ const std::vector<ConnectionInfo> &infos) = 0;
+
+ private:
+ talk_base::Thread *worker_thread_;
+ MediaEngine *media_engine_;
+ BaseSession *session_;
+ MediaChannel *media_channel_;
+ TransportChannel *transport_channel_;
+ TransportChannel *rtcp_transport_channel_;
+ SrtpFilter srtp_filter_;
+ RtcpMuxFilter rtcp_mux_filter_;
+ talk_base::scoped_ptr<SocketMonitor> socket_monitor_;
+ bool enabled_;
+ bool writable_;
+ bool has_codec_;
+ bool muted_;
+};
+
+// VoiceChannel is a specialization that adds support for early media, DTMF,
+// and input/output level monitoring.
+class VoiceChannel : public BaseChannel {
+ public:
+ VoiceChannel(talk_base::Thread *thread, MediaEngine *media_engine,
+ VoiceMediaChannel *channel, BaseSession *session, bool rtcp);
+ ~VoiceChannel();
+
+ // downcasts a MediaChannel
+ virtual VoiceMediaChannel* media_channel() const {
+ return static_cast<VoiceMediaChannel*>(BaseChannel::media_channel());
+ }
+
+ // Add an incoming stream with the specified SSRC.
+ bool AddStream(uint32 ssrc);
+
+ bool SetRingbackTone(const void* buf, int len);
+ void SetEarlyMedia(bool enable);
+ // This signal is emitted when we have gone a period of time without
+ // receiving early media. When received, a UI should start playing its
+ // own ringing sound
+ sigslot::signal1<VoiceChannel*> SignalEarlyMediaTimeout;
+
+ bool PlayRingbackTone(bool play, bool loop);
+ bool PressDTMF(int digit, bool playout);
+
+ // Monitoring functions
+ sigslot::signal2<VoiceChannel*, const std::vector<ConnectionInfo> &>
+ SignalConnectionMonitor;
+
+ void StartMediaMonitor(int cms);
+ void StopMediaMonitor();
+ sigslot::signal2<VoiceChannel*, const VoiceMediaInfo&> SignalMediaMonitor;
+
+ void StartAudioMonitor(int cms);
+ void StopAudioMonitor();
+ sigslot::signal2<VoiceChannel*, const AudioInfo&> SignalAudioMonitor;
+
+ int GetInputLevel_w();
+ int GetOutputLevel_w();
+ void GetActiveStreams_w(AudioInfo::StreamList* actives);
+
+ private:
+ struct SetRingbackToneMessageData : public talk_base::MessageData {
+ SetRingbackToneMessageData(const void* b, int l)
+ : buf(b),
+ len(l) {
+ }
+ const void* buf;
+ int len;
+ };
+ struct PlayRingbackToneMessageData : public talk_base::MessageData {
+ PlayRingbackToneMessageData(bool p, bool l)
+ : play(p),
+ loop(l),
+ result(false) {
+ }
+ bool play;
+ bool loop;
+ bool result;
+ };
+ struct DtmfMessageData : public talk_base::MessageData {
+ DtmfMessageData(int d, bool p)
+ : digit(d),
+ playout(p),
+ result(false) {
+ }
+ int digit;
+ bool playout;
+ bool result;
+ };
+
+ // overrides from BaseChannel
+ virtual void OnChannelRead(TransportChannel* channel,
+ const char *data, size_t len);
+ virtual void ChangeState();
+ virtual bool SetLocalDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type);
+ virtual bool SetRemoteDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type);
+
+ void AddStream_w(uint32 ssrc);
+ void RemoveStream_w(uint32 ssrc);
+
+ void SetRingbackTone_w(const void* buf, int len);
+ bool PlayRingbackTone_w(bool play, bool loop);
+ void HandleEarlyMediaTimeout();
+ bool PressDTMF_w(int digit, bool playout);
+
+ virtual void OnMessage(talk_base::Message *pmsg);
+ virtual void OnConnectionMonitorUpdate(
+ SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos);
+ virtual void OnMediaMonitorUpdate(
+ VoiceMediaChannel *media_channel, const VoiceMediaInfo& info);
+ void OnAudioMonitorUpdate(AudioMonitor *monitor, const AudioInfo& info);
+
+ static const int kEarlyMediaTimeout = 1000;
+ bool received_media_;
+ talk_base::scoped_ptr<VoiceMediaMonitor> media_monitor_;
+ talk_base::scoped_ptr<AudioMonitor> audio_monitor_;
+};
+
+// VideoChannel is a specialization for video.
+class VideoChannel : public BaseChannel {
+ public:
+ VideoChannel(talk_base::Thread *thread, MediaEngine *media_engine,
+ VideoMediaChannel *channel, BaseSession *session, bool rtcp,
+ VoiceChannel *voice_channel);
+ ~VideoChannel();
+
+ // downcasts a MediaChannel
+ virtual VideoMediaChannel* media_channel() const {
+ return static_cast<VideoMediaChannel*>(BaseChannel::media_channel());
+ }
+
+ // Add an incoming stream with the specified SSRC.
+ bool AddStream(uint32 ssrc, uint32 voice_ssrc);
+
+ bool SetRenderer(uint32 ssrc, VideoRenderer* renderer);
+
+ bool AddScreencast(uint32 ssrc, talk_base::WindowId id);
+ bool RemoveScreencast(uint32 ssrc);
+
+ sigslot::signal2<VideoChannel*, const std::vector<ConnectionInfo> &>
+ SignalConnectionMonitor;
+
+ void StartMediaMonitor(int cms);
+ void StopMediaMonitor();
+ sigslot::signal2<VideoChannel*, const VideoMediaInfo&> SignalMediaMonitor;
+
+ private:
+ // overrides from BaseChannel
+ virtual void ChangeState();
+ virtual bool SetLocalDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type);
+ virtual bool SetRemoteDescription_w(const MediaSessionDescription& desc,
+ DescriptionType type);
+
+ void AddStream_w(uint32 ssrc, uint32 voice_ssrc);
+ void RemoveStream_w(uint32 ssrc);
+
+ struct RenderMessageData : public talk_base::MessageData {
+ RenderMessageData(uint32 s, VideoRenderer* r) : ssrc(s), renderer(r) {}
+ uint32 ssrc;
+ VideoRenderer* renderer;
+ };
+
+ struct ScreencastMessageData : public talk_base::MessageData {
+ ScreencastMessageData(uint32 s, talk_base::WindowId id)
+ : ssrc(s), window_id(id) {}
+ uint32 ssrc;
+ talk_base::WindowId window_id;
+ };
+
+ void SetRenderer_w(uint32 ssrc, VideoRenderer* renderer);
+
+ void AddScreencast_w(uint32 ssrc, talk_base::WindowId);
+ void RemoveScreencast_w(uint32 ssrc);
+
+ virtual void OnMessage(talk_base::Message *pmsg);
+ virtual void OnConnectionMonitorUpdate(
+ SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos);
+ virtual void OnMediaMonitorUpdate(
+ VideoMediaChannel *media_channel, const VideoMediaInfo& info);
+
+ VoiceChannel *voice_channel_;
+ VideoRenderer *renderer_;
+ talk_base::scoped_ptr<VideoMediaMonitor> media_monitor_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_CHANNEL_H_
diff --git a/third_party/libjingle/source/talk/session/phone/channelmanager.cc b/third_party/libjingle/source/talk/session/phone/channelmanager.cc
new file mode 100644
index 0000000..92957bf
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/channelmanager.cc
@@ -0,0 +1,752 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/channelmanager.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <algorithm>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/sigslotrepeater.h"
+#include "talk/base/stringencode.h"
+#include "talk/session/phone/mediaengine.h"
+#include "talk/session/phone/soundclip.h"
+#ifdef USE_TALK_SOUND
+#include "talk/sound/platformsoundsystemfactory.h"
+#include "talk/sound/soundsysteminterface.h"
+#endif
+
+namespace cricket {
+
+enum {
+ MSG_CREATEVOICECHANNEL = 1,
+ MSG_DESTROYVOICECHANNEL = 2,
+ MSG_SETAUDIOOPTIONS = 3,
+ MSG_SETOUTPUTVOLUME = 4,
+ MSG_SETLOCALMONITOR = 5,
+ MSG_SETVOICELOGGING = 6,
+ MSG_CREATEVIDEOCHANNEL = 11,
+ MSG_DESTROYVIDEOCHANNEL = 12,
+ MSG_SETVIDEOOPTIONS = 13,
+ MSG_SETLOCALRENDERER = 14,
+ MSG_SETDEFAULTVIDEOCODEC = 15,
+ MSG_SETVIDEOLOGGING = 16,
+ MSG_CREATESOUNDCLIP = 17,
+ MSG_DESTROYSOUNDCLIP = 18,
+ MSG_CAMERASTARTED = 19,
+ MSG_SETVIDEOCAPTURE = 20,
+};
+
+struct CreationParams : public talk_base::MessageData {
+ CreationParams(BaseSession* s, bool r, VoiceChannel* c)
+ : transport_factory(s), rtcp(r), voice_channel(c),
+ video_channel(NULL) {}
+ BaseSession* transport_factory;
+ bool rtcp;
+ VoiceChannel* voice_channel;
+ VideoChannel* video_channel;
+};
+
+struct AudioOptions : public talk_base::MessageData {
+ AudioOptions(int o, const Device* in, const Device* out)
+ : options(o), in_device(in), out_device(out) {}
+ int options;
+ const Device* in_device;
+ const Device* out_device;
+ bool result;
+};
+
+struct VolumeLevel : public talk_base::MessageData {
+ explicit VolumeLevel(int l) : level(l), result(false) {}
+ int level;
+ bool result;
+};
+
+struct VideoOptions : public talk_base::MessageData {
+ explicit VideoOptions(const Device* d) : cam_device(d), result(false) {}
+ const Device* cam_device;
+ bool result;
+};
+
+struct DefaultVideoCodec : public talk_base::MessageData {
+ explicit DefaultVideoCodec(const VideoCodec& c) : codec(c), result(false) {}
+ VideoCodec codec;
+ bool result;
+};
+
+struct LocalMonitor : public talk_base::MessageData {
+ explicit LocalMonitor(bool e) : enable(e), result(false) {}
+ bool enable;
+ bool result;
+};
+
+struct LocalRenderer : public talk_base::MessageData {
+ explicit LocalRenderer(VideoRenderer* r) : renderer(r), result(false) {}
+ VideoRenderer* renderer;
+ bool result;
+};
+
+struct LoggingOptions : public talk_base::MessageData {
+ explicit LoggingOptions(int lev, const char* f) : level(lev), filter(f) {}
+ int level;
+ std::string filter;
+};
+
+struct CaptureParams : public talk_base::MessageData {
+ explicit CaptureParams(bool c) : capture(c), result(MediaEngine::CR_FAILURE) {
+ }
+ bool capture;
+ MediaEngine::CaptureResult result;
+};
+
+ChannelManager::ChannelManager(talk_base::Thread* worker_thread)
+ :
+#ifdef USE_TALK_SOUND
+ sound_system_factory_(new PlatformSoundSystemFactory()),
+#endif
+ media_engine_(MediaEngine::Create(
+#ifdef USE_TALK_SOUND
+ sound_system_factory_.get()
+#endif
+ )),
+ device_manager_(new DeviceManager(
+#ifdef USE_TALK_SOUND
+ sound_system_factory_.get()
+#endif
+ )),
+ initialized_(false), main_thread_(talk_base::Thread::Current()),
+ worker_thread_(NULL), audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS),
+ capturing_(false),
+ monitoring_(false) {
+ Construct(worker_thread);
+}
+
+ChannelManager::ChannelManager(MediaEngine* me, DeviceManager* dm,
+ talk_base::Thread* worker_thread)
+ :
+#ifdef USE_TALK_SOUND
+ sound_system_factory_(NULL),
+#endif
+ media_engine_(me), device_manager_(dm),
+ initialized_(false), main_thread_(talk_base::Thread::Current()),
+ worker_thread_(NULL), audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS),
+ capturing_(false),
+ monitoring_(false) {
+ Construct(worker_thread);
+}
+
+void ChannelManager::Construct(talk_base::Thread* worker_thread) {
+ // Init the device manager immediately, and set up our default video device.
+ SignalDevicesChange.repeat(device_manager_->SignalDevicesChange);
+ device_manager_->Init();
+ SetVideoOptions("");
+
+ // Camera is started asynchronously, request callbacks when startup
+ // completes to be able to forward them to the rendering manager.
+ media_engine_->SignalVideoCaptureResult.connect(
+ this, &ChannelManager::OnVideoCaptureResult);
+
+ // If we're given a worker thread, init the media engine right away.
+ if (worker_thread)
+ Init(worker_thread);
+}
+
+ChannelManager::~ChannelManager() {
+ if (initialized_)
+ Terminate();
+}
+
+int ChannelManager::GetCapabilities() {
+ return media_engine_->GetCapabilities() & device_manager_->GetCapabilities();
+}
+
+void ChannelManager::GetSupportedCodecs(std::vector<Codec>* codecs) const {
+ codecs->clear();
+
+ for (std::vector<Codec>::const_iterator it = media_engine_->codecs().begin();
+ it != media_engine_->codecs().end(); ++it) {
+ codecs->push_back(*it);
+ }
+}
+
+void ChannelManager::GetSupportedVideoCodecs(
+ std::vector<VideoCodec>* codecs) const {
+ codecs->clear();
+
+ std::vector<VideoCodec>::const_iterator it;
+ for (it = media_engine_->video_codecs().begin();
+ it != media_engine_->video_codecs().end(); ++it) {
+ codecs->push_back(*it);
+ }
+}
+
+bool ChannelManager::Init(talk_base::Thread* worker_thread) {
+ ASSERT(!initialized_);
+ if (initialized_) {
+ return false;
+ }
+
+ ASSERT(worker_thread != NULL);
+ if (worker_thread && worker_thread->started()) {
+ if (media_engine_->Init()) {
+ worker_thread_ = worker_thread;
+ initialized_ = true;
+
+ // Now that we're initialized, apply any stored preferences.
+ if (!SetAudioOptions(audio_in_device_, audio_out_device_,
+ audio_options_)) {
+ audio_in_device_.clear();
+ audio_out_device_.clear();
+ }
+ if (!SetVideoOptions(camera_device_)) {
+ // TODO(juberti): Consider resetting to the default cam here.
+ camera_device_.clear();
+ }
+ // Now apply the default video codec that has been set earlier.
+ if (default_video_codec_.id != 0) {
+ SetDefaultVideoCodec(default_video_codec_);
+ }
+ }
+ }
+ return initialized_;
+}
+
+void ChannelManager::Terminate() {
+ ASSERT(initialized_);
+ if (!initialized_) {
+ return;
+ }
+
+ // Need to destroy the voice/video channels
+ while (!video_channels_.empty()) {
+ DestroyVideoChannel_w(video_channels_.back());
+ }
+ while (!voice_channels_.empty()) {
+ DestroyVoiceChannel_w(voice_channels_.back());
+ }
+ while (!soundclips_.empty()) {
+ DestroySoundclip_w(soundclips_.back());
+ }
+
+ media_engine_->Terminate();
+ initialized_ = false;
+ worker_thread_ = NULL;
+}
+
+VoiceChannel* ChannelManager::CreateVoiceChannel(BaseSession* session,
+ bool rtcp) {
+ CreationParams params(session, rtcp, NULL);
+ return (Send(MSG_CREATEVOICECHANNEL, &params)) ? params.voice_channel : NULL;
+}
+
+VoiceChannel* ChannelManager::CreateVoiceChannel_w(BaseSession* session,
+ bool rtcp) {
+ talk_base::CritScope cs(&crit_);
+
+ // This is ok to alloc from a thread other than the worker thread
+ ASSERT(initialized_);
+ VoiceMediaChannel* media_channel = media_engine_->CreateChannel();
+ if (media_channel == NULL)
+ return NULL;
+
+ VoiceChannel* voice_channel = new VoiceChannel(worker_thread_,
+ media_engine_.get(),
+ media_channel, session, rtcp);
+ voice_channels_.push_back(voice_channel);
+ return voice_channel;
+}
+
+void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) {
+ if (voice_channel) {
+ talk_base::TypedMessageData<VoiceChannel *> data(voice_channel);
+ Send(MSG_DESTROYVOICECHANNEL, &data);
+ }
+}
+
+void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) {
+ talk_base::CritScope cs(&crit_);
+ // Destroy voice channel.
+ ASSERT(initialized_);
+ VoiceChannels::iterator it = std::find(voice_channels_.begin(),
+ voice_channels_.end(), voice_channel);
+ ASSERT(it != voice_channels_.end());
+ if (it == voice_channels_.end())
+ return;
+
+ voice_channels_.erase(it);
+ delete voice_channel;
+}
+
+VideoChannel* ChannelManager::CreateVideoChannel(BaseSession* session,
+ bool rtcp,
+ VoiceChannel* voice_channel) {
+ CreationParams params(session, rtcp, voice_channel);
+ return (Send(MSG_CREATEVIDEOCHANNEL, &params)) ? params.video_channel : NULL;
+}
+
+VideoChannel* ChannelManager::CreateVideoChannel_w(BaseSession* session,
+ bool rtcp,
+ VoiceChannel* voice_channel) {
+ talk_base::CritScope cs(&crit_);
+
+ // This is ok to alloc from a thread other than the worker thread
+ ASSERT(initialized_);
+ VideoMediaChannel* media_channel =
+ // voice_channel can be NULL in case of NullVoiceEngine.
+ media_engine_->CreateVideoChannel(voice_channel ?
+ voice_channel->media_channel() : NULL);
+ if (media_channel == NULL)
+ return NULL;
+
+ VideoChannel* video_channel = new VideoChannel(worker_thread_,
+ media_engine_.get(),
+ media_channel,
+ session, rtcp, voice_channel);
+ video_channels_.push_back(video_channel);
+ return video_channel;
+}
+
+void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) {
+ if (video_channel) {
+ talk_base::TypedMessageData<VideoChannel *> data(video_channel);
+ Send(MSG_DESTROYVIDEOCHANNEL, &data);
+ }
+}
+
+void ChannelManager::DestroyVideoChannel_w(VideoChannel *video_channel) {
+ talk_base::CritScope cs(&crit_);
+ // Destroy voice channel.
+ ASSERT(initialized_);
+ VideoChannels::iterator it = std::find(video_channels_.begin(),
+ video_channels_.end(), video_channel);
+ ASSERT(it != video_channels_.end());
+ if (it == video_channels_.end())
+ return;
+
+ video_channels_.erase(it);
+ delete video_channel;
+}
+
+Soundclip* ChannelManager::CreateSoundclip() {
+ talk_base::TypedMessageData<Soundclip*> data(NULL);
+ Send(MSG_CREATESOUNDCLIP, &data);
+ return data.data();
+}
+
+Soundclip* ChannelManager::CreateSoundclip_w() {
+ talk_base::CritScope cs(&crit_);
+
+ ASSERT(initialized_);
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+
+ SoundclipMedia* soundclip_media = media_engine_->CreateSoundclip();
+ if (!soundclip_media) {
+ return NULL;
+ }
+
+ Soundclip* soundclip = new Soundclip(worker_thread_, soundclip_media);
+ soundclips_.push_back(soundclip);
+ return soundclip;
+}
+
+void ChannelManager::DestroySoundclip(Soundclip* soundclip) {
+ if (soundclip) {
+ talk_base::TypedMessageData<Soundclip*> data(soundclip);
+ Send(MSG_DESTROYSOUNDCLIP, &data);
+ }
+}
+
+void ChannelManager::DestroySoundclip_w(Soundclip* soundclip) {
+ talk_base::CritScope cs(&crit_);
+ // Destroy soundclip.
+ ASSERT(initialized_);
+ Soundclips::iterator it = std::find(soundclips_.begin(),
+ soundclips_.end(), soundclip);
+ ASSERT(it != soundclips_.end());
+ if (it == soundclips_.end())
+ return;
+
+ soundclips_.erase(it);
+ delete soundclip;
+}
+
+bool ChannelManager::GetAudioOptions(std::string* in_name,
+ std::string* out_name, int* opts) {
+ *in_name = audio_in_device_;
+ *out_name = audio_out_device_;
+ *opts = audio_options_;
+ return true;
+}
+
+bool ChannelManager::SetAudioOptions(const std::string& in_name,
+ const std::string& out_name, int opts) {
+ // Get device ids from DeviceManager.
+ Device in_dev, out_dev;
+ if (!device_manager_->GetAudioInputDevice(in_name, &in_dev) ||
+ !device_manager_->GetAudioOutputDevice(out_name, &out_dev)) {
+ LOG(LS_WARNING) << "Device manager can't find selected device";
+ return false;
+ }
+
+ // If we're initialized, pass the settings to the media engine.
+ bool ret = true;
+ if (initialized_) {
+ AudioOptions options(opts, &in_dev, &out_dev);
+ ret = (Send(MSG_SETAUDIOOPTIONS, &options) && options.result);
+ }
+
+ // If all worked well, save the values for use in GetAudioOptions.
+ if (ret) {
+ audio_options_ = opts;
+ audio_in_device_ = in_name;
+ audio_out_device_ = out_name;
+ }
+ return ret;
+}
+
+bool ChannelManager::SetAudioOptions_w(int opts, const Device* in_dev,
+ const Device* out_dev) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(initialized_);
+
+ // Set audio options
+ bool ret = media_engine_->SetAudioOptions(opts);
+
+ // Set the audio devices
+ if (ret) {
+ // Need to grab the critsection for this because SetSoundDevices in GIPS
+ // relies on a list of channels and our Terminate() method destroys channels
+ // from a different thread.
+ talk_base::CritScope cs(&crit_);
+ ret = media_engine_->SetSoundDevices(in_dev, out_dev);
+ }
+
+ return ret;
+}
+
+bool ChannelManager::SetOutputVolume(int level) {
+ VolumeLevel volume(level);
+ return (Send(MSG_SETOUTPUTVOLUME, &volume) && volume.result);
+}
+
+bool ChannelManager::SetOutputVolume_w(int level) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(initialized_);
+ return media_engine_->SetOutputVolume(level);
+}
+
+bool ChannelManager::GetVideoOptions(std::string* cam_name) {
+ *cam_name = camera_device_;
+ return true;
+}
+
+bool ChannelManager::SetVideoOptions(const std::string& cam_name) {
+ bool ret;
+ Device device;
+
+ if (cam_name.empty()) {
+ // If we're passed the default device name, get the default device.
+ ret = device_manager_->GetDefaultVideoCaptureDevice(&device);
+ } else {
+ // Convert the camera name to a device, fail if it can't be found.
+ std::vector<Device> devices;
+ ret = device_manager_->GetVideoCaptureDevices(&devices);
+ if (ret) {
+ for (size_t i = 0; i < devices.size(); ++i) {
+ if (devices[i].name == cam_name) {
+ device = devices[i];
+ break;
+ }
+ }
+ ret = !device.name.empty();
+ }
+ }
+
+ // If we're running, tell the media engine about it.
+ if (ret && initialized_) {
+ VideoOptions options(&device);
+ ret = (Send(MSG_SETVIDEOOPTIONS, &options) && options.result);
+ }
+
+ // If everything worked, retain the name of the selected camera.
+ if (ret) {
+ camera_device_ = device.name;
+ }
+ return ret;
+}
+
+bool ChannelManager::SetVideoOptions_w(const Device* cam_device) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(initialized_);
+
+ // Set the video input device
+ return media_engine_->SetVideoCaptureDevice(cam_device);
+}
+
+bool ChannelManager::SetDefaultVideoCodec(const VideoCodec& c) {
+ bool ret = true;
+ if (initialized_) {
+ DefaultVideoCodec codec(c);
+ ret = Send(MSG_SETDEFAULTVIDEOCODEC, &codec) && codec.result;
+ }
+ if (ret) {
+ default_video_codec_ = c;
+ }
+ return ret;
+}
+
+bool ChannelManager::SetDefaultVideoCodec_w(const VideoCodec& c) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(initialized_);
+ return media_engine_->SetDefaultVideoCodec(c);
+}
+
+bool ChannelManager::SetLocalMonitor(bool enable) {
+ LocalMonitor monitor(enable);
+ bool ret = Send(MSG_SETLOCALMONITOR, &monitor) && monitor.result;
+ if (ret) {
+ monitoring_ = enable;
+ }
+ return ret;
+}
+
+bool ChannelManager::SetLocalMonitor_w(bool enable) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(initialized_);
+ return media_engine_->SetLocalMonitor(enable);
+}
+
+bool ChannelManager::SetLocalRenderer(VideoRenderer* renderer) {
+ bool ret;
+ LocalRenderer capture(renderer);
+ ret = (Send(MSG_SETLOCALRENDERER, &capture) && capture.result);
+ if (ret) {
+ capturing_ = (renderer != NULL);
+ }
+ return ret;
+}
+
+bool ChannelManager::SetLocalRenderer_w(VideoRenderer* renderer) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(initialized_);
+ return media_engine_->SetLocalRenderer(renderer);
+}
+
+MediaEngine::CaptureResult ChannelManager::SetVideoCapture(bool capture) {
+ bool ret;
+ CaptureParams capture_params(capture);
+ ret = (Send(MSG_SETVIDEOCAPTURE, &capture_params) &&
+ (capture_params.result != MediaEngine::CR_FAILURE));
+ if (ret) {
+ capturing_ = capture;
+ }
+ return capture_params.result;
+}
+
+MediaEngine::CaptureResult ChannelManager::SetVideoCapture_w(bool capture) {
+ ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(initialized_);
+ return media_engine_->SetVideoCapture(capture);
+}
+
+void ChannelManager::SetVoiceLogging(int level, const char* filter) {
+ SetMediaLogging(false, level, filter);
+}
+
+void ChannelManager::SetVideoLogging(int level, const char* filter) {
+ SetMediaLogging(true, level, filter);
+}
+
+void ChannelManager::SetMediaLogging(bool video, int level,
+ const char* filter) {
+ // Can be called before initialization; in this case, the worker function
+ // is simply called on the main thread.
+ if (initialized_) {
+ LoggingOptions options(level, filter);
+ Send((video) ? MSG_SETVIDEOLOGGING : MSG_SETVOICELOGGING, &options);
+ } else {
+ SetMediaLogging_w(video, level, filter);
+ }
+}
+
+void ChannelManager::SetMediaLogging_w(bool video, int level,
+ const char* filter) {
+ // Can be called before initialization
+ ASSERT(worker_thread_ == talk_base::Thread::Current() || !initialized_);
+ if (video) {
+ media_engine_->SetVideoLogging(level, filter);
+ } else {
+ media_engine_->SetVoiceLogging(level, filter);
+ }
+}
+
+bool ChannelManager::Send(uint32 id, talk_base::MessageData* data) {
+ if (!worker_thread_) return false;
+ worker_thread_->Send(this, id, data);
+ return true;
+}
+
+void ChannelManager::OnVideoCaptureResult(bool result) {
+ capturing_ = result;
+ main_thread_->Post(this, MSG_CAMERASTARTED,
+ new talk_base::TypedMessageData<bool>(result));
+}
+
+void ChannelManager::OnMessage(talk_base::Message* message) {
+ talk_base::MessageData* data = message->pdata;
+ switch (message->message_id) {
+ case MSG_CREATEVOICECHANNEL: {
+ CreationParams* p = static_cast<CreationParams*>(data);
+ p->voice_channel = CreateVoiceChannel_w(p->transport_factory, p->rtcp);
+ break;
+ }
+ case MSG_DESTROYVOICECHANNEL: {
+ VoiceChannel* p = static_cast<talk_base::TypedMessageData<VoiceChannel*>*>
+ (data)->data();
+ DestroyVoiceChannel_w(p);
+ break;
+ }
+ case MSG_CREATEVIDEOCHANNEL: {
+ CreationParams* p = static_cast<CreationParams*>(data);
+ p->video_channel = CreateVideoChannel_w(p->transport_factory,
+ p->rtcp, p->voice_channel);
+ break;
+ }
+ case MSG_DESTROYVIDEOCHANNEL: {
+ VideoChannel* p = static_cast<talk_base::TypedMessageData<VideoChannel*>*>
+ (data)->data();
+ DestroyVideoChannel_w(p);
+ break;
+ }
+ case MSG_CREATESOUNDCLIP: {
+ talk_base::TypedMessageData<Soundclip*> *p =
+ static_cast<talk_base::TypedMessageData<Soundclip*>*>(data);
+ p->data() = CreateSoundclip_w();
+ break;
+ }
+ case MSG_DESTROYSOUNDCLIP: {
+ talk_base::TypedMessageData<Soundclip*> *p =
+ static_cast<talk_base::TypedMessageData<Soundclip*>*>(data);
+ DestroySoundclip_w(p->data());
+ break;
+ }
+ case MSG_SETAUDIOOPTIONS: {
+ AudioOptions* p = static_cast<AudioOptions*>(data);
+ p->result = SetAudioOptions_w(p->options,
+ p->in_device, p->out_device);
+ break;
+ }
+ case MSG_SETOUTPUTVOLUME: {
+ VolumeLevel* p = static_cast<VolumeLevel*>(data);
+ p->result = SetOutputVolume_w(p->level);
+ break;
+ }
+ case MSG_SETLOCALMONITOR: {
+ LocalMonitor* p = static_cast<LocalMonitor*>(data);
+ p->result = SetLocalMonitor_w(p->enable);
+ break;
+ }
+ case MSG_SETVIDEOOPTIONS: {
+ VideoOptions* p = static_cast<VideoOptions*>(data);
+ p->result = SetVideoOptions_w(p->cam_device);
+ break;
+ }
+ case MSG_SETDEFAULTVIDEOCODEC: {
+ DefaultVideoCodec* p = static_cast<DefaultVideoCodec*>(data);
+ p->result = SetDefaultVideoCodec_w(p->codec);
+ break;
+ }
+ case MSG_SETLOCALRENDERER: {
+ LocalRenderer* p = static_cast<LocalRenderer*>(data);
+ p->result = SetLocalRenderer_w(p->renderer);
+ break;
+ }
+ case MSG_SETVIDEOCAPTURE: {
+ CaptureParams* p = static_cast<CaptureParams*>(data);
+ p->result = SetVideoCapture_w(p->capture);
+ break;
+ }
+ case MSG_SETVOICELOGGING:
+ case MSG_SETVIDEOLOGGING: {
+ LoggingOptions* p = static_cast<LoggingOptions*>(data);
+ bool video = (message->message_id == MSG_SETVIDEOLOGGING);
+ SetMediaLogging_w(video, p->level, p->filter.c_str());
+ break;
+ }
+ case MSG_CAMERASTARTED: {
+ talk_base::TypedMessageData<bool> *data =
+ static_cast<talk_base::TypedMessageData<bool>*>(message->pdata);
+ SignalVideoCaptureResult(data->data());
+ delete data;
+ break;
+ }
+ }
+}
+
+static void GetDeviceNames(const std::vector<Device>& devs,
+ std::vector<std::string>* names) {
+ names->clear();
+ for (size_t i = 0; i < devs.size(); ++i) {
+ names->push_back(devs[i].name);
+ }
+}
+
+bool ChannelManager::GetAudioInputDevices(std::vector<std::string>* names) {
+ names->clear();
+ std::vector<Device> devs;
+ bool ret = device_manager_->GetAudioInputDevices(&devs);
+ if (ret)
+ GetDeviceNames(devs, names);
+
+ return ret;
+}
+
+bool ChannelManager::GetAudioOutputDevices(std::vector<std::string>* names) {
+ names->clear();
+ std::vector<Device> devs;
+ bool ret = device_manager_->GetAudioOutputDevices(&devs);
+ if (ret)
+ GetDeviceNames(devs, names);
+
+ return ret;
+}
+
+bool ChannelManager::GetVideoCaptureDevices(std::vector<std::string>* names) {
+ names->clear();
+ std::vector<Device> devs;
+ bool ret = device_manager_->GetVideoCaptureDevices(&devs);
+ if (ret)
+ GetDeviceNames(devs, names);
+
+ return ret;
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/channelmanager.h b/third_party/libjingle/source/talk/session/phone/channelmanager.h
new file mode 100644
index 0000000..e0d8dec
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/channelmanager.h
@@ -0,0 +1,200 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CHANNELMANAGER_H_
+#define TALK_SESSION_PHONE_CHANNELMANAGER_H_
+
+#include <vector>
+
+#include "talk/base/criticalsection.h"
+#include "talk/base/sigslotrepeater.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/session.h"
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/mediaengine.h"
+#include "talk/session/phone/devicemanager.h"
+#ifdef USE_TALK_SOUND
+#include "talk/sound/soundsystemfactory.h"
+#endif
+
+namespace cricket {
+
+class Soundclip;
+class VoiceChannel;
+
+// ChannelManager allows the MediaEngine to run on a separate thread, and takes
+// care of marshalling calls between threads. It also creates and keeps track of
+// voice and video channels; by doing so, it can temporarily pause all the
+// channels when a new audio or video device is chosen. The voice and video
+// channels are stored in separate vectors, to easily allow operations on just
+// voice or just video channels.
+// ChannelManager also allows the application to discover what devices it has
+// using device manager.
+class ChannelManager : public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+ public:
+ // Creates the channel manager, and initializes it if the thread is not NULL.
+ explicit ChannelManager(talk_base::Thread* worker);
+ // For testing purposes. Allows the media engine and dev manager to be mocks.
+ // The ChannelManager takes ownership of these objects.
+ ChannelManager(MediaEngine* me, DeviceManager* dm, talk_base::Thread* worker);
+ ~ChannelManager();
+
+ // Gets capabilities. Can be called prior to starting the media engine.
+ int GetCapabilities();
+
+ // Retrieves the list of supported audio & video codec types.
+ // Can be called before starting the media engine.
+ void GetSupportedCodecs(std::vector<Codec>* codecs) const;
+ void GetSupportedVideoCodecs(std::vector<VideoCodec>* codecs) const;
+
+ // Determines if a specific audio or video codec is supported.
+ // Can be called before starting the media engine.
+ bool FindCodec(const Codec& codec) const {
+ return media_engine_->FindCodec(codec);
+ }
+ bool FindVideoCodec(const VideoCodec& video_codec) const {
+ return media_engine_->FindVideoCodec(video_codec);
+ }
+
+ // Indicates whether the media engine is started.
+ bool initialized() const { return initialized_; }
+ talk_base::Thread* worker_thread() const { return worker_thread_; }
+ // Starts up the media engine.
+ bool Init(talk_base::Thread* worker_thread);
+ // Shuts down the media engine.
+ void Terminate();
+
+ // The operations below all occur on the worker thread.
+
+ // Creates a voice channel, to be associated with the specified session.
+ VoiceChannel* CreateVoiceChannel(BaseSession* session, bool rtcp);
+ // Destroys a voice channel created with the Create API.
+ void DestroyVoiceChannel(VoiceChannel* voice_channel);
+ // Creates a video channel, synced with the specified voice channel, and
+ // associated with the specified session.
+ VideoChannel* CreateVideoChannel(BaseSession* session, bool rtcp,
+ VoiceChannel* voice_channel);
+ // Destroys a video channel created with the Create API.
+ void DestroyVideoChannel(VideoChannel* video_channel);
+
+ // Creates a soundclip.
+ Soundclip* CreateSoundclip();
+ // Destroys a soundclip created with the Create API.
+ void DestroySoundclip(Soundclip* soundclip);
+
+ // Indicates whether any channels exist.
+ bool has_channels() const {
+ return (!voice_channels_.empty() || !video_channels_.empty() ||
+ !soundclips_.empty());
+ }
+
+ // Configures the audio and video devices.
+ bool GetAudioOptions(std::string* wave_in_device,
+ std::string* wave_out_device, int* opts);
+ bool SetAudioOptions(const std::string& wave_in_device,
+ const std::string& wave_out_device, int opts);
+ bool SetOutputVolume(int level);
+ bool GetVideoOptions(std::string* cam_device);
+ bool SetVideoOptions(const std::string& cam_device);
+ bool SetDefaultVideoCodec(const VideoCodec& codec);
+
+ // Starts/stops the local microphone and enables polling of the input level.
+ bool SetLocalMonitor(bool enable);
+ bool monitoring() const { return monitoring_; }
+ // Sets the local renderer where to renderer the local camera.
+ bool SetLocalRenderer(VideoRenderer* renderer);
+ // Starts and stops the local camera and renders it to the local renderer.
+ MediaEngine::CaptureResult SetVideoCapture(bool capture);
+ bool capturing() const { return capturing_; }
+
+ // Configures the logging output of the mediaengine(s).
+ void SetVoiceLogging(int level, const char* filter);
+ void SetVideoLogging(int level, const char* filter);
+
+ // The operations below occur on the main thread.
+
+ bool GetAudioInputDevices(std::vector<std::string>* names);
+ bool GetAudioOutputDevices(std::vector<std::string>* names);
+ bool GetVideoCaptureDevices(std::vector<std::string>* names);
+ sigslot::repeater0<> SignalDevicesChange;
+ sigslot::signal1<bool> SignalVideoCaptureResult;
+
+ private:
+ typedef std::vector<VoiceChannel*> VoiceChannels;
+ typedef std::vector<VideoChannel*> VideoChannels;
+ typedef std::vector<Soundclip*> Soundclips;
+
+ void Construct(talk_base::Thread* worker_thread);
+ bool Send(uint32 id, talk_base::MessageData* pdata);
+ VoiceChannel* CreateVoiceChannel_w(BaseSession* session, bool rtcp);
+ void DestroyVoiceChannel_w(VoiceChannel* voice_channel);
+ VideoChannel* CreateVideoChannel_w(BaseSession* session, bool rtcp,
+ VoiceChannel* voice_channel);
+ void DestroyVideoChannel_w(VideoChannel* video_channel);
+ Soundclip* CreateSoundclip_w();
+ void DestroySoundclip_w(Soundclip* soundclip);
+ bool SetAudioOptions_w(int opts, const Device* in_dev,
+ const Device* out_dev);
+ bool SetOutputVolume_w(int level);
+ bool SetLocalMonitor_w(bool enable);
+ bool SetVideoOptions_w(const Device* cam_device);
+ bool SetDefaultVideoCodec_w(const VideoCodec& codec);
+ bool SetLocalRenderer_w(VideoRenderer* renderer);
+ MediaEngine::CaptureResult SetVideoCapture_w(bool capture);
+ void SetMediaLogging(bool video, int level, const char* filter);
+ void SetMediaLogging_w(bool video, int level, const char* filter);
+ void OnVideoCaptureResult(bool result);
+ void OnMessage(talk_base::Message *message);
+
+ talk_base::CriticalSection crit_;
+#ifdef USE_TALK_SOUND
+ talk_base::scoped_ptr<SoundSystemFactory> sound_system_factory_;
+#endif
+ talk_base::scoped_ptr<MediaEngine> media_engine_;
+ talk_base::scoped_ptr<DeviceManager> device_manager_;
+ bool initialized_;
+ talk_base::Thread* main_thread_;
+ talk_base::Thread* worker_thread_;
+
+ VoiceChannels voice_channels_;
+ VideoChannels video_channels_;
+ Soundclips soundclips_;
+
+ std::string audio_in_device_;
+ std::string audio_out_device_;
+ int audio_options_;
+ std::string camera_device_;
+ VideoCodec default_video_codec_;
+
+ bool capturing_;
+ bool monitoring_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_CHANNELMANAGER_H_
diff --git a/third_party/libjingle/source/talk/session/phone/codec.cc b/third_party/libjingle/source/talk/session/phone/codec.cc
new file mode 100644
index 0000000..7c6f5a6
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/codec.cc
@@ -0,0 +1,59 @@
+/*
+ * libjingle
+ * Copyright 2004--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/codec.h"
+#include <sstream>
+
+namespace cricket {
+
+static const int kMaxStaticPayloadId = 95;
+
+bool Codec::Matches(int payload, const std::string& nm) const {
+ return (id <= kMaxStaticPayloadId && id == payload) ||
+ (id > kMaxStaticPayloadId && name == nm);
+}
+
+std::string Codec::ToString() const {
+ std::ostringstream os;
+ os << "Codec[" << id << ":" << name << ":" << clockrate << ":" << bitrate
+ << ":" << channels << ":" << preference << "]";
+ return os.str();
+}
+
+bool VideoCodec::Matches(int payload, const std::string& nm) const {
+ return (id <= kMaxStaticPayloadId && id == payload) ||
+ (id > kMaxStaticPayloadId && name == nm);
+}
+
+std::string VideoCodec::ToString() const {
+ std::ostringstream os;
+ os << "VideoCodec[" << id << ":" << name << ":" << width << ":" << height
+ << ":" << framerate << ":" << preference << "]";
+ return os.str();
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/codec.h b/third_party/libjingle/source/talk/session/phone/codec.h
new file mode 100644
index 0000000..fdebbbe
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/codec.h
@@ -0,0 +1,90 @@
+/*
+ * libjingle
+ * Copyright 2004--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CODEC_H_
+#define TALK_SESSION_PHONE_CODEC_H_
+
+#include <string>
+
+namespace cricket {
+
+struct Codec {
+ int id;
+ std::string name;
+ int clockrate;
+ int bitrate;
+ int channels;
+
+ int preference;
+
+ // Creates a codec with the given parameters.
+ Codec(int pt, const std::string& nm, int cr, int br, int cs, int pr)
+ : id(pt), name(nm), clockrate(cr), bitrate(br),
+ channels(cs), preference(pr) {}
+
+ // Creates an empty codec.
+ Codec() : id(0), clockrate(0), bitrate(0), channels(0), preference(0) {}
+
+ bool Matches(int payload, const std::string& nm) const;
+
+ static bool Preferable(const Codec& first, const Codec& other) {
+ return first.preference > other.preference;
+ }
+
+ std::string ToString() const;
+};
+
+struct VideoCodec {
+ int id;
+ std::string name;
+ int width;
+ int height;
+ int framerate;
+
+ int preference;
+
+ // Creates a codec with the given parameters.
+ VideoCodec(int pt, const std::string& nm, int w, int h, int fr, int pr)
+ : id(pt), name(nm), width(w), height(h), framerate(fr), preference(pr) {}
+
+ // Creates an empty codec.
+ VideoCodec()
+ : id(0), width(0), height(0), framerate(0), preference(0) {}
+
+ bool Matches(int payload, const std::string& nm) const;
+
+ static bool Preferable(const VideoCodec& first, const VideoCodec& other) {
+ return first.preference > other.preference;
+ }
+
+ std::string ToString() const;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_CODEC_H_
+
diff --git a/third_party/libjingle/source/talk/session/phone/cryptoparams.h b/third_party/libjingle/source/talk/session/phone/cryptoparams.h
new file mode 100644
index 0000000..8891659
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/cryptoparams.h
@@ -0,0 +1,54 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CRYPTOPARAMS_H_
+#define TALK_SESSION_PHONE_CRYPTOPARAMS_H_
+
+#include <string>
+
+namespace cricket {
+
+// Parameters for SRTP negotiation, as described in RFC 4586.
+struct CryptoParams {
+ CryptoParams() : tag(0) {}
+ CryptoParams(int t, const std::string& cs,
+ const std::string& kp, const std::string& sp)
+ : tag(t), cipher_suite(cs), key_params(kp), session_params(sp) {}
+
+ bool Matches(const CryptoParams& params) const {
+ return (tag == params.tag && cipher_suite == params.cipher_suite);
+ }
+
+ int tag;
+ std::string cipher_suite;
+ std::string key_params;
+ std::string session_params;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_CRYPTOPARAMS_H_
diff --git a/third_party/libjingle/source/talk/session/phone/devicemanager.cc b/third_party/libjingle/source/talk/session/phone/devicemanager.cc
new file mode 100644
index 0000000..3e98e0a
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/devicemanager.cc
@@ -0,0 +1,885 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/devicemanager.h"
+
+#if WIN32
+#include <atlbase.h>
+#include <dbt.h>
+#include <strmif.h> // must come before ks.h
+#include <ks.h>
+#include <ksmedia.h>
+#define INITGUID // For PKEY_AudioEndpoint_GUID
+#include <mmdeviceapi.h>
+#include <functiondiscoverykeys_devpkey.h>
+#include <uuids.h>
+#include "talk/base/win32.h" // ToUtf8
+#include "talk/base/win32window.h"
+#elif OSX
+#include <CoreAudio/CoreAudio.h>
+#include <QuickTime/QuickTime.h>
+#elif LINUX
+#include <unistd.h>
+#ifndef USE_TALK_SOUND
+#include <alsa/asoundlib.h>
+#endif
+#include "talk/base/linux.h"
+#include "talk/base/fileutils.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/stream.h"
+#include "talk/session/phone/v4llookup.h"
+#endif
+
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/session/phone/mediaengine.h"
+#if USE_TALK_SOUND
+#include "talk/sound/platformsoundsystem.h"
+#include "talk/sound/sounddevicelocator.h"
+#include "talk/sound/soundsysteminterface.h"
+#endif
+
+namespace cricket {
+// Initialize to empty string.
+const std::string DeviceManager::kDefaultDeviceName;
+
+#if WIN32
+class DeviceWatcher : public talk_base::Win32Window {
+ public:
+ explicit DeviceWatcher(DeviceManager* dm);
+ bool Start();
+ void Stop();
+ private:
+ HDEVNOTIFY Register(REFGUID guid);
+ void Unregister(HDEVNOTIFY notify);
+ virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result);
+ DeviceManager* manager_;
+ HDEVNOTIFY audio_notify_;
+ HDEVNOTIFY video_notify_;
+};
+#else
+// TODO(juberti): Implement this for other platforms.
+class DeviceWatcher {
+ public:
+ explicit DeviceWatcher(DeviceManager* dm) {}
+ bool Start() { return true; }
+ void Stop() {}
+};
+#endif
+
+#ifndef LINUX
+static bool ShouldDeviceBeIgnored(const std::string& device_name);
+#endif
+static bool GetVideoDevices(std::vector<Device>* out);
+#if WIN32
+static const wchar_t kFriendlyName[] = L"FriendlyName";
+static const wchar_t kDevicePath[] = L"DevicePath";
+static const char kUsbDevicePathPrefix[] = "\\\\?\\usb";
+static bool GetDevices(const CLSID& catid, std::vector<Device>* out);
+static bool GetCoreAudioDevices(bool input, std::vector<Device>* devs);
+static bool GetWaveDevices(bool input, std::vector<Device>* devs);
+#elif OSX
+static const UInt32 kAudioDeviceNameLength = 64;
+static const int kVideoDeviceOpenAttempts = 3;
+static bool GetAudioDeviceIDs(bool inputs, std::vector<AudioDeviceID>* out);
+static bool GetAudioDeviceName(AudioDeviceID id, bool input, std::string* out);
+#endif
+
+DeviceManager::DeviceManager(
+#ifdef USE_TALK_SOUND
+ SoundSystemFactory *factory
+#endif
+ )
+ : initialized_(false),
+ watcher_(new DeviceWatcher(this))
+#ifdef USE_TALK_SOUND
+ , sound_system_(factory)
+#endif
+ {
+}
+
+DeviceManager::~DeviceManager() {
+ if (initialized_) {
+ Terminate();
+ }
+ delete watcher_;
+}
+
+bool DeviceManager::Init() {
+ if (!initialized_) {
+ if (!watcher_->Start()) {
+ return false;
+ }
+ initialized_ = true;
+ }
+ return true;
+}
+
+void DeviceManager::Terminate() {
+ if (initialized_) {
+ watcher_->Stop();
+ initialized_ = false;
+ }
+}
+
+int DeviceManager::GetCapabilities() {
+ std::vector<Device> devices;
+ int caps = MediaEngine::VIDEO_RECV;
+ if (GetAudioInputDevices(&devices) && !devices.empty()) {
+ caps |= MediaEngine::AUDIO_SEND;
+ }
+ if (GetAudioOutputDevices(&devices) && !devices.empty()) {
+ caps |= MediaEngine::AUDIO_RECV;
+ }
+ if (GetVideoCaptureDevices(&devices) && !devices.empty()) {
+ caps |= MediaEngine::VIDEO_SEND;
+ }
+ return caps;
+}
+
+bool DeviceManager::GetAudioInputDevices(std::vector<Device>* devices) {
+ return GetAudioDevicesByPlatform(true, devices);
+}
+
+bool DeviceManager::GetAudioOutputDevices(std::vector<Device>* devices) {
+ return GetAudioDevicesByPlatform(false, devices);
+}
+
+bool DeviceManager::GetAudioInputDevice(const std::string& name, Device* out) {
+ return GetAudioDevice(true, name, out);
+}
+
+bool DeviceManager::GetAudioOutputDevice(const std::string& name, Device* out) {
+ return GetAudioDevice(false, name, out);
+}
+
+bool DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
+ return GetVideoDevices(devices);
+}
+
+bool DeviceManager::GetDefaultVideoCaptureDevice(Device* device) {
+ bool ret = false;
+#if WIN32
+ // If there are multiple capture devices, we want the first USB one.
+ // This avoids issues with defaulting to virtual cameras or grabber cards.
+ std::vector<Device> devices;
+ ret = (GetVideoDevices(&devices) && !devices.empty());
+ if (ret) {
+ *device = devices[0];
+ for (size_t i = 0; i < devices.size(); ++i) {
+ if (strnicmp(devices[i].id.c_str(), kUsbDevicePathPrefix,
+ ARRAY_SIZE(kUsbDevicePathPrefix) - 1) == 0) {
+ *device = devices[i];
+ break;
+ }
+ }
+ }
+#else
+ // We just return the first device.
+ std::vector<Device> devices;
+ ret = (GetVideoCaptureDevices(&devices) && !devices.empty());
+ if (ret) {
+ *device = devices[0];
+ }
+#endif
+ return ret;
+}
+
+
+bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name,
+ Device* out) {
+ // If the name is empty, return the default device id.
+ if (name.empty() || name == kDefaultDeviceName) {
+ *out = Device(name, -1);
+ return true;
+ }
+
+ std::vector<Device> devices;
+ bool ret = is_input ? GetAudioInputDevices(&devices) :
+ GetAudioOutputDevices(&devices);
+ if (ret) {
+ ret = false;
+ for (size_t i = 0; i < devices.size(); ++i) {
+ if (devices[i].name == name) {
+ *out = devices[i];
+ ret = true;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+bool DeviceManager::GetAudioDevicesByPlatform(bool input,
+ std::vector<Device>* devs) {
+ devs->clear();
+#if defined(USE_TALK_SOUND)
+ if (!sound_system_.get()) {
+ return false;
+ }
+ SoundSystemInterface::SoundDeviceLocatorList list;
+ bool success;
+ if (input) {
+ success = sound_system_->EnumerateCaptureDevices(&list);
+ } else {
+ success = sound_system_->EnumeratePlaybackDevices(&list);
+ }
+ if (!success) {
+ LOG(LS_ERROR) << "Can't enumerate devices";
+ sound_system_.release();
+ return false;
+ }
+ int index = 0;
+ for (SoundSystemInterface::SoundDeviceLocatorList::iterator i = list.begin();
+ i != list.end();
+ ++i, ++index) {
+ devs->push_back(Device((*i)->name(), index));
+ }
+ SoundSystemInterface::ClearSoundDeviceLocatorList(&list);
+ sound_system_.release();
+ return true;
+
+#elif defined(WIN32)
+ if (talk_base::IsWindowsVistaOrLater()) {
+ return GetCoreAudioDevices(input, devs);
+ } else {
+ return GetWaveDevices(input, devs);
+ }
+
+#elif defined(OSX)
+ std::vector<AudioDeviceID> dev_ids;
+ bool ret = GetAudioDeviceIDs(input, &dev_ids);
+ if (ret) {
+ for (size_t i = 0; i < dev_ids.size(); ++i) {
+ std::string name;
+ if (GetAudioDeviceName(dev_ids[i], input, &name)) {
+ devs->push_back(Device(name, dev_ids[i]));
+ }
+ }
+ }
+ return ret;
+
+#elif defined(LINUX)
+ int card = -1, dev = -1;
+ snd_ctl_t *handle = NULL;
+ snd_pcm_info_t *pcminfo = NULL;
+
+ snd_pcm_info_malloc(&pcminfo);
+
+ while (true) {
+ if (snd_card_next(&card) != 0 || card < 0)
+ break;
+
+ char *card_name;
+ if (snd_card_get_name(card, &card_name) != 0)
+ continue;
+
+ char card_string[7];
+ snprintf(card_string, sizeof(card_string), "hw:%d", card);
+ if (snd_ctl_open(&handle, card_string, 0) != 0)
+ continue;
+
+ while (true) {
+ if (snd_ctl_pcm_next_device(handle, &dev) < 0 || dev < 0)
+ break;
+ snd_pcm_info_set_device(pcminfo, dev);
+ snd_pcm_info_set_subdevice(pcminfo, 0);
+ snd_pcm_info_set_stream(pcminfo, input ? SND_PCM_STREAM_CAPTURE :
+ SND_PCM_STREAM_PLAYBACK);
+ if (snd_ctl_pcm_info(handle, pcminfo) != 0)
+ continue;
+
+ char name[128];
+ talk_base::sprintfn(name, sizeof(name), "%s (%s)", card_name,
+ snd_pcm_info_get_name(pcminfo));
+ // TODO(tschmelcher): We might want to identify devices with something
+ // more specific than just their card number (e.g., the PCM names that
+ // aplay -L prints out).
+ devs->push_back(Device(name, card));
+
+ LOG(LS_INFO) << "Found device: id = " << card << ", name = "
+ << name;
+ }
+ snd_ctl_close(handle);
+ }
+ snd_pcm_info_free(pcminfo);
+ return true;
+#else
+ return false;
+#endif
+}
+
+#if defined(WIN32)
+bool GetVideoDevices(std::vector<Device>* devices) {
+ // TODO(juberti): Move the CoInit stuff to Initialize/Terminate.
+ HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ if (FAILED(hr)) {
+ LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr;
+ if (hr != RPC_E_CHANGED_MODE) {
+ return false;
+ }
+ }
+
+ bool ret = GetDevices(CLSID_VideoInputDeviceCategory, devices);
+ if (SUCCEEDED(hr)) {
+ CoUninitialize();
+ }
+ return ret;
+}
+
+bool GetDevices(const CLSID& catid, std::vector<Device>* devices) {
+ HRESULT hr;
+
+ // CComPtr is a scoped pointer that will be auto released when going
+ // out of scope. CoUninitialize must not be called before the
+ // release.
+ CComPtr<ICreateDevEnum> sys_dev_enum;
+ CComPtr<IEnumMoniker> cam_enum;
+ if (FAILED(hr = sys_dev_enum.CoCreateInstance(CLSID_SystemDeviceEnum)) ||
+ FAILED(hr = sys_dev_enum->CreateClassEnumerator(catid, &cam_enum, 0))) {
+ LOG(LS_ERROR) << "Failed to create device enumerator, hr=" << hr;
+ return false;
+ }
+
+ // Only enum devices if CreateClassEnumerator returns S_OK. If there are no
+ // devices available, S_FALSE will be returned, but enumMk will be NULL.
+ if (hr == S_OK) {
+ CComPtr<IMoniker> mk;
+ while (cam_enum->Next(1, &mk, NULL) == S_OK) {
+ CComPtr<IPropertyBag> bag;
+ if (SUCCEEDED(mk->BindToStorage(NULL, NULL,
+ __uuidof(bag), reinterpret_cast<void**>(&bag)))) {
+ CComVariant name, path;
+ std::string name_str, path_str;
+ if (SUCCEEDED(bag->Read(kFriendlyName, &name, 0)) &&
+ name.vt == VT_BSTR) {
+ name_str = talk_base::ToUtf8(name.bstrVal);
+ if (!ShouldDeviceBeIgnored(name_str)) {
+ // Get the device id if one exists.
+ if (SUCCEEDED(bag->Read(kDevicePath, &path, 0)) &&
+ path.vt == VT_BSTR) {
+ path_str = talk_base::ToUtf8(path.bstrVal);
+ }
+
+ devices->push_back(Device(name_str, path_str));
+ }
+ }
+ }
+ mk = NULL;
+ }
+ }
+
+ return true;
+}
+
+// Adapted from http://msdn.microsoft.com/en-us/library/dd370812(v=VS.85).aspx
+HRESULT CricketDeviceFromImmDevice(IMMDevice* device, Device* out) {
+ CComPtr<IPropertyStore> props;
+ PROPVARIANT name, guid;
+
+ HRESULT hr = device->OpenPropertyStore(STGM_READ, &props);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Get the endpoint's name and id.
+ PropVariantInit(&name);
+ hr = props->GetValue(PKEY_Device_FriendlyName, &name);
+ if (SUCCEEDED(hr)) {
+ PropVariantInit(&guid);
+ hr = props->GetValue(PKEY_AudioEndpoint_GUID, &guid);
+
+ if (SUCCEEDED(hr)) {
+ out->name = talk_base::ToUtf8(name.pwszVal);
+ out->id = talk_base::ToUtf8(guid.pwszVal);
+ }
+ PropVariantClear(&guid);
+ }
+ PropVariantClear(&name);
+ return hr;
+}
+
+bool GetCoreAudioDevices(bool input, std::vector<Device>* devs) {
+ HRESULT hr = S_OK;
+ CComPtr<IMMDeviceEnumerator> enumerator;
+
+ hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
+ __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&enumerator));
+ if (SUCCEEDED(hr)) {
+ CComPtr<IMMDeviceCollection> devices;
+ hr = enumerator->EnumAudioEndpoints((input ? eCapture : eRender),
+ DEVICE_STATE_ACTIVE, &devices);
+ if (SUCCEEDED(hr)) {
+ unsigned int count;
+ hr = devices->GetCount(&count);
+
+ if (SUCCEEDED(hr)) {
+ for (unsigned int i = 0; i < count; i++) {
+ CComPtr<IMMDevice> device;
+
+ // Get pointer to endpoint number i.
+ hr = devices->Item(i, &device);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ Device dev;
+ hr = CricketDeviceFromImmDevice(device, &dev);
+ if (SUCCEEDED(hr)) {
+ devs->push_back(dev);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!SUCCEEDED(hr)) {
+ LOG(LS_WARNING) << "GetCoreAudioDevices failed with hr " << hr;
+ return false;
+ }
+ return true;
+}
+
+bool GetWaveDevices(bool input, std::vector<Device>* devs) {
+ // Note, we don't use the System Device Enumerator interface here since it
+ // adds lots of pseudo-devices to the list, such as DirectSound and Wave
+ // variants of the same device.
+ if (input) {
+ int num_devs = waveInGetNumDevs();
+ for (int i = 0; i < num_devs; ++i) {
+ WAVEINCAPS caps;
+ if (waveInGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
+ caps.wChannels > 0) {
+ devs->push_back(Device(talk_base::ToUtf8(caps.szPname),
+ talk_base::ToString(i)));
+ }
+ }
+ } else {
+ int num_devs = waveOutGetNumDevs();
+ for (int i = 0; i < num_devs; ++i) {
+ WAVEOUTCAPS caps;
+ if (waveOutGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
+ caps.wChannels > 0) {
+ devs->push_back(Device(talk_base::ToUtf8(caps.szPname), i));
+ }
+ }
+ }
+ return true;
+}
+
+DeviceWatcher::DeviceWatcher(DeviceManager* manager)
+ : manager_(manager), audio_notify_(NULL), video_notify_(NULL) {
+}
+
+bool DeviceWatcher::Start() {
+ if (!Create(NULL, _T("libjingle DeviceWatcher Window"),
+ 0, 0, 0, 0, 0, 0)) {
+ return false;
+ }
+
+ audio_notify_ = Register(KSCATEGORY_AUDIO);
+ if (!audio_notify_) {
+ Stop();
+ return false;
+ }
+
+ video_notify_ = Register(KSCATEGORY_VIDEO);
+ if (!video_notify_) {
+ Stop();
+ return false;
+ }
+
+ return true;
+}
+
+void DeviceWatcher::Stop() {
+ UnregisterDeviceNotification(video_notify_);
+ video_notify_ = NULL;
+ UnregisterDeviceNotification(audio_notify_);
+ audio_notify_ = NULL;
+ Destroy();
+}
+
+HDEVNOTIFY DeviceWatcher::Register(REFGUID guid) {
+ DEV_BROADCAST_DEVICEINTERFACE dbdi;
+ dbdi.dbcc_size = sizeof(dbdi);
+ dbdi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ dbdi.dbcc_classguid = guid;
+ dbdi.dbcc_name[0] = '\0';
+ return RegisterDeviceNotification(handle(), &dbdi,
+ DEVICE_NOTIFY_WINDOW_HANDLE);
+}
+
+void DeviceWatcher::Unregister(HDEVNOTIFY handle) {
+ UnregisterDeviceNotification(handle);
+}
+
+bool DeviceWatcher::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result) {
+ if (uMsg == WM_DEVICECHANGE) {
+ if (wParam == DBT_DEVICEARRIVAL ||
+ wParam == DBT_DEVICEREMOVECOMPLETE) {
+ DEV_BROADCAST_DEVICEINTERFACE* dbdi =
+ reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lParam);
+ if (dbdi->dbcc_classguid == KSCATEGORY_AUDIO ||
+ dbdi->dbcc_classguid == KSCATEGORY_VIDEO) {
+ manager_->OnDevicesChange();
+ }
+ }
+ result = 0;
+ return true;
+ }
+
+ return false;
+}
+#elif defined(OSX)
+static bool GetVideoDevices(std::vector<Device>* devices) {
+ ComponentDescription only_vdig;
+ memset(&only_vdig, 0, sizeof(only_vdig));
+ only_vdig.componentType = videoDigitizerComponentType;
+ only_vdig.componentSubType = kAnyComponentSubType;
+ only_vdig.componentManufacturer = kAnyComponentManufacturer;
+
+ // Enumerate components (drivers).
+ Component component = 0;
+ while ((component = FindNextComponent(component, &only_vdig))) {
+ // Get the name of the component and see if we want to open it.
+ Handle name_handle = NewHandle(0);
+ GetComponentInfo(component, NULL, name_handle, NULL, NULL);
+ Ptr name_ptr = *name_handle;
+ std::string comp_name(name_ptr + 1, static_cast<size_t>(*name_ptr));
+ DisposeHandle(name_handle);
+
+ if (!ShouldDeviceBeIgnored(comp_name)) {
+ // Try to open the component.
+ // DV Video will fail with err=-9408 (deviceCantMeetRequest)
+ // IIDC FireWire Video and USB Video Class Video will fail with err=704
+ // if no cameras are present, or there is contention for the camera.
+ // We can't tell the scenarios apart, so we will retry a few times if
+ // we get a 704 to make sure we detect the cam if one is really there.
+ int attempts = 0;
+ ComponentInstance vdig;
+ OSErr err;
+ do {
+ err = OpenAComponent(component, &vdig);
+ attempts++;
+ } while (!vdig && err == 704 && attempts < kVideoDeviceOpenAttempts);
+
+ if (vdig) {
+ // We were able to open the component.
+ LOG(LS_INFO) << "Opened component \"" << comp_name
+ << "\", tries=" << attempts;
+
+ // Enumerate cameras on the component.
+ // Note, that due to QT strangeness VDGetNumberOfInputs really returns
+ // the number of inputs minus one. If no inputs are available -1 is
+ // returned.
+ short num_inputs; // NOLINT
+ VideoDigitizerError err = VDGetNumberOfInputs(vdig, &num_inputs);
+ if (err == 0 && num_inputs >= 0) {
+ LOG(LS_INFO) << "Found " << num_inputs + 1 << " webcams attached.";
+ Str255 pname;
+ for (int i = 0; i <= num_inputs; ++i) {
+ err = VDGetInputName(vdig, i, pname);
+ if (err == 0) {
+ // The format for camera ids is <component>:<camera index>.
+ char id_buf[256];
+ talk_base::sprintfn(id_buf, ARRAY_SIZE(id_buf), "%s:%d",
+ comp_name.c_str(), i);
+ std::string name(reinterpret_cast<const char*>(pname + 1),
+ static_cast<size_t>(*pname)), id(id_buf);
+ LOG(LS_INFO) << " Webcam " << i << ": " << name;
+ devices->push_back(Device(name, id));
+ }
+ }
+ }
+ CloseComponent(vdig);
+ } else {
+ LOG(LS_INFO) << "Failed to open component \"" << comp_name
+ << "\", err=" << err;
+ }
+ }
+ }
+ return true;
+}
+
+static bool GetAudioDeviceIDs(bool input,
+ std::vector<AudioDeviceID>* out_dev_ids) {
+ UInt32 propsize;
+ OSErr err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+ &propsize, NULL);
+ if (0 != err) {
+ LOG(LS_ERROR) << "Couldn't get information about property, "
+ << "so no device list acquired.";
+ return false;
+ }
+
+ size_t num_devices = propsize / sizeof(AudioDeviceID);
+ talk_base::scoped_array<AudioDeviceID> device_ids(
+ new AudioDeviceID[num_devices]);
+
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+ &propsize, device_ids.get());
+ if (0 != err) {
+ LOG(LS_ERROR) << "Failed to get device ids, "
+ << "so no device listing acquired.";
+ return false;
+ }
+
+ for (size_t i = 0; i < num_devices; ++i) {
+ AudioDeviceID an_id = device_ids[i];
+ // find out the number of channels for this direction
+ // (input/output) on this device -
+ // we'll ignore anything with no channels.
+ err = AudioDeviceGetPropertyInfo(an_id, 0, input,
+ kAudioDevicePropertyStreams,
+ &propsize, NULL);
+ if (0 == err) {
+ unsigned num_channels = propsize / sizeof(AudioStreamID);
+ if (0 < num_channels) {
+ out_dev_ids->push_back(an_id);
+ }
+ } else {
+ LOG(LS_ERROR) << "No property info for stream property for device id "
+ << an_id << "(is_input == " << input
+ << "), so not including it in the list.";
+ }
+ }
+
+ return true;
+}
+
+static bool GetAudioDeviceName(AudioDeviceID id,
+ bool input,
+ std::string* out_name) {
+ UInt32 nameLength = kAudioDeviceNameLength;
+ char name[kAudioDeviceNameLength + 1];
+ OSErr err = AudioDeviceGetProperty(id, 0, input,
+ kAudioDevicePropertyDeviceName,
+ &nameLength, name);
+ if (0 != err) {
+ LOG(LS_ERROR) << "No name acquired for device id " << id;
+ return false;
+ }
+
+ *out_name = name;
+ return true;
+}
+
+#elif defined(LINUX)
+static const std::string kVideoMetaPathK2_4("/proc/video/dev/");
+static const std::string kVideoMetaPathK2_6("/sys/class/video4linux/");
+
+enum MetaType { M2_4, M2_6, NONE };
+
+static void ScanDeviceDirectory(const std::string& devdir,
+ std::vector<Device>* devices) {
+ talk_base::scoped_ptr<talk_base::DirectoryIterator> directoryIterator(
+ talk_base::Filesystem::IterateDirectory());
+
+ if (directoryIterator->Iterate(talk_base::Pathname(devdir))) {
+ do {
+ std::string filename = directoryIterator->Name();
+ std::string device_name = devdir + filename;
+ if (!directoryIterator->IsDots()) {
+ if (filename.find("video") == 0 &&
+ V4LLookup::IsV4L2Device(device_name)) {
+ devices->push_back(Device(device_name, device_name));
+ }
+ }
+ } while (directoryIterator->Next());
+ }
+}
+
+static std::string GetVideoDeviceNameK2_6(const std::string& device_meta_path) {
+ std::string device_name;
+
+ talk_base::scoped_ptr<talk_base::FileStream> device_meta_stream(
+ talk_base::Filesystem::OpenFile(device_meta_path, "r"));
+
+ if (device_meta_stream.get() != NULL) {
+ if (device_meta_stream->ReadLine(&device_name) != talk_base::SR_SUCCESS) {
+ LOG(LS_ERROR) << "Failed to read V4L2 device meta " << device_meta_path;
+ }
+ device_meta_stream->Close();
+ }
+
+ return device_name;
+}
+
+static std::string Trim(const std::string& s, const std::string& drop = " \t") {
+ std::string::size_type first = s.find_first_not_of(drop);
+ std::string::size_type last = s.find_last_not_of(drop);
+
+ if (first == std::string::npos || last == std::string::npos)
+ return std::string("");
+
+ return s.substr(first, last - first + 1);
+}
+
+static std::string GetVideoDeviceNameK2_4(const std::string& device_meta_path) {
+ talk_base::ConfigParser::MapVector all_values;
+
+ talk_base::ConfigParser config_parser;
+ talk_base::FileStream* file_stream =
+ talk_base::Filesystem::OpenFile(device_meta_path, "r");
+
+ if (file_stream == NULL) return "";
+
+ config_parser.Attach(file_stream);
+ config_parser.Parse(&all_values);
+
+ for (talk_base::ConfigParser::MapVector::iterator i = all_values.begin();
+ i != all_values.end(); ++i) {
+ talk_base::ConfigParser::SimpleMap::iterator device_name_i =
+ i->find("name");
+
+ if (device_name_i != i->end()) {
+ return device_name_i->second;
+ }
+ }
+
+ return "";
+}
+
+static std::string GetVideoDeviceName(MetaType meta,
+ const std::string& device_file_name) {
+ std::string device_meta_path;
+ std::string device_name;
+ std::string meta_file_path;
+
+ if (meta == M2_6) {
+ meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/name";
+
+ LOG(LS_INFO) << "Trying " + meta_file_path;
+ device_name = GetVideoDeviceNameK2_6(meta_file_path);
+
+ if (device_name.empty()) {
+ meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/model";
+
+ LOG(LS_INFO) << "Trying " << meta_file_path;
+ device_name = GetVideoDeviceNameK2_6(meta_file_path);
+ }
+ } else {
+ meta_file_path = kVideoMetaPathK2_4 + device_file_name;
+ LOG(LS_INFO) << "Trying " << meta_file_path;
+ device_name = GetVideoDeviceNameK2_4(meta_file_path);
+ }
+
+ if (device_name.empty()) {
+ device_name = "/dev/" + device_file_name;
+ LOG(LS_ERROR)
+ << "Device name not found, defaulting to device path " << device_name;
+ }
+
+ LOG(LS_INFO) << "Name for " << device_file_name << " is " << device_name;
+
+ return Trim(device_name);
+}
+
+static void ScanV4L2Devices(std::vector<Device>* devices) {
+ LOG(LS_INFO) << ("Enumerating V4L2 devices");
+
+ MetaType meta;
+ std::string metadata_dir;
+
+ talk_base::scoped_ptr<talk_base::DirectoryIterator> directoryIterator(
+ talk_base::Filesystem::IterateDirectory());
+
+ // Try and guess kernel version
+ if (directoryIterator->Iterate(kVideoMetaPathK2_6)) {
+ meta = M2_6;
+ metadata_dir = kVideoMetaPathK2_6;
+ } else if (directoryIterator->Iterate(kVideoMetaPathK2_4)) {
+ meta = M2_4;
+ metadata_dir = kVideoMetaPathK2_4;
+ } else {
+ meta = NONE;
+ }
+
+ if (meta != NONE) {
+ LOG(LS_INFO) << "V4L2 device metadata found at " << metadata_dir;
+
+ do {
+ std::string filename = directoryIterator->Name();
+
+ if (filename.find("video") == 0) {
+ std::string device_path = "/dev/" + filename;
+
+ if (V4LLookup::IsV4L2Device(device_path)) {
+ devices->push_back(
+ Device(GetVideoDeviceName(meta, filename), device_path));
+ }
+ }
+ } while (directoryIterator->Next());
+ } else {
+ LOG(LS_ERROR) << "Unable to detect v4l2 metadata directory";
+ }
+
+ if (devices->size() == 0) {
+ LOG(LS_INFO) << "Plan B. Scanning all video devices in /dev directory";
+ ScanDeviceDirectory("/dev/", devices);
+ }
+
+ LOG(LS_INFO) << "Total V4L2 devices found : " << devices->size();
+}
+
+static bool GetVideoDevices(std::vector<Device>* devices) {
+ ScanV4L2Devices(devices);
+ return true;
+}
+#endif
+
+#ifndef LINUX
+// TODO(tommyw): Try to get hold of a copy of Final Cut to understand why we
+// crash while scanning their components on OS X.
+static bool ShouldDeviceBeIgnored(const std::string& device_name) {
+ static const char* const kFilteredDevices[] = {
+ "Google Camera Adapter", // Our own magiccams
+#ifdef WIN32
+ "Asus virtual Camera", // Bad Asus desktop virtual cam
+ "Bluetooth Video", // Bad Sony viao bluetooth sharing driver
+#endif
+#ifdef OSX
+ "DVCPRO HD", // Final cut
+#endif
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(kFilteredDevices); ++i) {
+ if (strnicmp(device_name.c_str(), kFilteredDevices[i],
+ strlen(kFilteredDevices[i])) == 0) {
+ LOG(LS_INFO) << "Ignoring device " << device_name;
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+}; // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/devicemanager.h b/third_party/libjingle/source/talk/session/phone/devicemanager.h
new file mode 100644
index 0000000..a79f7fb
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/devicemanager.h
@@ -0,0 +1,107 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_DEVICEMANAGER_H_
+#define TALK_SESSION_PHONE_DEVICEMANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/base/sigslot.h"
+#include "talk/base/stringencode.h"
+#ifdef USE_TALK_SOUND
+#include "talk/sound/soundsystemfactory.h"
+#endif
+
+namespace cricket {
+
+class DeviceWatcher;
+
+// Used to represent an audio or video capture or render device.
+struct Device {
+ Device() {}
+ Device(const std::string& first, int second)
+ : name(first),
+ id(talk_base::ToString(second)) {
+ }
+ Device(const std::string& first, const std::string& second)
+ : name(first), id(second) {}
+
+ std::string name;
+ std::string id;
+};
+
+// DeviceManager manages the audio and video devices on the system.
+// Methods are virtual to allow for easy stubbing/mocking in tests.
+class DeviceManager {
+ public:
+ DeviceManager(
+#ifdef USE_TALK_SOUND
+ SoundSystemFactory *factory
+#endif
+ );
+ virtual ~DeviceManager();
+
+ // Initialization
+ virtual bool Init();
+ virtual void Terminate();
+ bool initialized() const { return initialized_; }
+
+ // Capabilities
+ virtual int GetCapabilities();
+
+ // Device enumeration
+ virtual bool GetAudioInputDevices(std::vector<Device>* devices);
+ virtual bool GetAudioOutputDevices(std::vector<Device>* devices);
+
+ bool GetAudioInputDevice(const std::string& name, Device* out);
+ bool GetAudioOutputDevice(const std::string& name, Device* out);
+
+ virtual bool GetVideoCaptureDevices(std::vector<Device>* devs);
+ virtual bool GetDefaultVideoCaptureDevice(Device* device);
+ sigslot::signal0<> SignalDevicesChange;
+
+ void OnDevicesChange() { SignalDevicesChange(); }
+
+ static const std::string kDefaultDeviceName;
+
+ protected:
+ virtual bool GetAudioDevice(bool is_input, const std::string& name,
+ Device* out);
+ private:
+ bool GetAudioDevicesByPlatform(bool input, std::vector<Device>* devs);
+
+ bool initialized_;
+ DeviceWatcher* watcher_;
+#ifdef USE_TALK_SOUND
+ SoundSystemHandle sound_system_;
+#endif
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_DEVICEMANAGER_H_
diff --git a/third_party/libjingle/source/talk/session/phone/filevideoengine.h b/third_party/libjingle/source/talk/session/phone/filevideoengine.h
new file mode 100644
index 0000000..158b775
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/filevideoengine.h
@@ -0,0 +1,118 @@
+// Copyright 2008 Google Inc. All Rights Reserved.
+// Author: pzembrod@google.com (Philip Zembrod)
+
+// FileVideoEngine shall be used to spool received video into files and to
+// transmit prerecorded video from files. It is intended for use with
+// call.exe clients and derived bots for 1:1 and n:n TalkVideo testing.
+// Having a client without actual connection to media hardware allows to
+// run several of them on one machine, together withone actual browser
+// client, for single-handed manual testing and single-browser automatic
+// testing.
+// Currently FileVideoEngine records into file names like "video_1.rtp" or
+// "somebasename_video_5.rtp". The base name can be set in the engine.
+// The auxiliary rtcp streams are recorded into files with the extension
+// ".rtcp".
+
+// FileVideoEngine is designed for the MediaEngine mechanism
+// in Talk Video clients, to replace LmiVideoEngine
+// in a constructor call like
+// return new CompositeMediaEngine<GipsVoiceEngine, LmiVideoEngine>;
+// Eventually together with a FileAudioEngine, a file based media engine
+// shall be constructed with
+// return new CompositeMediaEngine<FileAudioEngine, FileVideoEngine>;
+// While FileAudioEngine is not available, FileVideoEngine can be combined
+// with GipsVoiceEngine:
+// return new CompositeMediaEngine<GipsVoiceEngine, FileVideoEngine>;
+// One of these calls is performed by the static method
+// MediaEngine* MediaEngine::Create()
+// depending on compiler flags or through dependency injection of a
+// factory.
+
+#ifndef TALK_SESSION_PHONE_FILEVIDEOENGINE_H__
+#define TALK_SESSION_PHONE_FILEVIDEOENGINE_H__
+
+#include <string>
+#include <vector>
+
+// basictypes.h is included for DISALLOW_EVIL_CONSTRUCTORS
+#include "talk/base/basictypes.h"
+// codec.h is included because std::vector<VideoCodec> must know
+// VideoCodec's size
+#include "talk/session/phone/codec.h"
+// filevideomediachannel.h is included because in mediaengine.cc
+// when compiling CompositeMediaEngine the compiler must know that
+// FileVideoMediaChannel* FileVideoEngine::CreateChannel(...)
+// returns a class derived from VideoMediaChannel.
+#include "talk/session/phone/filevideomediachannel.h"
+// Using CaptureResult enumeration from mediaengine.h.
+#include "talk/session/phone/mediaengine.h"
+
+// Transmitting video is harder and will be implemented when
+// receiving and recording works.
+// TODO(pzembrod): remove all ifdef REPLAY_IMPLEMENTED once
+// transmitting is implemented and tested.
+#undef REPLAY_IMPLEMENTED
+
+namespace cricket {
+
+struct Device;
+class VideoRenderer;
+class VoiceMediaChannel;
+
+// See comment at top of file
+class FileVideoEngine {
+ public:
+ FileVideoEngine();
+ virtual ~FileVideoEngine() {}
+ // Init() is called by virtual bool CompositeMediaEngine::Init().
+ bool Init();
+
+ // Accessors for file_base_name_.
+ // The getter is virtual to allow mocking it out.
+ void set_file_base_name(const std::string& file_base_name);
+ virtual const std::string& file_base_name() const;
+
+ // This creates the actual worker, a FileVideoMediaChannel instance.
+ FileVideoMediaChannel* CreateChannel(VoiceMediaChannel* voice_media_channel);
+
+ // All the following methods are called by corresponding
+ // virtual methods of CompositeMediaEngine and don't do
+ // anything yet.
+ void GetCaptureDeviceNames(std::vector<std::string>* names);
+ bool SetCaptureDevice(const Device* device);
+ bool SetCaptureFormat(int width, int height, int framerate);
+ void Terminate() {}
+ int GetCapabilities() { return 0; }
+ bool SetOptions(int opts) { return true; }
+ bool SetLocalRenderer(VideoRenderer* renderer) { return true; }
+ MediaEngine::CaptureResult SetCapture(bool capture) {
+ return MediaEngine::CR_SUCCESS;
+ }
+ const std::vector<VideoCodec>& codecs();
+ bool FindCodec(const VideoCodec& codec);
+ bool SetDefaultCodec(const VideoCodec& codec);
+ void SetLogging(int severity, const char* filter) {}
+ sigslot::signal1<bool> SignalCaptureResult;
+
+ private:
+ // The engine just supports one capture device; actual name's not
+ // important, behind it are recorded files, of course.
+ static const char* const kFileCaptureDeviceName;
+
+ // The codec in which the recording currently available for replay
+ // is encoded.
+ VideoCodec replay_codec_;
+
+ // List of supported codecs. Published by codec(). Used by FindCodec.
+ // Since we could record any encoding we might like some generic
+ // flexibility here. Might be overkill, though.
+ std::vector<VideoCodec> codecs_;
+
+ // Recording file base name for FileVideoMediaChannel.
+ std::string file_base_name_;
+ DISALLOW_EVIL_CONSTRUCTORS(FileVideoEngine);
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_FILEVIDEOENGINE_H__
diff --git a/third_party/libjingle/source/talk/session/phone/filevideomediachannel.h b/third_party/libjingle/source/talk/session/phone/filevideomediachannel.h
new file mode 100644
index 0000000..d7f8137
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/filevideomediachannel.h
@@ -0,0 +1,149 @@
+// Copyright 2008 Google Inc. All Rights Reserved.
+// Author: pzembrod@google.com (Philip Zembrod)
+
+// FileVideoMediaChannel is the worker channel for FileVideoEngine,
+// designed for the MediaEngine mechanism in Talk Video clients
+// (see comments at top of file filevideoengine.h).
+// FileVideoMediaChannel takes care of the actual transmitting
+// and receiving of video streams over rtp and the reading
+// from and recording to files. FileVideoMediaChannel is created
+// by FileVideoEngine::CreateChannel().
+// rtp (real time protocol) is a udp protocol used (here) to transfer
+// audio and video streams.
+// rtcp (real time control protocol), also udp-based, is an associated
+// control protocol.
+
+#ifndef TALK_SESSION_PHONE_FILEVIDEOMEDIACHANNEL_H_
+#define TALK_SESSION_PHONE_FILEVIDEOMEDIACHANNEL_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/base/criticalsection.h"
+#include "talk/base/stream.h" // for StreamResult
+#include "talk/session/phone/mediachannel.h"
+
+using talk_base::CritScope;
+using talk_base::FileStream;
+
+namespace cricket {
+
+class FileVideoEngine;
+
+// See comment at top of file
+class FileVideoMediaChannel : public VideoMediaChannel {
+ public:
+ // The creating engine is used to provide the recording file base name.
+ explicit FileVideoMediaChannel(FileVideoEngine* engine);
+ virtual ~FileVideoMediaChannel();
+ // FileVideoMediaChannel can run without Init(), but then it doesn't record.
+ // Calling Init() again restarts recording to engine's current file name.
+ // If the file name has changed since the last Init() call, the old files
+ // are closed and new files (one for rtp, one for rtcp) opened.
+ virtual void Init();
+
+ virtual bool SetOptions(int options) {
+ return true;
+ }
+ virtual bool SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
+ return true;
+ }
+
+ // This is called after initiating or accepting a call
+ // with the possible codecs of a session. The channel has to
+ // choose one it can handle and inform the session of the choice.
+ virtual bool SetSendCodecs(const std::vector<VideoCodec>& codecs);
+
+ // Called on receiving a rtp packet. packet_data points to a buffer
+ // containing the packet's bytes, packet_length is the buffer's length.
+ virtual void OnPacketReceived(const void* packet_data, int packet_length);
+ // Called on receiving a rtcp packet. packet_data points to a buffer
+ // containing the packet's bytes, packet_length is the buffer's length.
+ virtual void OnRtcpReceived(const void* packet_data, int packet_length);
+
+ // Remaining pure virtual methods of MediaChannel which are
+ // not overridden in VideoMediaChannel are overridden with
+ // empty implementations here to be able to run the class.
+ // Most or all of them will doubtless be filled out with
+ // useful code later.
+ virtual bool SetRenderer(uint32 ssrc, VideoRenderer* r) { return true; }
+ virtual bool SetRender(bool render) { return true; }
+ virtual bool AddScreencast(uint32 ssrc, talk_base::WindowId id) {
+ return true;
+ }
+ virtual bool RemoveScreencast(uint32 ssrc) { return true; }
+ virtual bool SetSend(bool send) { return true; }
+ virtual void SetSendSsrc(uint32 id) {}
+ virtual bool SetMaxSendBandwidth(int max_bandwidth) { return false; }
+ virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc) { return false; }
+ virtual bool RemoveStream(uint32 ssrc) { return false; }
+ virtual bool Mute(bool muted) { return false; }
+ virtual bool GetStats(VideoMediaInfo* info) { return false; }
+
+ // Builds the file name for rtp recording.
+ // public for testing.
+ std::string rtp_filename() const;
+ // Builds the file name for rtcp recording.
+ // public for testing.
+ std::string rtcp_filename() const;
+
+ private:
+ // Common inner function for rtp_filename() and rtcp_filename(). Does
+ // all the work except for the extensions ".rtp"/".rtcp".
+ std::string file_name() const;
+
+ // Creates and opens rtp_writer_ for writing (mode "w") into file
+ // rtp_filename().
+ void OpenRtpFile();
+ // Creates and opens rtcp_writer_ for writing (mode "w") into file
+ // rtcp_filename().
+ void OpenRtcpFile();
+
+ // Closes and deletes rtp_writer_.
+ void CloseRtpFile();
+ // Closes and deletes rtcp_writer_.
+ void CloseRtcpFile();
+
+ // Helper function for OpenRtpFile() and OpenRtcpFile().
+ // Creates, opens and returns FileStream object.
+ // Params as in FileStream::Open(). Returns NULL on failure.
+ FileStream* OpenFileStream(const std::string& file_name, const char* mode);
+
+ // Helper function for OnPacketReceived() and OnRtcpReceived().
+ // Writes rtp or rtcp packet in packet_data/packet_length to writer.
+ // FileStream *writer my be NULL, then nothing is written.
+ // protocol_name should be "rtp" or "rtcp" and is used in
+ // logging messages only.
+ void WritePacket(FileStream* writer, const std::string& protocol_name,
+ const void* packet_data, int packet_length);
+
+ // Helper method for WritePacket(). It's a wrapper around
+ // FileStream::Write() with error logging and returns the
+ // return value of FileStream::Write(), SR_SUCCESS on success.
+ // *what_is_written is used for the error message and should be
+ // something like "rtp packet size" or "rtp packet data".
+ // writer must not be NULL.
+ talk_base::StreamResult WriteAndCheck(FileStream* writer,
+ const std::string& what_is_written, const void* data, int length);
+
+ // Engine creating FileVideoMediaChannel instance, used to
+ // provide file_base_name() for recording.
+ FileVideoEngine* engine_;
+ // FileStream for recording rtp packages
+ talk_base::scoped_ptr<FileStream> rtp_writer_;
+ // FileStream for recording rtcp packages
+ talk_base::scoped_ptr<FileStream> rtcp_writer_;
+
+ // ID to create unique filenames for several instances
+ // which might exist in parallel.
+ int id_;
+ // Static counter to create these IDs.
+ static int id_generator_;
+ // Mutex for id_generator_.
+ static talk_base::CriticalSection cs_id_generator_;
+ DISALLOW_EVIL_CONSTRUCTORS(FileVideoMediaChannel);
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_FILEVIDEOMEDIACHANNEL_H_
diff --git a/third_party/libjingle/source/talk/session/phone/mediachannel.h b/third_party/libjingle/source/talk/session/phone/mediachannel.h
new file mode 100644
index 0000000..857f1b6
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/mediachannel.h
@@ -0,0 +1,312 @@
+/*
+ * libjingle
+ * Copyright 2004--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_
+
+#include <vector>
+
+#include "talk/base/basictypes.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/socket.h"
+#include "talk/base/windowpicker.h"
+#include "talk/session/phone/codec.h"
+// TODO(juberti): re-evaluate this include
+#include "talk/session/phone/audiomonitor.h"
+
+namespace flute {
+ class MagicCamVideoRenderer;
+}
+
+namespace cricket {
+
+enum VoiceMediaChannelOptions {
+ OPT_CONFERENCE = 0x10000, // tune the audio stream for conference mode
+ OPT_ENERGYLEVEL = 0x20000, // include the energy level in RTP packets, as
+ // defined in https://datatracker.ietf.org/drafts/
+ // draft-lennox-avt-rtp-audio-level-exthdr/
+};
+
+enum VideoMediaChannelOptions {
+};
+
+class MediaChannel : public sigslot::has_slots<> {
+ public:
+ class NetworkInterface {
+ public:
+ enum SocketType { ST_RTP, ST_RTCP };
+ virtual int SendPacket(const void *data, size_t len) = 0;
+ virtual int SendRtcp(const void *data, size_t len) = 0;
+ virtual int SetOption(SocketType type, talk_base::Socket::Option opt,
+ int option) = 0;
+ virtual ~NetworkInterface() {}
+ };
+
+ MediaChannel() : network_interface_(NULL) {}
+ virtual ~MediaChannel() {}
+
+ // Gets/sets the abstract inteface class for sending RTP/RTCP data.
+ NetworkInterface *network_interface() { return network_interface_; }
+ virtual void SetInterface(NetworkInterface *iface) {
+ network_interface_ = iface;
+ }
+
+ // Called when a RTP packet is received.
+ virtual void OnPacketReceived(const void *data, int len) = 0;
+ // Called when a RTCP packet is received.
+ virtual void OnRtcpReceived(const void *data, int len) = 0;
+ // Sets the SSRC to be used for outgoing data.
+ virtual void SetSendSsrc(uint32 id) = 0;
+ // Mutes the channel.
+ virtual bool Mute(bool on) = 0;
+
+ virtual bool SetRtpExtensionHeaders(bool enable_all) { return true; }
+ virtual bool SetMaxSendBandwidth(int max_bandwidth) = 0;
+ virtual bool SetOptions(int options) = 0;
+
+ protected:
+ NetworkInterface *network_interface_;
+};
+
+enum SendFlags {
+ SEND_NOTHING,
+ SEND_RINGBACKTONE,
+ SEND_MICROPHONE
+};
+
+// TODO(juberti): separate into VoiceMediaInfo and VideoMediaInfo
+struct MediaInfo {
+ int fraction_lost;
+ int cum_lost;
+ int ext_max;
+ int jitter;
+ int RTT;
+ int bytesSent;
+ int packetsSent;
+ int bytesReceived;
+ int packetsReceived;
+};
+
+typedef MediaInfo VoiceMediaInfo;
+typedef MediaInfo VideoMediaInfo;
+
+class VoiceMediaChannel : public MediaChannel {
+ public:
+ VoiceMediaChannel() {}
+ virtual ~VoiceMediaChannel() {}
+ // Sets the codecs/payload types to be used for incoming media.
+ virtual bool SetRecvCodecs(const std::vector<Codec>& codecs) = 0;
+ // Sets the codecs/payload types to be used for outgoing media.
+ virtual bool SetSendCodecs(const std::vector<Codec>& codecs) = 0;
+ // Starts or stops playout of received audio.
+ virtual bool SetPlayout(bool playout) = 0;
+ // Starts or stops sending (and potentially capture) of local audio.
+ virtual bool SetSend(SendFlags flag) = 0;
+ // Adds a new receive-only stream with the specified SSRC.
+ virtual bool AddStream(uint32 ssrc) = 0;
+ // Removes a stream added with AddStream.
+ virtual bool RemoveStream(uint32 ssrc) = 0;
+ // Gets current energy levels for all incoming streams.
+ virtual bool GetActiveStreams(AudioInfo::StreamList* actives) = 0;
+ // Get the current energy level for the outgoing stream.
+ virtual int GetOutputLevel() = 0;
+ // Specifies a ringback tone to be played during call setup.
+ virtual void SetRingbackTone(const char *buf, int len) = 0;
+ // Plays or stops the aforementioned ringback tone
+ virtual bool PlayRingbackTone(bool play, bool loop) = 0;
+ // Sends a out-of-band DTMF signal using the specified event.
+ virtual bool PressDTMF(int event, bool playout) = 0;
+ // Gets quality stats for the channel.
+ virtual bool GetStats(VoiceMediaInfo* info) = 0;
+};
+
+// Represents a (decoded) video frame, in YUV420 (a.k.a. I420) format.
+class VideoFrame {
+ friend class flute::MagicCamVideoRenderer;
+
+ public:
+ VideoFrame() : rendered_(false) {}
+
+ virtual ~VideoFrame() {}
+
+ virtual size_t GetWidth() const = 0;
+ virtual size_t GetHeight() const = 0;
+ virtual const uint8 *GetYPlane() const = 0;
+ virtual const uint8 *GetUPlane() const = 0;
+ virtual const uint8 *GetVPlane() const = 0;
+ virtual uint8 *GetYPlane() = 0;
+ virtual uint8 *GetUPlane() = 0;
+ virtual uint8 *GetVPlane() = 0;
+ virtual int32 GetYPitch() const = 0;
+ virtual int32 GetUPitch() const = 0;
+ virtual int32 GetVPitch() const = 0;
+
+ // For retrieving the aspect ratio of each pixel. Usually this is 1x1, but
+ // the aspect_ratio_idc parameter of H.264 can specify non-square pixels.
+ virtual size_t GetPixelWidth() const = 0;
+ virtual size_t GetPixelHeight() const = 0;
+
+ // Writes the frame into the given frame buffer, provided that it is of
+ // sufficient size. Returns the frame's actual size, regardless of whether
+ // it was written or not (like snprintf). If there is insufficient space,
+ // nothing is written.
+ virtual size_t CopyToBuffer(uint8 *buffer, size_t size) const = 0;
+
+ // Converts the I420 data to RGB of a certain type such as BGRA and RGBA.
+ // Returns the frame's actual size, regardless of whether it was written or
+ // not (like snprintf). Parameters size and pitch_rgb are in units of bytes.
+ // If there is insufficient space, nothing is written.
+ virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8 *buffer,
+ size_t size, size_t pitch_rgb) const = 0;
+
+ // Writes the frame into the given planes, stretched to the given width and
+ // height.
+ // "interpolate" controls whether to interpolate or just take the
+ // nearest-point.
+ virtual void StretchToPlanes(uint8 *y, uint8 *u, uint8 *v,
+ int32 pitchY, int32 pitchU, int32 pitchV,
+ size_t width, size_t height,
+ bool interpolate) const = 0;
+
+ // Writes the frame into the given frame buffer, stretched to the given
+ // width and height, provided that it is of sufficient size. Returns the
+ // frame's actual size, regardless of whether it was written or not
+ // (like snprintf). If there is insufficient space, nothing is written.
+ // "interpolate" controls whether to interpolate or just take the
+ // nearest-point.
+ virtual size_t StretchToBuffer(size_t w, size_t h, uint8 *buffer, size_t size,
+ bool interpolate) const = 0;
+
+ // Writes the frame into the target VideoFrame, stretched to the size of that
+ // frame.
+ // "interpolate" controls whether to interpolate or just take the
+ // nearest-point.
+ virtual void StretchToFrame(VideoFrame *target, bool interpolate) const = 0;
+
+ // Stretches the frame to the given size, creating a new VideoFrame object to
+ // hold it.
+ // "interpolate" controls whether to interpolate or just take the
+ // nearest-point.
+ virtual VideoFrame *Stretch(size_t w, size_t h, bool interpolate) const = 0;
+
+ // Size of an I420 image of given dimensions when stored as a frame buffer.
+ static size_t SizeOf(size_t w, size_t h) {
+ return w * h * 3 / 2;
+ }
+
+ protected:
+ // The frame needs to be rendered to magiccam only once.
+ // TODO(geer): Remove this flag once magiccam rendering is fully replaced
+ // by client3d rendering.
+ mutable bool rendered_;
+};
+
+// Simple subclass for use in mocks.
+class NullVideoFrame : public VideoFrame {
+ public:
+ virtual size_t GetWidth() const { return 0; }
+ virtual size_t GetHeight() const { return 0; }
+ virtual const uint8 *GetYPlane() const { return NULL; }
+ virtual const uint8 *GetUPlane() const { return NULL; }
+ virtual const uint8 *GetVPlane() const { return NULL; }
+ virtual uint8 *GetYPlane() { return NULL; }
+ virtual uint8 *GetUPlane() { return NULL; }
+ virtual uint8 *GetVPlane() { return NULL; }
+ virtual int32 GetYPitch() const { return 0; }
+ virtual int32 GetUPitch() const { return 0; }
+ virtual int32 GetVPitch() const { return 0; }
+
+ virtual size_t GetPixelWidth() const { return 1; }
+ virtual size_t GetPixelHeight() const { return 1; }
+
+ virtual size_t CopyToBuffer(uint8 *buffer, size_t size) const {
+ return 0;
+ }
+
+ virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8 *buffer,
+ size_t size, size_t pitch_rgb) const {
+ return 0;
+ }
+
+ virtual void StretchToPlanes(uint8 *y, uint8 *u, uint8 *v,
+ int32 pitchY, int32 pitchU, int32 pitchV,
+ size_t width, size_t height,
+ bool interpolate) const {
+ }
+
+ virtual size_t StretchToBuffer(size_t w, size_t h, uint8 *buffer, size_t size,
+ bool interpolate) const {
+ return 0;
+ }
+
+ virtual void StretchToFrame(VideoFrame *target, bool interpolate) const {
+ }
+
+ virtual VideoFrame *Stretch(size_t w, size_t h, bool interpolate) const {
+ return NULL;
+ }
+};
+
+class VideoRenderer {
+ public:
+ virtual ~VideoRenderer() {}
+ // Called when the video has changed size.
+ virtual bool SetSize(int width, int height, int reserved) = 0;
+ // Called when a new frame is available for display.
+ virtual bool RenderFrame(const VideoFrame *frame) = 0;
+};
+
+class VideoMediaChannel : public MediaChannel {
+ public:
+ VideoMediaChannel() { renderer_ = NULL; }
+ virtual ~VideoMediaChannel() {}
+ // Sets the codecs/payload types to be used for incoming media.
+ virtual bool SetRecvCodecs(const std::vector<VideoCodec> &codecs) = 0;
+ // Sets the codecs/payload types to be used for outgoing media.
+ virtual bool SetSendCodecs(const std::vector<VideoCodec> &codecs) = 0;
+ // Starts or stops playout of received video.
+ virtual bool SetRender(bool render) = 0;
+ // Starts or stops transmission (and potentially capture) of local video.
+ virtual bool SetSend(bool send) = 0;
+ // Adds a new receive-only stream with the specified SSRC.
+ virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc) = 0;
+ // Removes a stream added with AddStream.
+ virtual bool RemoveStream(uint32 ssrc) = 0;
+ // Sets the renderer object to be used for the specified stream.
+ // If SSRC is 0, the renderer is used for the 'default' stream.
+ virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer) = 0;
+ virtual bool AddScreencast(uint32 ssrc, talk_base::WindowId id) = 0;
+ virtual bool RemoveScreencast(uint32 ssrc) = 0;
+ // Gets quality stats for the channel.
+ virtual bool GetStats(VideoMediaInfo* info) = 0;
+ protected:
+ VideoRenderer *renderer_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIACHANNEL_H_
diff --git a/third_party/libjingle/source/talk/session/phone/mediaengine.cc b/third_party/libjingle/source/talk/session/phone/mediaengine.cc
new file mode 100644
index 0000000..1b59d5c
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/mediaengine.cc
@@ -0,0 +1,44 @@
+//
+// libjingle
+// Copyright 2004--2007, Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. The name of the author may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/mediaengine.h"
+
+
+namespace cricket {
+
+// TODO(pthatcher): according to thaloun, HAVE_GIPSVIDEO will always
+// be false, so we can get rid of it.
+
+MediaEngine* MediaEngine::Create(
+#ifdef USE_TALK_SOUND
+ SoundSystemFactory *factory
+#endif
+ ) {
+ return new NullMediaEngine();
+}
+
+}; // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/mediaengine.h b/third_party/libjingle/source/talk/session/phone/mediaengine.h
new file mode 100644
index 0000000..2633e05
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/mediaengine.h
@@ -0,0 +1,319 @@
+/*
+ * libjingle
+ * Copyright 2004--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_MEDIAENGINE_H_
+#define TALK_SESSION_PHONE_MEDIAENGINE_H_
+
+#ifdef OSX
+#include <CoreAudio/CoreAudio.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include "talk/base/sigslotrepeater.h"
+#include "talk/session/phone/codec.h"
+#include "talk/session/phone/devicemanager.h"
+#include "talk/session/phone/mediachannel.h"
+#ifdef USE_TALK_SOUND
+#include "talk/sound/soundsystemfactory.h"
+#endif
+
+namespace cricket {
+
+// A class for playing out soundclips.
+class SoundclipMedia {
+ public:
+ enum SoundclipFlags {
+ SF_LOOP = 1,
+ };
+
+ virtual ~SoundclipMedia() {}
+
+ // Plays a sound out to the speakers with the given audio stream. The stream
+ // must be 16-bit little-endian 16 kHz PCM. If a stream is already playing
+ // on this SoundclipMedia, it is stopped. If clip is NULL, nothing is played.
+ // Returns whether it was successful.
+ virtual bool PlaySound(const char *clip, int len, int flags) = 0;
+};
+
+// MediaEngine is an abstraction of a media engine which can be subclassed
+// to support different media componentry backends. It supports voice and
+// video operations in the same class to facilitate proper synchronization
+// between both media types.
+class MediaEngine {
+ public:
+ // TODO(juberti): Move this to a global location (also used in DeviceManager)
+ // Capabilities of the media engine.
+ enum Capabilities {
+ AUDIO_RECV = 1 << 0,
+ AUDIO_SEND = 1 << 1,
+ VIDEO_RECV = 1 << 2,
+ VIDEO_SEND = 1 << 3,
+ };
+
+ // Bitmask flags for options that may be supported by the media engine
+ // implementation
+ enum AudioOptions {
+ ECHO_CANCELLATION = 1 << 0,
+ AUTO_GAIN_CONTROL = 1 << 1,
+ DEFAULT_AUDIO_OPTIONS = ECHO_CANCELLATION | AUTO_GAIN_CONTROL
+ };
+ enum VideoOptions {
+ };
+
+ enum CaptureResult {
+ CR_SUCCESS,
+ CR_PENDING,
+ CR_FAILURE,
+ CR_NO_DEVICE,
+ };
+
+ virtual ~MediaEngine() {}
+ static MediaEngine* Create(
+#ifdef USE_TALK_SOUND
+ SoundSystemFactory *factory
+#endif
+ );
+
+ // Initialization
+ // Starts the engine.
+ virtual bool Init() = 0;
+ // Shuts down the engine.
+ virtual void Terminate() = 0;
+ // Returns what the engine is capable of, as a set of Capabilities, above.
+ virtual int GetCapabilities() = 0;
+
+ // MediaChannel creation
+ // Creates a voice media channel. Returns NULL on failure.
+ virtual VoiceMediaChannel *CreateChannel() = 0;
+ // Creates a video media channel, paired with the specified voice channel.
+ // Returns NULL on failure.
+ virtual VideoMediaChannel *CreateVideoChannel(
+ VoiceMediaChannel* voice_media_channel) = 0;
+
+ // Creates a soundclip object for playing sounds on. Returns NULL on failure.
+ virtual SoundclipMedia *CreateSoundclip() = 0;
+
+ // Configuration
+ // Sets global audio options. "options" are from AudioOptions, above.
+ virtual bool SetAudioOptions(int options) = 0;
+ // Sets global video options. "options" are from VideoOptions, above.
+ virtual bool SetVideoOptions(int options) = 0;
+ // Sets the default (maximum) codec/resolution to capture and encode video.
+ virtual bool SetDefaultVideoCodec(const VideoCodec& codec) = 0;
+
+ // Device selection
+ // TODO(tschmelcher): Add method for selecting the soundclip device.
+ virtual bool SetSoundDevices(const Device* in_device,
+ const Device* out_device) = 0;
+ virtual bool SetVideoCaptureDevice(const Device* cam_device) = 0;
+
+ // Device configuration
+ // Sets the current speaker volume, as a value between 0 and 255.
+ virtual bool SetOutputVolume(int level) = 0;
+
+ // Local monitoring
+ // Gets the current microphone level, as a value between 0 and 10.
+ virtual int GetInputLevel() = 0;
+ // Starts or stops the local microphone. Useful if local mic info is needed
+ // prior to a call being connected; the mic will be started automatically
+ // when a VoiceMediaChannel starts sending.
+ virtual bool SetLocalMonitor(bool enable) = 0;
+ // Installs a callback for raw frames from the local camera.
+ virtual bool SetLocalRenderer(VideoRenderer* renderer) = 0;
+ // Starts/stops local camera.
+ virtual CaptureResult SetVideoCapture(bool capture) = 0;
+
+ // Codecs
+ virtual const std::vector<Codec>& codecs() = 0;
+ virtual const std::vector<VideoCodec>& video_codecs() = 0;
+ virtual bool FindCodec(const Codec &codec) = 0;
+ virtual bool FindVideoCodec(const VideoCodec &codec) = 0;
+
+ // Logging control
+ virtual void SetVoiceLogging(int min_sev, const char* filter) = 0;
+ virtual void SetVideoLogging(int min_sev, const char* filter) = 0;
+
+ sigslot::repeater1<bool> SignalVideoCaptureResult;
+};
+
+// CompositeMediaEngine constructs a MediaEngine from separate
+// voice and video engine classes.
+template<class VOICE, class VIDEO>
+class CompositeMediaEngine : public MediaEngine {
+ public:
+#ifdef USE_TALK_SOUND
+ CompositeMediaEngine(SoundSystemFactory *factory) : voice_(factory) {}
+#endif
+ CompositeMediaEngine() {}
+ virtual bool Init() {
+ if (!voice_.Init())
+ return false;
+ if (!video_.Init()) {
+ voice_.Terminate();
+ return false;
+ }
+ SignalVideoCaptureResult.repeat(video_.SignalCaptureResult);
+ return true;
+ }
+ virtual void Terminate() {
+ video_.Terminate();
+ voice_.Terminate();
+ }
+
+ virtual int GetCapabilities() {
+ return (voice_.GetCapabilities() | video_.GetCapabilities());
+ }
+ virtual VoiceMediaChannel *CreateChannel() {
+ return voice_.CreateChannel();
+ }
+ virtual VideoMediaChannel *CreateVideoChannel(VoiceMediaChannel* channel) {
+ return video_.CreateChannel(channel);
+ }
+ virtual SoundclipMedia *CreateSoundclip() {
+ return voice_.CreateSoundclip();
+ }
+
+ virtual bool SetAudioOptions(int o) {
+ return voice_.SetOptions(o);
+ }
+ virtual bool SetVideoOptions(int o) {
+ return video_.SetOptions(o);
+ }
+ virtual bool SetDefaultVideoCodec(const VideoCodec& codec) {
+ return video_.SetDefaultCodec(codec);
+ }
+
+ virtual bool SetSoundDevices(const Device* in_device,
+ const Device* out_device) {
+ return voice_.SetDevices(in_device, out_device);
+ }
+ virtual bool SetVideoCaptureDevice(const Device* cam_device) {
+ return video_.SetCaptureDevice(cam_device);
+ }
+
+ virtual bool SetOutputVolume(int level) {
+ return voice_.SetOutputVolume(level);
+ }
+
+ virtual int GetInputLevel() {
+ return voice_.GetInputLevel();
+ }
+ virtual bool SetLocalMonitor(bool enable) {
+ return voice_.SetLocalMonitor(enable);
+ }
+ virtual bool SetLocalRenderer(VideoRenderer* renderer) {
+ return video_.SetLocalRenderer(renderer);
+ }
+ virtual MediaEngine::CaptureResult SetVideoCapture(bool capture) {
+ return video_.SetCapture(capture);
+ }
+
+ virtual const std::vector<Codec>& codecs() {
+ return voice_.codecs();
+ }
+ virtual const std::vector<VideoCodec>& video_codecs() {
+ return video_.codecs();
+ }
+
+ virtual bool FindCodec(const Codec &codec) {
+ return voice_.FindCodec(codec);
+ }
+ virtual bool FindVideoCodec(const VideoCodec &codec) {
+ return video_.FindCodec(codec);
+ }
+
+ virtual void SetVoiceLogging(int min_sev, const char* filter) {
+ return voice_.SetLogging(min_sev, filter);
+ }
+ virtual void SetVideoLogging(int min_sev, const char* filter) {
+ return video_.SetLogging(min_sev, filter);
+ }
+
+ private:
+ VOICE voice_;
+ VIDEO video_;
+};
+
+// NullVoiceEngine can be used with CompositeMediaEngine in the case where only
+// a video engine is desired.
+class NullVoiceEngine {
+ public:
+ bool Init() { return true; }
+ void Terminate() {}
+ int GetCapabilities() { return 0; }
+ VoiceMediaChannel* CreateChannel() {
+ return NULL;
+ }
+ SoundclipMedia* CreateSoundclip() {
+ return NULL;
+ }
+ bool SetOptions(int opts) { return true; }
+ bool SetDevices(const Device* in_device, const Device* out_device) {
+ return true;
+ }
+ bool SetOutputVolume(int level) { return true; }
+ int GetInputLevel() { return 0; }
+ bool SetLocalMonitor(bool enable) { return true; }
+ const std::vector<Codec>& codecs() { return codecs_; }
+ bool FindCodec(const Codec&) { return false; }
+ void SetLogging(int min_sev, const char* filter) {}
+ private:
+ std::vector<Codec> codecs_;
+};
+
+// NullVideoEngine can be used with CompositeMediaEngine in the case where only
+// a voice engine is desired.
+class NullVideoEngine {
+ public:
+ bool Init() { return true; }
+ void Terminate() {}
+ int GetCapabilities() { return 0; }
+ VideoMediaChannel* CreateChannel(VoiceMediaChannel* voice_media_channel) {
+ return NULL;
+ }
+ bool SetOptions(int opts) { return true; }
+ bool SetDefaultCodec(const VideoCodec& codec) { return true; }
+ bool SetCaptureDevice(const Device* cam_device) { return true; }
+ bool SetLocalRenderer(VideoRenderer* renderer) { return true; }
+ MediaEngine::CaptureResult SetCapture(bool capture) {
+ return MediaEngine::CR_SUCCESS;
+ }
+ const std::vector<VideoCodec>& codecs() { return codecs_; }
+ bool FindCodec(const VideoCodec&) { return false; }
+ void SetLogging(int min_sev, const char* filter) {}
+ sigslot::signal1<bool> SignalCaptureResult;
+ private:
+ std::vector<VideoCodec> codecs_;
+};
+
+typedef CompositeMediaEngine<NullVoiceEngine, NullVideoEngine> NullMediaEngine;
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIAENGINE_H_
diff --git a/third_party/libjingle/source/talk/session/phone/mediamonitor.cc b/third_party/libjingle/source/talk/session/phone/mediamonitor.cc
new file mode 100644
index 0000000..909b536
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/mediamonitor.cc
@@ -0,0 +1,109 @@
+/*
+ * libjingle
+ * Copyright 2005--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/session/phone/mediamonitor.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/session/phone/mediaengine.h"
+
+namespace cricket {
+
+enum {
+ MSG_MONITOR_POLL = 1,
+ MSG_MONITOR_START = 2,
+ MSG_MONITOR_STOP = 3,
+ MSG_MONITOR_SIGNAL = 4
+};
+
+MediaMonitor::MediaMonitor(talk_base::Thread* worker_thread,
+ talk_base::Thread* monitor_thread)
+ : worker_thread_(worker_thread),
+ monitor_thread_(monitor_thread), monitoring_(false), rate_(0) {
+}
+
+MediaMonitor::~MediaMonitor() {
+ monitoring_ = false;
+ monitor_thread_->Clear(this);
+ worker_thread_->Clear(this);
+}
+
+void MediaMonitor::Start(uint32 milliseconds) {
+ rate_ = milliseconds;
+ if (rate_ < 100)
+ rate_ = 100;
+ worker_thread_->Post(this, MSG_MONITOR_START);
+}
+
+void MediaMonitor::Stop() {
+ worker_thread_->Post(this, MSG_MONITOR_STOP);
+ rate_ = 0;
+}
+
+void MediaMonitor::OnMessage(talk_base::Message* message) {
+ talk_base::CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ ASSERT(talk_base::Thread::Current() == worker_thread_);
+ if (!monitoring_) {
+ monitoring_ = true;
+ PollMediaChannel();
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ ASSERT(talk_base::Thread::Current() == worker_thread_);
+ if (monitoring_) {
+ monitoring_ = false;
+ worker_thread_->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ ASSERT(talk_base::Thread::Current() == worker_thread_);
+ PollMediaChannel();
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ ASSERT(talk_base::Thread::Current() == monitor_thread_);
+ Update();
+ break;
+ }
+}
+
+void MediaMonitor::PollMediaChannel() {
+ talk_base::CritScope cs(&crit_);
+ ASSERT(talk_base::Thread::Current() == worker_thread_);
+
+ GetStats();
+
+ // Signal the monitoring thread, start another poll timer
+ monitor_thread_->Post(this, MSG_MONITOR_SIGNAL);
+ worker_thread_->PostDelayed(rate_, this, MSG_MONITOR_POLL);
+}
+
+}
diff --git a/third_party/libjingle/source/talk/session/phone/mediamonitor.h b/third_party/libjingle/source/talk/session/phone/mediamonitor.h
new file mode 100644
index 0000000..9eda2aa
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/mediamonitor.h
@@ -0,0 +1,100 @@
+/*
+ * libjingle
+ * Copyright 2005--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Class to collect statistics from a media channel
+
+#ifndef TALK_SESSION_PHONE_MEDIAMONITOR_H_
+#define TALK_SESSION_PHONE_MEDIAMONITOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/criticalsection.h"
+#include "talk/session/phone/mediachannel.h"
+
+namespace cricket {
+
+// The base MediaMonitor class, independent of voice and video.
+class MediaMonitor : public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+ public:
+ MediaMonitor(talk_base::Thread* worker_thread,
+ talk_base::Thread* monitor_thread);
+ ~MediaMonitor();
+
+ void Start(uint32 milliseconds);
+ void Stop();
+
+ protected:
+ void OnMessage(talk_base::Message *message);
+ void PollMediaChannel();
+ virtual void GetStats() = 0;
+ virtual void Update() = 0;
+
+ talk_base::CriticalSection crit_;
+ talk_base::Thread* worker_thread_;
+ talk_base::Thread* monitor_thread_;
+ bool monitoring_;
+ uint32 rate_;
+};
+
+// Templatized MediaMonitor that can deal with different kinds of media.
+template<class MC, class MI>
+class MediaMonitorT : public MediaMonitor {
+ public:
+ MediaMonitorT(MC* media_channel, talk_base::Thread* worker_thread,
+ talk_base::Thread* monitor_thread)
+ : MediaMonitor(worker_thread, monitor_thread),
+ media_channel_(media_channel) {}
+ sigslot::signal2<MC*, const MI&> SignalUpdate;
+
+ protected:
+ // These routines assume the crit_ lock is held by the calling thread.
+ virtual void GetStats() {
+ media_info_.packetsReceived = -1;
+ if (!media_channel_->GetStats(&media_info_)) {
+ media_info_.packetsReceived = -1;
+ }
+ }
+ virtual void Update() {
+ MI stats(media_info_);
+ crit_.Leave();
+ SignalUpdate(media_channel_, stats);
+ crit_.Enter();
+ }
+
+ private:
+ MC* media_channel_;
+ MI media_info_;
+};
+
+typedef MediaMonitorT<VoiceMediaChannel, VoiceMediaInfo> VoiceMediaMonitor;
+typedef MediaMonitorT<VideoMediaChannel, VideoMediaInfo> VideoMediaMonitor;
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIAMONITOR_H_
+
diff --git a/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc b/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc
new file mode 100644
index 0000000..7d0e476
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc
@@ -0,0 +1,371 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/mediasessionclient.h"
+
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmllite/qname.h"
+
+using namespace talk_base;
+
+namespace cricket {
+
+MediaSessionClient::MediaSessionClient(
+ const buzz::Jid& jid, SessionManager *manager)
+ : jid_(jid), session_manager_(manager), focus_call_(NULL),
+ channel_manager_(new ChannelManager(session_manager_->worker_thread())) {
+ Construct();
+}
+
+MediaSessionClient::MediaSessionClient(
+ const buzz::Jid& jid, SessionManager *manager,
+ MediaEngine* media_engine, DeviceManager* device_manager)
+ : jid_(jid), session_manager_(manager), focus_call_(NULL),
+ channel_manager_(new ChannelManager(
+ media_engine, device_manager, session_manager_->worker_thread())) {
+ Construct();
+}
+
+
+void MediaSessionClient::Construct() {
+ // Register ourselves as the handler of phone and video sessions.
+ session_manager_->AddClient(NS_GINGLE_AUDIO, this);
+ session_manager_->AddClient(NS_GINGLE_VIDEO, this);
+ // Forward device notifications.
+ SignalDevicesChange.repeat(channel_manager_->SignalDevicesChange);
+}
+
+MediaSessionClient::~MediaSessionClient() {
+ // 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_;
+
+ // Remove ourselves from the client map.
+ session_manager_->RemoveClient(NS_GINGLE_VIDEO);
+ session_manager_->RemoveClient(NS_GINGLE_AUDIO);
+}
+
+MediaSessionDescription* MediaSessionClient::CreateOfferSessionDescription(
+ bool video) {
+ MediaSessionDescription* session_desc = new MediaSessionDescription();
+
+
+ // add audio codecs
+ std::vector<Codec> codecs;
+ channel_manager_->GetSupportedCodecs(&codecs);
+ for (std::vector<Codec>::const_iterator i = codecs.begin();
+ i != codecs.end(); ++i)
+ session_desc->voice().AddCodec(*i);
+
+ // add video codecs, if this is a video call
+ if (video) {
+ std::vector<VideoCodec> video_codecs;
+ channel_manager_->GetSupportedVideoCodecs(&video_codecs);
+ for (std::vector<VideoCodec>::const_iterator i = video_codecs.begin();
+ i != video_codecs.end(); i++)
+ session_desc->video().AddCodec(*i);
+ }
+
+ session_desc->Sort();
+ return session_desc;
+}
+
+MediaSessionDescription* MediaSessionClient::CreateAcceptSessionDescription(
+ const SessionDescription* offer) {
+ const MediaSessionDescription* offer_desc =
+ static_cast<const MediaSessionDescription*>(offer);
+ MediaSessionDescription* accept_desc = new MediaSessionDescription();
+
+ // add audio codecs
+ std::vector<Codec> codecs;
+ channel_manager_->GetSupportedCodecs(&codecs);
+ for (unsigned int i = 0; i < offer_desc->voice().codecs().size(); ++i) {
+ if (channel_manager_->FindCodec(offer_desc->voice().codecs()[i]))
+ accept_desc->voice().AddCodec(offer_desc->voice().codecs()[i]);
+ }
+
+ // add video codecs, if the incoming session description has them
+ if (!offer_desc->video().codecs().empty()) {
+ std::vector<VideoCodec> video_codecs;
+ channel_manager_->GetSupportedVideoCodecs(&video_codecs);
+ for (unsigned int i = 0; i < offer_desc->video().codecs().size(); ++i) {
+ if (channel_manager_->FindVideoCodec(offer_desc->video().codecs()[i]))
+ accept_desc->video().AddCodec(offer_desc->video().codecs()[i]);
+ }
+ }
+
+ accept_desc->Sort();
+ return accept_desc;
+}
+
+Call *MediaSessionClient::CreateCall(bool video, bool mux) {
+ Call *call = new Call(this, video, mux);
+ calls_[call->id()] = call;
+ SignalCallCreate(call);
+ return call;
+}
+
+void MediaSessionClient::OnSessionCreate(Session *session,
+ bool received_initiate) {
+ if (received_initiate) {
+ session->SignalState.connect(this, &MediaSessionClient::OnSessionState);
+
+ Call *call = CreateCall(session->session_type() == NS_GINGLE_VIDEO);
+ session_map_[session->id()] = call;
+ call->AddSession(session);
+ }
+}
+
+void MediaSessionClient::OnSessionState(BaseSession *session,
+ BaseSession::State state) {
+ if (state == Session::STATE_RECEIVEDINITIATE) {
+ // If our accept would have no codecs, then we must reject this call.
+ MediaSessionDescription* accept_desc =
+ CreateAcceptSessionDescription(session->remote_description());
+ if (accept_desc->voice().codecs().size() == 0) {
+ // TODO(?): include an error description with the rejection.
+ session->Reject();
+ }
+ delete accept_desc;
+ }
+}
+
+void MediaSessionClient::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 MediaSessionClient::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 *MediaSessionClient::GetFocus() {
+ return focus_call_;
+}
+
+void MediaSessionClient::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 MediaSessionClient::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 *MediaSessionClient::CreateSession(Call *call) {
+ const std::string& type = call->video() ? NS_GINGLE_VIDEO : NS_GINGLE_AUDIO;
+ Session *session = session_manager_->CreateSession(jid().Str(), type);
+ session_map_[session->id()] = call;
+ return session;
+}
+
+bool MediaSessionClient::ParseAudioCodec(const buzz::XmlElement* element,
+ Codec* out) {
+ int id = GetXmlAttr(element, QN_ID, -1);
+ if (id < 0)
+ return false;
+
+ std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
+ int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0);
+ int bitrate = GetXmlAttr(element, QN_BITRATE, 0);
+ int channels = GetXmlAttr(element, QN_CHANNELS, 1);
+ *out = Codec(id, name, clockrate, bitrate, channels, 0);
+ return true;
+}
+
+bool MediaSessionClient::ParseVideoCodec(const buzz::XmlElement* element,
+ VideoCodec* out) {
+ int id = GetXmlAttr(element, QN_ID, -1);
+ if (id < 0)
+ return false;
+
+ std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
+ int width = GetXmlAttr(element, QN_WIDTH, 0);
+ int height = GetXmlAttr(element, QN_HEIGHT, 0);
+ int framerate = GetXmlAttr(element, QN_FRAMERATE, 0);
+ *out = VideoCodec(id, name, width, height, framerate, 0);
+ return true;
+}
+
+const FormatDescription* MediaSessionClient::ParseFormat(
+ const buzz::XmlElement* element) {
+ MediaSessionDescription* media = new MediaSessionDescription();
+ // Includes payloads of unknown type (xmlns). We need to know they
+ // are there so that we don't auto-add old codecs unless there are
+ // really no payload types, even of unknown type.
+ bool has_payload_types = false;
+ for (const buzz::XmlElement* payload_type = element->FirstElement();
+ payload_type != NULL;
+ payload_type = payload_type->NextElement()) {
+ has_payload_types = true;
+ const std::string& name = payload_type->Name().LocalPart();
+ if (name == QN_GINGLE_AUDIO_PAYLOADTYPE.LocalPart() ||
+ name == QN_GINGLE_VIDEO_PAYLOADTYPE.LocalPart()) {
+ if (payload_type->Name() == QN_GINGLE_AUDIO_PAYLOADTYPE) {
+ Codec acodec;
+ if (ParseAudioCodec(payload_type, &acodec)) {
+ media->voice().AddCodec(acodec);
+ }
+ } else if (payload_type->Name() == QN_GINGLE_VIDEO_PAYLOADTYPE) {
+ VideoCodec vcodec;
+ if (ParseVideoCodec(payload_type, &vcodec)) {
+ media->video().AddCodec(vcodec);
+ }
+ }
+ }
+ }
+
+ if (!has_payload_types) {
+ // For backward compatibility, we can assume the other client is
+ // an old version of Talk if it has no audio payload types at all.
+ media->voice().AddCodec(Codec(103, "ISAC", 16000, -1, 1, 1));
+ media->voice().AddCodec(Codec(0, "PCMU", 8000, 64000, 1, 0));
+ }
+
+ // get ssrcs, if present
+ const buzz::XmlElement* src_id;
+ src_id = element->FirstNamed(QN_GINGLE_AUDIO_SRCID);
+ if (src_id) {
+ media->voice().set_ssrc(strtoul(src_id->BodyText().c_str(),
+ NULL, 10));
+ }
+ src_id = element->FirstNamed(QN_GINGLE_VIDEO_SRCID);
+ if (src_id) {
+ media->video().set_ssrc(strtoul(src_id->BodyText().c_str(),
+ NULL, 10));
+ }
+
+ return media;
+}
+
+buzz::XmlElement* WriteAudioCodec(const Codec& codec) {
+ buzz::XmlElement* payload_type =
+ new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
+ AddXmlAttr(payload_type, QN_ID, codec.id);
+ payload_type->AddAttr(QN_NAME, codec.name);
+ if (codec.clockrate > 0)
+ AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
+ if (codec.bitrate > 0)
+ AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
+ if (codec.channels > 1)
+ AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
+ return payload_type;
+}
+
+buzz::XmlElement* WriteVideoCodec(const VideoCodec& codec) {
+ buzz::XmlElement* payload_type =
+ new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
+ AddXmlAttr(payload_type, QN_ID, codec.id);
+ payload_type->AddAttr(QN_NAME, codec.name);
+ AddXmlAttr(payload_type, QN_WIDTH, codec.width);
+ AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
+ AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
+ return payload_type;
+}
+
+buzz::XmlElement* MediaSessionClient::WriteFormat(
+ const FormatDescription* untyped_format) {
+ const MediaSessionDescription* media =
+ static_cast<const MediaSessionDescription*>(untyped_format);
+
+ bool has_video_codecs = !media->video().codecs().empty();
+ buzz::XmlElement* format_elem = new buzz::XmlElement(
+ has_video_codecs ? QN_GINGLE_VIDEO_FORMAT
+ : QN_GINGLE_AUDIO_FORMAT, true);
+
+ for (size_t i = 0; i < media->voice().codecs().size(); ++i) {
+ format_elem->AddElement(WriteAudioCodec(media->voice().codecs()[i]));
+ }
+ for (size_t i = 0; i < media->video().codecs().size(); ++i) {
+ format_elem->AddElement(WriteVideoCodec(media->video().codecs()[i]));
+ }
+
+ // Add ssrcs, if set.
+ if (media->voice().ssrc_set()) {
+ buzz::XmlElement* src_id =
+ new buzz::XmlElement(QN_GINGLE_AUDIO_SRCID, true);
+ if (media->voice().ssrc()) {
+ SetXmlBody(src_id, media->voice().ssrc());
+ }
+ format_elem->AddElement(src_id);
+ }
+ if (has_video_codecs && media->video().ssrc_set()) {
+ buzz::XmlElement* src_id =
+ new buzz::XmlElement(QN_GINGLE_VIDEO_SRCID, true);
+ if (media->video().ssrc()) {
+ SetXmlBody(src_id, media->video().ssrc());
+ }
+ format_elem->AddElement(src_id);
+ }
+
+
+ return format_elem;
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/mediasessionclient.h b/third_party/libjingle/source/talk/session/phone/mediasessionclient.h
new file mode 100644
index 0000000..d55b3b0
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/mediasessionclient.h
@@ -0,0 +1,212 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_MEDIASESSIONCLIENT_H_
+#define TALK_SESSION_PHONE_MEDIASESSIONCLIENT_H_
+
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include "talk/session/phone/call.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/session/phone/cryptoparams.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/sigslotrepeater.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/sessiondescription.h"
+
+namespace cricket {
+
+class Call;
+class MediaSessionDescription;
+
+class MediaSessionClient: public SessionClient, public sigslot::has_slots<> {
+ public:
+ MediaSessionClient(const buzz::Jid& jid, SessionManager *manager);
+ // Alternative constructor, allowing injection of media_engine
+ // and device_manager.
+ MediaSessionClient(const buzz::Jid& jid, SessionManager *manager,
+ MediaEngine* media_engine, DeviceManager* device_manager);
+ ~MediaSessionClient();
+
+ const buzz::Jid &jid() const { return jid_; }
+ SessionManager* session_manager() const { return session_manager_; }
+ ChannelManager* channel_manager() const { return channel_manager_; }
+
+ int GetCapabilities() { return channel_manager_->GetCapabilities(); }
+
+ Call *CreateCall(bool video = false, bool mux = false);
+ void DestroyCall(Call *call);
+
+ Call *GetFocus();
+ void SetFocus(Call *call);
+
+ void JoinCalls(Call *call_to_join, Call *call);
+
+ bool GetAudioInputDevices(std::vector<std::string>* names) {
+ return channel_manager_->GetAudioInputDevices(names);
+ }
+ bool GetAudioOutputDevices(std::vector<std::string>* names) {
+ return channel_manager_->GetAudioOutputDevices(names);
+ }
+ bool GetVideoCaptureDevices(std::vector<std::string>* names) {
+ return channel_manager_->GetVideoCaptureDevices(names);
+ }
+
+ bool SetAudioOptions(const std::string& in_name, const std::string& out_name,
+ int opts) {
+ return channel_manager_->SetAudioOptions(in_name, out_name, opts);
+ }
+ bool SetOutputVolume(int level) {
+ return channel_manager_->SetOutputVolume(level);
+ }
+ bool SetVideoOptions(const std::string& cam_device) {
+ return channel_manager_->SetVideoOptions(cam_device);
+ }
+
+ sigslot::signal2<Call *, Call *> SignalFocus;
+ sigslot::signal1<Call *> SignalCallCreate;
+ sigslot::signal1<Call *> SignalCallDestroy;
+ sigslot::repeater0<> SignalDevicesChange;
+
+ MediaSessionDescription* CreateOfferSessionDescription(bool video = false);
+ MediaSessionDescription* CreateAcceptSessionDescription(
+ const SessionDescription* offer);
+
+ private:
+ void Construct();
+ void OnSessionCreate(Session *session, bool received_initiate);
+ void OnSessionState(BaseSession *session, BaseSession::State state);
+ void OnSessionDestroy(Session *session);
+ virtual const FormatDescription* ParseFormat(const buzz::XmlElement* element);
+ virtual buzz::XmlElement* WriteFormat(const FormatDescription* format);
+ Session *CreateSession(Call *call);
+ static bool ParseAudioCodec(const buzz::XmlElement* element, Codec* out);
+ static bool ParseVideoCodec(const buzz::XmlElement* element, VideoCodec* out);
+
+
+
+ buzz::Jid jid_;
+ SessionManager* session_manager_;
+ Call *focus_call_;
+ ChannelManager *channel_manager_;
+ std::map<uint32, Call *> calls_;
+ std::map<SessionID, Call *> session_map_;
+
+ friend class Call;
+};
+
+// Parameters for a voice and/or video session.
+class MediaSessionDescription : public SessionDescription {
+ public:
+ // Base class with common options.
+ struct Content {
+ Content() : ssrc_(0), ssrc_set_(false), rtcp_mux_(false),
+ rtp_headers_disabled_(false) {}
+
+ uint32 ssrc() const { return ssrc_; }
+ bool ssrc_set() const { return ssrc_set_; }
+ void set_ssrc(uint32 ssrc) {
+ ssrc_ = ssrc;
+ ssrc_set_ = true;
+ }
+
+ bool rtcp_mux() const { return rtcp_mux_; }
+ void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; }
+
+ bool rtp_headers_disabled() const {
+ return rtp_headers_disabled_;
+ }
+ void set_rtp_headers_disabled(bool disable) {
+ rtp_headers_disabled_ = disable;
+ }
+
+ const std::vector<CryptoParams>& cryptos() const { return cryptos_; }
+ void AddCrypto(const CryptoParams& params) {
+ cryptos_.push_back(params);
+ }
+
+ template <class T> struct PreferenceSort {
+ bool operator()(T a, T b) { return a.preference > b.preference; }
+ };
+
+ uint32 ssrc_;
+ bool ssrc_set_;
+ bool rtcp_mux_;
+ bool rtp_headers_disabled_;
+ std::vector<CryptoParams> cryptos_;
+ };
+ // Voice-specific options.
+ struct VoiceContent : public Content {
+ const std::vector<Codec>& codecs() const { return codecs_; }
+ void AddCodec(const Codec& codec) {
+ codecs_.push_back(codec);
+ }
+ void SortCodecs() {
+ std::sort(codecs_.begin(), codecs_.end(), PreferenceSort<Codec>());
+ }
+ std::vector<Codec> codecs_;
+ };
+ // Video-specific options.
+ struct VideoContent : public Content {
+ const std::vector<VideoCodec>& codecs() const { return codecs_; }
+ void AddCodec(const VideoCodec& codec) {
+ codecs_.push_back(codec);
+ }
+ void SortCodecs() {
+ std::sort(codecs_.begin(), codecs_.end(), PreferenceSort<VideoCodec>());
+ }
+ std::vector<VideoCodec> codecs_;
+ };
+
+ const VoiceContent& voice() const { return voice_; }
+ VoiceContent& voice() { return voice_; }
+ const VideoContent& video() const { return video_; }
+ VideoContent& video() { return video_; }
+
+ void Sort() {
+ voice_.SortCodecs();
+ video_.SortCodecs();
+ }
+
+ const std::string &lang() const { return lang_; }
+ void set_lang(const std::string &lang) { lang_ = lang; }
+
+ private:
+ VoiceContent voice_;
+ VideoContent video_;
+ std::string lang_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIASESSIONCLIENT_H_
diff --git a/third_party/libjingle/source/talk/session/phone/soundclip.cc b/third_party/libjingle/source/talk/session/phone/soundclip.cc
new file mode 100644
index 0000000..f1069e0
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/soundclip.cc
@@ -0,0 +1,82 @@
+/*
+ * libjingle
+ * Copyright 2004--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/soundclip.h"
+
+namespace cricket {
+
+enum {
+ MSG_PLAYSOUND = 1,
+};
+
+struct PlaySoundMessageData : talk_base::MessageData {
+ PlaySoundMessageData(const void *c,
+ int l,
+ SoundclipMedia::SoundclipFlags f)
+ : clip(c),
+ len(l),
+ flags(f),
+ result(false) {
+ }
+
+ const void *clip;
+ int len;
+ SoundclipMedia::SoundclipFlags flags;
+ bool result;
+};
+
+Soundclip::Soundclip(talk_base::Thread *thread, SoundclipMedia *soundclip_media)
+ : worker_thread_(thread),
+ soundclip_media_(soundclip_media) {
+}
+
+bool Soundclip::PlaySound(const void *clip,
+ int len,
+ SoundclipMedia::SoundclipFlags flags) {
+ PlaySoundMessageData data(clip, len, flags);
+ worker_thread_->Send(this, MSG_PLAYSOUND, &data);
+ return data.result;
+}
+
+bool Soundclip::PlaySound_w(const void *clip,
+ int len,
+ SoundclipMedia::SoundclipFlags flags) {
+ return soundclip_media_->PlaySound(static_cast<const char *>(clip),
+ len,
+ flags);
+}
+
+void Soundclip::OnMessage(talk_base::Message *message) {
+ ASSERT(message->message_id == MSG_PLAYSOUND);
+ PlaySoundMessageData *data =
+ static_cast<PlaySoundMessageData *>(message->pdata);
+ data->result = PlaySound_w(data->clip,
+ data->len,
+ data->flags);
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/soundclip.h b/third_party/libjingle/source/talk/session/phone/soundclip.h
new file mode 100644
index 0000000..4038477
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/soundclip.h
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2004--2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_SOUNDCLIP_H_
+#define TALK_SESSION_PHONE_SOUNDCLIP_H_
+
+#include "talk/base/scoped_ptr.h"
+#include "talk/session/phone/mediaengine.h"
+
+namespace talk_base {
+
+class Thread;
+
+}
+
+namespace cricket {
+
+// Soundclip wraps SoundclipMedia to support marshalling calls to the proper
+// thread.
+class Soundclip : private talk_base::MessageHandler {
+ public:
+ Soundclip(talk_base::Thread* thread, SoundclipMedia* soundclip_media);
+
+ // Plays a sound out to the speakers with the given audio stream. The stream
+ // must be 16-bit little-endian 16 kHz PCM. If a stream is already playing
+ // on this Soundclip, it is stopped. If clip is NULL, nothing is played.
+ // Returns whether it was successful.
+ bool PlaySound(const void* clip,
+ int len,
+ SoundclipMedia::SoundclipFlags flags);
+
+ private:
+ bool PlaySound_w(const void* clip,
+ int len,
+ SoundclipMedia::SoundclipFlags flags);
+
+ // From MessageHandler
+ virtual void OnMessage(talk_base::Message* message);
+
+ talk_base::Thread* worker_thread_;
+ talk_base::scoped_ptr<SoundclipMedia> soundclip_media_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_SOUNDCLIP_H_
diff --git a/third_party/libjingle/source/talk/session/phone/srtpfilter.cc b/third_party/libjingle/source/talk/session/phone/srtpfilter.cc
new file mode 100644
index 0000000..cbe5c17
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/srtpfilter.cc
@@ -0,0 +1,428 @@
+/*
+ * libjingle
+ * Copyright 2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#undef HAVE_CONFIG_H // talk's config.h conflicts with the one included by the
+ // libsrtp headers. Don't use it.
+#include "talk/session/phone/srtpfilter.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include "talk/base/base64.h"
+#include "talk/base/logging.h"
+
+// TODO(juberti):For the XCode build, we force SRTP (b/2500074)
+#if defined(OSX) && !defined(HAVE_SRTP)
+#define HAVE_SRTP 1
+#endif
+
+// Enable this line to turn on SRTP debugging
+// #define SRTP_DEBUG
+
+#ifdef HAVE_SRTP
+#include "third_party/libsrtp/include/srtp.h"
+#ifdef _DEBUG
+extern "C" debug_module_t mod_srtp;
+#endif
+#else
+// SrtpFilter needs that constant.
+#define SRTP_MASTER_KEY_LEN 30
+#endif // HAVE_SRTP
+
+namespace cricket {
+
+const std::string& CS_DEFAULT = CS_AES_CM_128_HMAC_SHA1_80;
+const std::string CS_AES_CM_128_HMAC_SHA1_80 = "AES_CM_128_HMAC_SHA1_80";
+const std::string CS_AES_CM_128_HMAC_SHA1_32 = "AES_CM_128_HMAC_SHA1_32";
+
+SrtpFilter::SrtpFilter() : state_(ST_INIT) {
+}
+
+SrtpFilter::~SrtpFilter() {
+}
+
+bool SrtpFilter::IsActive() const {
+ return (state_ == ST_ACTIVE);
+}
+
+bool SrtpFilter::SetOffer(const std::vector<CryptoParams>& offer_params,
+ DescriptionSource source) {
+ bool ret = false;
+ if (state_ == ST_INIT) {
+ ret = StoreParams(offer_params, source);
+ } else {
+ LOG(LS_ERROR) << "Invalid state for SRTP offer";
+ }
+ return ret;
+}
+
+bool SrtpFilter::SetAnswer(const std::vector<CryptoParams>& answer_params,
+ DescriptionSource source) {
+ bool ret = false;
+ if ((state_ == ST_SENTOFFER && source == DS_REMOTE) ||
+ (state_ == ST_RECEIVEDOFFER && source == DS_LOCAL)) {
+ // If the answer requests crypto, finalize the parameters and apply them.
+ // Otherwise, complete the negotiation of a unencrypted session.
+ if (!answer_params.empty()) {
+ CryptoParams selected_params;
+ ret = NegotiateParams(answer_params, &selected_params);
+ if (ret) {
+ if (state_ == ST_SENTOFFER) {
+ ret = ApplyParams(selected_params, answer_params[0]);
+ } else { // ST_RECEIVEDOFFER
+ ret = ApplyParams(answer_params[0], selected_params);
+ }
+ }
+ } else {
+ ret = ResetParams();
+ }
+ } else {
+ LOG(LS_ERROR) << "Invalid state for SRTP answer";
+ }
+ return ret;
+}
+
+bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
+ if (!IsActive()) {
+ return false;
+ }
+ return send_session_.ProtectRtp(p, in_len, max_len, out_len);
+}
+
+bool SrtpFilter::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
+ if (!IsActive()) {
+ return false;
+ }
+ return send_session_.ProtectRtcp(p, in_len, max_len, out_len);
+}
+
+bool SrtpFilter::UnprotectRtp(void* p, int in_len, int* out_len) {
+ if (!IsActive()) {
+ return false;
+ }
+ return recv_session_.UnprotectRtp(p, in_len, out_len);
+}
+
+bool SrtpFilter::UnprotectRtcp(void* p, int in_len, int* out_len) {
+ if (!IsActive()) {
+ return false;
+ }
+ return recv_session_.UnprotectRtcp(p, in_len, out_len);
+}
+
+
+bool SrtpFilter::StoreParams(const std::vector<CryptoParams>& params,
+ DescriptionSource source) {
+ offer_params_ = params;
+ state_ = (source == DS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER;
+ return true;
+}
+
+bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& answer_params,
+ CryptoParams* selected_params) {
+ // We're processing an accept. We should have exactly one set of params,
+ // unless the offer didn't mention crypto, in which case we shouldn't be here.
+ bool ret = (answer_params.size() == 1U && !offer_params_.empty());
+ if (ret) {
+ // We should find a match between the answer params and the offered params.
+ std::vector<CryptoParams>::const_iterator it;
+ for (it = offer_params_.begin(); it != offer_params_.end(); ++it) {
+ if (answer_params[0].Matches(*it)) {
+ break;
+ }
+ }
+
+ if (it != offer_params_.end()) {
+ *selected_params = *it;
+ } else {
+ ret = false;
+ }
+ }
+
+ if (!ret) {
+ LOG(LS_WARNING) << "Invalid parameters in SRTP answer";
+ }
+ return ret;
+}
+
+bool SrtpFilter::ApplyParams(const CryptoParams& send_params,
+ const CryptoParams& recv_params) {
+ // TODO(juberti): Zero these buffers after use.
+ bool ret;
+ uint8 send_key[SRTP_MASTER_KEY_LEN], recv_key[SRTP_MASTER_KEY_LEN];
+ ret = (ParseKeyParams(send_params.key_params, send_key, sizeof(send_key)) &&
+ ParseKeyParams(recv_params.key_params, recv_key, sizeof(recv_key)));
+ if (ret) {
+ ret = (send_session_.SetSend(send_params.cipher_suite,
+ send_key, sizeof(send_key)) &&
+ recv_session_.SetRecv(recv_params.cipher_suite,
+ recv_key, sizeof(recv_key)));
+ }
+ if (ret) {
+ offer_params_.clear();
+ state_ = ST_ACTIVE;
+ } else {
+ LOG(LS_WARNING) << "Failed to apply negotiated SRTP parameters";
+ }
+ return ret;
+}
+
+bool SrtpFilter::ResetParams() {
+ offer_params_.clear();
+ state_ = ST_INIT;
+ return true;
+}
+
+bool SrtpFilter::ParseKeyParams(const std::string& key_params,
+ uint8* key, int len) {
+ // example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2"
+
+ // Fail if key-method is wrong.
+ if (key_params.find("inline:") != 0) {
+ return false;
+ }
+
+ // Fail if base64 decode fails, or the key is the wrong size.
+ std::string key_b64(key_params.substr(7)), key_str;
+ if (!talk_base::Base64::Decode(key_b64, talk_base::Base64::DO_STRICT,
+ &key_str, NULL) ||
+ static_cast<int>(key_str.size()) != len) {
+ return false;
+ }
+
+ memcpy(key, key_str.c_str(), len);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// SrtpSession
+
+#ifdef HAVE_SRTP
+
+bool SrtpSession::inited_ = false;
+std::list<SrtpSession*> SrtpSession::sessions_;
+
+SrtpSession::SrtpSession()
+ : session_(NULL), rtp_auth_tag_len_(0), rtcp_auth_tag_len_(0) {
+ sessions_.push_back(this);
+}
+
+SrtpSession::~SrtpSession() {
+ sessions_.erase(std::find(sessions_.begin(), sessions_.end(), this));
+ if (session_) {
+ srtp_dealloc(session_);
+ }
+}
+
+bool SrtpSession::SetSend(const std::string& cs, const uint8* key, int len) {
+ return SetKey(ssrc_any_outbound, cs, key, len);
+}
+
+bool SrtpSession::SetRecv(const std::string& cs, const uint8* key, int len) {
+ return SetKey(ssrc_any_inbound, cs, key, len);
+}
+
+bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
+ if (!session_)
+ return false;
+ int need_len = in_len + rtp_auth_tag_len_; // NOLINT
+ if (max_len < need_len)
+ return false;
+ *out_len = in_len;
+ int err = srtp_protect(session_, p, out_len);
+ if (err != err_status_ok) {
+ LOG(LS_WARNING) << "Failed to protect SRTP packet, err=" << err;
+ return false;
+ }
+ return true;
+}
+
+bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
+ if (!session_)
+ return false;
+ int need_len = in_len + sizeof(uint32) + rtcp_auth_tag_len_; // NOLINT
+ if (max_len < need_len)
+ return false;
+ *out_len = in_len;
+ int err = srtp_protect_rtcp(session_, p, out_len);
+ if (err != err_status_ok) {
+ LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err;
+ return false;
+ }
+ return true;
+}
+
+bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) {
+ if (!session_)
+ return false;
+ *out_len = in_len;
+ int err = srtp_unprotect(session_, p, out_len);
+ if (err != err_status_ok) {
+ LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err;
+ return false;
+ }
+ return true;
+}
+
+bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) {
+ if (!session_)
+ return false;
+ *out_len = in_len;
+ int err = srtp_unprotect_rtcp(session_, p, out_len);
+ if (err != err_status_ok) {
+ LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err;
+ return false;
+ }
+ return true;
+}
+
+bool SrtpSession::SetKey(int type, const std::string& cs,
+ const uint8* key, int len) {
+ if (session_) {
+ return false;
+ }
+
+ if (!Init()) {
+ return false;
+ }
+
+ srtp_policy_t policy;
+ memset(&policy, 0, sizeof(policy));
+
+ if (cs == CS_AES_CM_128_HMAC_SHA1_80) {
+ crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
+ crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
+ } else if (cs == CS_AES_CM_128_HMAC_SHA1_32) {
+ crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); // rtp is 32,
+ crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // rtcp still 80
+ } else {
+ return false;
+ }
+
+ if (!key || len != SRTP_MASTER_KEY_LEN) {
+ return false;
+ }
+
+ policy.ssrc.type = static_cast<ssrc_type_t>(type);
+ policy.ssrc.value = 0;
+ policy.key = const_cast<uint8*>(key);
+ // TODO(astor) parse window size from WSH session-param
+ policy.window_size = 1024;
+ policy.allow_repeat_tx = 1;
+ policy.next = NULL;
+
+ int err = srtp_create(&session_, &policy);
+ if (err != err_status_ok) {
+ LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err;
+ return false;
+ }
+
+ rtp_auth_tag_len_ = policy.rtp.auth_tag_len;
+ rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len;
+ return true;
+}
+
+bool SrtpSession::Init() {
+ if (!inited_) {
+ int err;
+#ifdef DEBUG_SRTP
+ debug_on(mod_srtp);
+#endif
+ err = srtp_init();
+ if (err != err_status_ok) {
+ LOG(LS_ERROR) << "Failed to init SRTP, err=" << err;
+ return false;
+ }
+
+ err = srtp_install_event_handler(&SrtpSession::HandleEventThunk);
+ if (err != err_status_ok) {
+ LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err;
+ return false;
+ }
+
+ inited_ = true;
+ }
+
+ return true;
+}
+
+void SrtpSession::HandleEvent(const srtp_event_data_t* ev) {
+ // TODO(juberti): Do something about events.
+}
+
+void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) {
+ for (std::list<SrtpSession*>::iterator it = sessions_.begin();
+ it != sessions_.end(); ++it) {
+ if ((*it)->session_ == ev->session) {
+ (*it)->HandleEvent(ev);
+ break;
+ }
+ }
+}
+
+#else // !HAVE_SRTP
+
+namespace {
+bool SrtpNotAvailable(const char *func) {
+ LOG(LS_ERROR) << func << ": SRTP is not available on your system.";
+ return false;
+}
+} // anonymous namespace
+
+SrtpSession::SrtpSession() {
+ LOG(WARNING) << "SRTP implementation is missing.";
+}
+
+SrtpSession::~SrtpSession() {
+}
+
+bool SrtpSession::SetSend(const std::string& cs, const uint8* key, int len) {
+ return SrtpNotAvailable(__FUNCTION__);
+}
+
+bool SrtpSession::SetRecv(const std::string& cs, const uint8* key, int len) {
+ return SrtpNotAvailable(__FUNCTION__);
+}
+
+bool SrtpSession::ProtectRtp(void* data, int in_len, int max_len, int* out_len) {
+ return SrtpNotAvailable(__FUNCTION__);
+}
+
+bool SrtpSession::ProtectRtcp(void* data, int in_len, int max_len, int* out_len) {
+ return SrtpNotAvailable(__FUNCTION__);
+}
+
+bool SrtpSession::UnprotectRtp(void* data, int in_len, int* out_len) {
+ return SrtpNotAvailable(__FUNCTION__);
+}
+
+bool SrtpSession::UnprotectRtcp(void* data, int in_len, int* out_len) {
+ return SrtpNotAvailable(__FUNCTION__);
+}
+
+#endif // HAVE_SRTP
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/srtpfilter.h b/third_party/libjingle/source/talk/session/phone/srtpfilter.h
new file mode 100644
index 0000000..292ccb6
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/srtpfilter.h
@@ -0,0 +1,145 @@
+/*
+ * libjingle
+ * Copyright 2009, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_SRTPFILTER_H_
+#define TALK_SESSION_PHONE_SRTPFILTER_H_
+
+#include <list>
+#include <string>
+#include <vector>
+#include "talk/base/basictypes.h"
+#include "talk/session/phone/cryptoparams.h"
+#include "talk/p2p/base/sessiondescription.h"
+
+// Forward declaration to avoid pulling in libsrtp headers here
+struct srtp_event_data_t;
+struct srtp_ctx_t;
+typedef srtp_ctx_t* srtp_t;
+struct srtp_policy_t;
+
+namespace cricket {
+
+// Cipher suite to use for SRTP. Typically a 80-bit HMAC will be used, except
+// in applications (voice) where the additional bandwidth may be significant.
+// A 80-bit HMAC is always used for SRTCP.
+extern const std::string& CS_DEFAULT;
+// 128-bit AES with 80-bit SHA-1 HMAC.
+extern const std::string CS_AES_CM_128_HMAC_SHA1_80;
+// 128-bit AES with 32-bit SHA-1 HMAC.
+extern const std::string CS_AES_CM_128_HMAC_SHA1_32;
+
+// Class that wraps a libSRTP session. Used internally by SrtpFilter, below.
+class SrtpSession {
+ public:
+ SrtpSession();
+ ~SrtpSession();
+
+ // Configures the session for sending data using the specified
+ // cipher-suite and key. Receiving must be done by a separate session.
+ bool SetSend(const std::string& cs, const uint8* key, int len);
+ // Configures the session for receiving data using the specified
+ // cipher-suite and key. Sending must be done by a separate session.
+ bool SetRecv(const std::string& cs, const uint8* key, int len);
+
+ // Encrypts/signs an individual RTP/RTCP packet, in-place.
+ // If an HMAC is used, this will increase the packet size.
+ bool ProtectRtp(void* data, int in_len, int max_len, int* out_len);
+ bool ProtectRtcp(void* data, int in_len, int max_len, int* out_len);
+ // Decrypts/verifies an invidiual RTP/RTCP packet.
+ // If an HMAC is used, this will decrease the packet size.
+ bool UnprotectRtp(void* data, int in_len, int* out_len);
+ bool UnprotectRtcp(void* data, int in_len, int* out_len);
+
+ private:
+ bool SetKey(int type, const std::string& cs, const uint8* key, int len);
+ static bool Init();
+ void HandleEvent(const srtp_event_data_t* ev);
+ static void HandleEventThunk(srtp_event_data_t* ev);
+
+ srtp_t session_;
+ int rtp_auth_tag_len_;
+ int rtcp_auth_tag_len_;
+ static bool inited_;
+ static std::list<SrtpSession*> sessions_;
+};
+
+// Class to transform SRTP to/from RTP.
+// Initialize by calling SetSend with the local security params, then call
+// SetRecv once the remote security params are received. At that point
+// Protect/UnprotectRt(c)p can be called to encrypt/decrypt data.
+// TODO(juberti): Figure out concurrency policy for SrtpFilter.
+class SrtpFilter {
+ public:
+ SrtpFilter();
+ ~SrtpFilter();
+
+ // Whether the filter is active (i.e. crypto has been properly negotiated).
+ bool IsActive() const;
+
+ // Indicates which crypto algorithms and keys were contained in the offer.
+ // offer_params should contain a list of available parameters to use, or none,
+ // if crypto is not desired. This must be called before SetAnswer.
+ bool SetOffer(const std::vector<CryptoParams>& offer_params,
+ DescriptionSource source);
+ // Indicates which crypto algorithms and keys were contained in the answer.
+ // answer_params should contain the negotiated parameters, which may be none,
+ // if crypto was not desired or could not be negotiated (and not required).
+ // This must be called after SetOffer. If crypto negotiation completes
+ // successfully, this will advance the filter to the active state.
+ bool SetAnswer(const std::vector<CryptoParams>& answer_params,
+ DescriptionSource source);
+
+ // Encrypts/signs an individual RTP/RTCP packet, in-place.
+ // If an HMAC is used, this will increase the packet size.
+ bool ProtectRtp(void* data, int in_len, int max_len, int* out_len);
+ bool ProtectRtcp(void* data, int in_len, int max_len, int* out_len);
+ // Decrypts/verifies an invidiual RTP/RTCP packet.
+ // If an HMAC is used, this will decrease the packet size.
+ bool UnprotectRtp(void* data, int in_len, int* out_len);
+ bool UnprotectRtcp(void* data, int in_len, int* out_len);
+
+ protected:
+ bool StoreParams(const std::vector<CryptoParams>& offer_params,
+ DescriptionSource source);
+ bool NegotiateParams(const std::vector<CryptoParams>& answer_params,
+ CryptoParams* selected_params);
+ bool ApplyParams(const CryptoParams& send_params,
+ const CryptoParams& recv_params);
+ bool ResetParams();
+ static bool ParseKeyParams(const std::string& params, uint8* key, int len);
+
+ private:
+ enum State { ST_INIT, ST_SENTOFFER, ST_RECEIVEDOFFER, ST_ACTIVE };
+ State state_;
+ std::vector<CryptoParams> offer_params_;
+ SrtpSession send_session_;
+ SrtpSession recv_session_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_SRTPFILTER_H_
diff --git a/third_party/libjingle/source/talk/session/phone/v4llookup.cc b/third_party/libjingle/source/talk/session/phone/v4llookup.cc
new file mode 100644
index 0000000..7a283a0
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/v4llookup.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2009, Google Inc.
+ * Author: lexnikitin
+ *
+ * V4LLookup provides basic functionality to work with V2L2 devices in Linux
+ * The functionality is implemented as a class with virtual methods for
+ * the purpose of unit testing.
+ */
+#include "talk/session/phone/v4llookup.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <cstring>
+
+#include "talk/base/logging.h"
+
+namespace cricket {
+
+V4LLookup *V4LLookup::v4l_lookup_ = new V4LLookup();
+
+bool V4LLookup::CheckIsV4L2Device(const std::string& device_path) {
+ // check device major/minor numbers are in the range for video devices.
+ struct stat s;
+
+ if (lstat(device_path.c_str(), &s) != 0 || !S_ISCHR(s.st_mode)) return false;
+
+ int video_fd = -1;
+ bool is_v4l2 = false;
+
+ // check major/minur device numbers are in range for video device
+ if (major(s.st_rdev) == 81) {
+ dev_t num = minor(s.st_rdev);
+ if (num <= 63 && num >= 0) {
+ video_fd = ::open(device_path.c_str(), O_RDONLY | O_NONBLOCK);
+ if ((video_fd >= 0) || (errno == EBUSY)) {
+ ::v4l2_capability video_caps;
+ memset(&video_caps, 0, sizeof(video_caps));
+
+ if ((errno == EBUSY) ||
+ (::ioctl(video_fd, VIDIOC_QUERYCAP, &video_caps) >= 0 &&
+ (video_caps.capabilities & V4L2_CAP_VIDEO_CAPTURE))) {
+ LOG(LS_INFO) << "Found V4L2 capture device " << device_path;
+
+ is_v4l2 = true;
+ } else {
+ LOG(LS_ERROR) << "VIDIOC_QUERYCAP failed for " << device_path;
+ }
+ } else {
+ LOG(LS_ERROR) << "Failed to open " << device_path;
+ }
+ }
+ }
+
+ if (video_fd >= 0)
+ ::close(video_fd);
+
+ return is_v4l2;
+}
+
+}; // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/phone/v4llookup.h b/third_party/libjingle/source/talk/session/phone/v4llookup.h
new file mode 100644
index 0000000..64313a8
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/v4llookup.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009, Google Inc.
+ * Author: lexnikitin
+ *
+ * V4LLookup provides basic functionality to work with V2L2 devices in Linux
+ * The functionality is implemented as a class with virtual methods for
+ * the purpose of unit testing.
+ */
+#ifndef TALK_SESSION_PHONE_V4LLOOKUP_H_
+#define TALK_SESSION_PHONE_V4LLOOKUP_H_
+
+#include <string>
+
+#ifdef LINUX
+namespace cricket {
+class V4LLookup {
+ public:
+ virtual ~V4LLookup() {}
+
+ static bool IsV4L2Device(const std::string& device_path) {
+ return GetV4LLookup()->CheckIsV4L2Device(device_path);
+ }
+
+ static void SetV4LLookup(V4LLookup* v4l_lookup) {
+ v4l_lookup_ = v4l_lookup;
+ }
+
+ static V4LLookup* GetV4LLookup() {
+ return v4l_lookup_;
+ }
+
+ protected:
+ static V4LLookup* v4l_lookup_;
+ // Making virtual so it is easier to mock
+ virtual bool CheckIsV4L2Device(const std::string& device_path);
+
+};
+} // namespace cricket
+#endif
+#endif // TALK_SESSION_PHONE_V4LLOOKUP_H_
diff --git a/third_party/libjingle/source/talk/session/phone/voicechannel.h b/third_party/libjingle/source/talk/session/phone/voicechannel.h
new file mode 100644
index 0000000..95de637
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/phone/voicechannel.h
@@ -0,0 +1,33 @@
+/*
+ * libjingle
+ * Copyright 2004--2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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/session/phone/channel.h"
+
+#endif // _VOICECHANNEL_H_
diff --git a/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc b/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc
new file mode 100644
index 0000000..3df3bfc
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc
@@ -0,0 +1,567 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "pseudotcpchannel.h"
+
+using namespace talk_base;
+
+namespace cricket {
+
+extern const talk_base::ConstantLabel SESSION_STATES[];
+
+// MSG_WK_* - worker thread messages
+// MSG_ST_* - stream thread messages
+// MSG_SI_* - signal thread messages
+
+enum {
+ MSG_WK_CLOCK = 1,
+ MSG_WK_PURGE,
+ MSG_ST_EVENT,
+ MSG_SI_DESTROYCHANNEL,
+ MSG_SI_DESTROY,
+};
+
+struct EventData : public MessageData {
+ int event, error;
+ EventData(int ev, int err = 0) : event(ev), error(err) { }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel::InternalStream
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel::InternalStream : public StreamInterface {
+public:
+ InternalStream(PseudoTcpChannel* parent);
+ virtual ~InternalStream();
+
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+
+private:
+ // parent_ is accessed and modified exclusively on the event thread, to
+ // avoid thread contention. This means that the PseudoTcpChannel cannot go
+ // away until after it receives a Close() from TunnelStream.
+ PseudoTcpChannel* parent_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel
+// Member object lifetime summaries:
+// session_ - passed in constructor, cleared when channel_ goes away.
+// channel_ - created in Connect, destroyed when session_ or tcp_ goes away.
+// tcp_ - created in Connect, destroyed when channel_ goes away, or connection
+// closes.
+// worker_thread_ - created when channel_ is created, purged when channel_ is
+// destroyed.
+// stream_ - created in GetStream, destroyed by owner at arbitrary time.
+// this - created in constructor, destroyed when worker_thread_ and stream_
+// are both gone.
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Signal thread methods
+//
+
+PseudoTcpChannel::PseudoTcpChannel(Thread* stream_thread, Session* session)
+ : signal_thread_(session->session_manager()->signaling_thread()),
+ worker_thread_(NULL),
+ stream_thread_(stream_thread),
+ session_(session), channel_(NULL), tcp_(NULL), stream_(NULL),
+ stream_readable_(false), pending_read_event_(false),
+ ready_to_connect_(false) {
+ ASSERT(signal_thread_->IsCurrent());
+ ASSERT(NULL != session_);
+}
+
+PseudoTcpChannel::~PseudoTcpChannel() {
+ ASSERT(signal_thread_->IsCurrent());
+ ASSERT(worker_thread_ == NULL);
+ ASSERT(session_ == NULL);
+ ASSERT(channel_ == NULL);
+ ASSERT(stream_ == NULL);
+ ASSERT(tcp_ == NULL);
+}
+
+bool PseudoTcpChannel::Connect(const std::string& channel_name) {
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+
+ if (channel_)
+ return false;
+
+ ASSERT(session_ != NULL);
+ worker_thread_ = session_->session_manager()->worker_thread();
+ channel_ = session_->CreateChannel(channel_name);
+ channel_name_ = channel_name;
+ channel_->SetOption(Socket::OPT_DONTFRAGMENT, 1);
+
+ channel_->SignalDestroyed.connect(this,
+ &PseudoTcpChannel::OnChannelDestroyed);
+ channel_->SignalWritableState.connect(this,
+ &PseudoTcpChannel::OnChannelWritableState);
+ channel_->SignalReadPacket.connect(this,
+ &PseudoTcpChannel::OnChannelRead);
+ channel_->SignalRouteChange.connect(this,
+ &PseudoTcpChannel::OnChannelConnectionChanged);
+
+ ASSERT(tcp_ == NULL);
+ tcp_ = new PseudoTcp(this, 0);
+ if (session_->initiator()) {
+ // Since we may try several protocols and network adapters that won't work,
+ // waiting until we get our first writable notification before initiating
+ // TCP negotiation.
+ ready_to_connect_ = true;
+ }
+
+ return true;
+}
+
+StreamInterface* PseudoTcpChannel::GetStream() {
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ ASSERT(NULL != session_);
+ if (!stream_)
+ stream_ = new PseudoTcpChannel::InternalStream(this);
+ //TODO("should we disallow creation of new stream at some point?");
+ return stream_;
+}
+
+void PseudoTcpChannel::OnChannelDestroyed(TransportChannel* channel) {
+ LOG_F(LS_INFO) << "(" << channel->name() << ")";
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ ASSERT(channel == channel_);
+ signal_thread_->Clear(this, MSG_SI_DESTROYCHANNEL);
+ // When MSG_WK_PURGE is received, we know there will be no more messages from
+ // the worker thread.
+ worker_thread_->Clear(this, MSG_WK_CLOCK);
+ worker_thread_->Post(this, MSG_WK_PURGE);
+ session_ = NULL;
+ channel_ = NULL;
+ if ((stream_ != NULL)
+ && ((tcp_ == NULL) || (tcp_->State() != PseudoTcp::TCP_CLOSED)))
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, 0));
+ if (tcp_) {
+ tcp_->Close(true);
+ AdjustClock();
+ }
+ SignalChannelClosed(this);
+}
+
+void PseudoTcpChannel::OnSessionTerminate(Session* session) {
+ // When the session terminates before we even connected
+ CritScope lock(&cs_);
+ if (session_ != NULL && channel_ == NULL) {
+ ASSERT(session == session_);
+ ASSERT(worker_thread_ == NULL);
+ ASSERT(tcp_ == NULL);
+ LOG(LS_INFO) << "Destroying unconnected PseudoTcpChannel";
+ session_ = NULL;
+ if (stream_ != NULL)
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, -1));
+ }
+}
+
+//
+// Stream thread methods
+//
+
+StreamState PseudoTcpChannel::GetState() const {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!session_)
+ return SS_CLOSED;
+ if (!tcp_)
+ return SS_OPENING;
+ switch (tcp_->State()) {
+ case PseudoTcp::TCP_LISTEN:
+ case PseudoTcp::TCP_SYN_SENT:
+ case PseudoTcp::TCP_SYN_RECEIVED:
+ return SS_OPENING;
+ case PseudoTcp::TCP_ESTABLISHED:
+ return SS_OPEN;
+ case PseudoTcp::TCP_CLOSED:
+ default:
+ return SS_CLOSED;
+ }
+}
+
+StreamResult PseudoTcpChannel::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!tcp_)
+ return SR_BLOCK;
+
+ stream_readable_ = false;
+ int result = tcp_->Recv(static_cast<char*>(buffer), buffer_len);
+ //LOG_F(LS_VERBOSE) << "Recv returned: " << result;
+ if (result > 0) {
+ if (read)
+ *read = result;
+ // PseudoTcp doesn't currently support repeated Readable signals. Simulate
+ // them here.
+ stream_readable_ = true;
+ if (!pending_read_event_) {
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_READ), true);
+ }
+ return SR_SUCCESS;
+ } else if (IsBlockingError(tcp_->GetError())) {
+ return SR_BLOCK;
+ } else {
+ if (error)
+ *error = tcp_->GetError();
+ return SR_ERROR;
+ }
+ // This spot is never reached.
+}
+
+StreamResult PseudoTcpChannel::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!tcp_)
+ return SR_BLOCK;
+ int result = tcp_->Send(static_cast<const char*>(data), data_len);
+ //LOG_F(LS_VERBOSE) << "Send returned: " << result;
+ if (result > 0) {
+ if (written)
+ *written = result;
+ return SR_SUCCESS;
+ } else if (IsBlockingError(tcp_->GetError())) {
+ return SR_BLOCK;
+ } else {
+ if (error)
+ *error = tcp_->GetError();
+ return SR_ERROR;
+ }
+ // This spot is never reached.
+}
+
+void PseudoTcpChannel::Close() {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ stream_ = NULL;
+ // Clear out any pending event notifications
+ stream_thread_->Clear(this, MSG_ST_EVENT);
+ if (tcp_) {
+ tcp_->Close(false);
+ AdjustClock();
+ } else {
+ CheckDestroy();
+ }
+}
+
+//
+// Worker thread methods
+//
+
+void PseudoTcpChannel::OnChannelWritableState(TransportChannel* channel) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+ if (!ready_to_connect_ || !channel->writable())
+ return;
+
+ ready_to_connect_ = false;
+ tcp_->Connect();
+ AdjustClock();
+}
+
+void PseudoTcpChannel::OnChannelRead(TransportChannel* channel,
+ const char* data, size_t size) {
+ //LOG_F(LS_VERBOSE) << "(" << size << ")";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+ tcp_->NotifyPacket(data, size);
+ AdjustClock();
+}
+
+void PseudoTcpChannel::OnChannelConnectionChanged(TransportChannel* channel,
+ const SocketAddress& addr) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+
+ uint16 mtu = 1280; // safe default
+ talk_base::scoped_ptr<Socket> mtu_socket(
+ worker_thread_->socketserver()->CreateSocket(SOCK_DGRAM));
+ if (mtu_socket->Connect(addr) < 0 ||
+ mtu_socket->EstimateMTU(&mtu) < 0) {
+ LOG_F(LS_WARNING) << "Failed to estimate MTU, error="
+ << mtu_socket->GetError();
+ }
+
+ LOG_F(LS_VERBOSE) << "Using MTU of " << mtu << " bytes";
+ tcp_->NotifyMTU(mtu);
+ AdjustClock();
+}
+
+void PseudoTcpChannel::OnTcpOpen(PseudoTcp* tcp) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_) {
+ stream_readable_ = true;
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT,
+ new EventData(SE_OPEN | SE_READ | SE_WRITE));
+ }
+}
+
+void PseudoTcpChannel::OnTcpReadable(PseudoTcp* tcp) {
+ //LOG_F(LS_VERBOSE);
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_) {
+ stream_readable_ = true;
+ if (!pending_read_event_) {
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_READ));
+ }
+ }
+}
+
+void PseudoTcpChannel::OnTcpWriteable(PseudoTcp* tcp) {
+ //LOG_F(LS_VERBOSE);
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_)
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_WRITE));
+}
+
+void PseudoTcpChannel::OnTcpClosed(PseudoTcp* tcp, uint32 nError) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_)
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, nError));
+}
+
+//
+// Multi-thread methods
+//
+
+void PseudoTcpChannel::OnMessage(Message* pmsg) {
+ if (pmsg->message_id == MSG_WK_CLOCK) {
+
+ ASSERT(worker_thread_->IsCurrent());
+ //LOG(LS_INFO) << "PseudoTcpChannel::OnMessage(MSG_WK_CLOCK)";
+ CritScope lock(&cs_);
+ if (tcp_) {
+ tcp_->NotifyClock(PseudoTcp::Now());
+ AdjustClock(false);
+ }
+
+ } else if (pmsg->message_id == MSG_WK_PURGE) {
+
+ ASSERT(worker_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_WK_PURGE)";
+ // At this point, we know there are no additional worker thread messages.
+ CritScope lock(&cs_);
+ ASSERT(NULL == session_);
+ ASSERT(NULL == channel_);
+ worker_thread_ = NULL;
+ CheckDestroy();
+
+ } else if (pmsg->message_id == MSG_ST_EVENT) {
+
+ ASSERT(stream_thread_->IsCurrent());
+ //LOG(LS_INFO) << "PseudoTcpChannel::OnMessage(MSG_ST_EVENT, "
+ // << data->event << ", " << data->error << ")";
+ ASSERT(stream_ != NULL);
+ EventData* data = static_cast<EventData*>(pmsg->pdata);
+ if (data->event & SE_READ) {
+ CritScope lock(&cs_);
+ pending_read_event_ = false;
+ }
+ stream_->SignalEvent(stream_, data->event, data->error);
+ delete data;
+
+ } else if (pmsg->message_id == MSG_SI_DESTROYCHANNEL) {
+
+ ASSERT(signal_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_SI_DESTROYCHANNEL)";
+ ASSERT(session_ != NULL);
+ ASSERT(channel_ != NULL);
+ session_->DestroyChannel(channel_);
+
+ } else if (pmsg->message_id == MSG_SI_DESTROY) {
+
+ ASSERT(signal_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_SI_DESTROY)";
+ // The message queue is empty, so it is safe to destroy ourselves.
+ delete this;
+
+ } else {
+ ASSERT(false);
+ }
+}
+
+IPseudoTcpNotify::WriteResult PseudoTcpChannel::TcpWritePacket(
+ PseudoTcp* tcp, const char* buffer, size_t len) {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(tcp == tcp_);
+ ASSERT(NULL != channel_);
+ int sent = channel_->SendPacket(buffer, len);
+ if (sent > 0) {
+ //LOG_F(LS_VERBOSE) << "(" << sent << ") Sent";
+ return IPseudoTcpNotify::WR_SUCCESS;
+ } else if (IsBlockingError(channel_->GetError())) {
+ LOG_F(LS_VERBOSE) << "Blocking";
+ return IPseudoTcpNotify::WR_SUCCESS;
+ } else if (channel_->GetError() == EMSGSIZE) {
+ LOG_F(LS_ERROR) << "EMSGSIZE";
+ return IPseudoTcpNotify::WR_TOO_LARGE;
+ } else {
+ PLOG(LS_ERROR, channel_->GetError()) << "PseudoTcpChannel::TcpWritePacket";
+ ASSERT(false);
+ return IPseudoTcpNotify::WR_FAIL;
+ }
+}
+
+void PseudoTcpChannel::AdjustClock(bool clear) {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(NULL != tcp_);
+
+ long timeout = 0;
+ if (tcp_->GetNextClock(PseudoTcp::Now(), timeout)) {
+ ASSERT(NULL != channel_);
+ // Reset the next clock, by clearing the old and setting a new one.
+ if (clear)
+ worker_thread_->Clear(this, MSG_WK_CLOCK);
+ worker_thread_->PostDelayed(_max(timeout, 0L), this, MSG_WK_CLOCK);
+ return;
+ }
+
+ delete tcp_;
+ tcp_ = NULL;
+ ready_to_connect_ = false;
+
+ if (channel_) {
+ // If TCP has failed, no need for channel_ anymore
+ signal_thread_->Post(this, MSG_SI_DESTROYCHANNEL);
+ }
+}
+
+void PseudoTcpChannel::CheckDestroy() {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ if ((worker_thread_ != NULL) || (stream_ != NULL))
+ return;
+ signal_thread_->Post(this, MSG_SI_DESTROY);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel::InternalStream
+///////////////////////////////////////////////////////////////////////////////
+
+PseudoTcpChannel::InternalStream::InternalStream(PseudoTcpChannel* parent)
+ : parent_(parent) {
+}
+
+PseudoTcpChannel::InternalStream::~InternalStream() {
+ Close();
+}
+
+StreamState PseudoTcpChannel::InternalStream::GetState() const {
+ if (!parent_)
+ return SS_CLOSED;
+ return parent_->GetState();
+}
+
+StreamResult PseudoTcpChannel::InternalStream::Read(
+ void* buffer, size_t buffer_len, size_t* read, int* error) {
+ if (!parent_) {
+ if (error)
+ *error = ENOTCONN;
+ return SR_ERROR;
+ }
+ return parent_->Read(buffer, buffer_len, read, error);
+}
+
+StreamResult PseudoTcpChannel::InternalStream::Write(
+ const void* data, size_t data_len, size_t* written, int* error) {
+ if (!parent_) {
+ if (error)
+ *error = ENOTCONN;
+ return SR_ERROR;
+ }
+ return parent_->Write(data, data_len, written, error);
+}
+
+void PseudoTcpChannel::InternalStream::Close() {
+ if (!parent_)
+ return;
+ parent_->Close();
+ parent_ = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h b/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h
new file mode 100644
index 0000000..ff1db96
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h
@@ -0,0 +1,127 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PSEUDOTCPCHANNEL_H__
+#define __PSEUDOTCPCHANNEL_H__
+
+#include "talk/base/criticalsection.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/stream.h"
+#include "talk/p2p/base/pseudotcp.h"
+#include "talk/p2p/base/session.h"
+
+namespace talk_base {
+class Thread;
+}
+
+namespace cricket {
+
+class TransportChannel;
+
+///////////////////////////////////////////////////////////////////////////////
+// ChannelStream
+// Note: The lifetime of TunnelSession is complicated. It needs to survive
+// until the following three conditions are true:
+// 1) TunnelStream has called Close (tracked via non-null stream_)
+// 2) PseudoTcp has completed (tracked via non-null tcp_)
+// 3) Session has been destroyed (tracked via non-null session_)
+// This is accomplished by calling CheckDestroy after these indicators change.
+///////////////////////////////////////////////////////////////////////////////
+// TunnelStream
+// Note: Because TunnelStream provides a stream interface, it's lifetime is
+// controlled by the owner of the stream pointer. As a result, we must support
+// both the TunnelSession disappearing before TunnelStream, and vice versa.
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel
+ : public IPseudoTcpNotify,
+ public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ // Signal thread methods
+ PseudoTcpChannel(talk_base::Thread* stream_thread,
+ Session* session);
+
+ bool Connect(const std::string& channel_name);
+ talk_base::StreamInterface* GetStream();
+
+ sigslot::signal1<PseudoTcpChannel*> SignalChannelClosed;
+
+ void OnSessionTerminate(Session* session);
+
+private:
+ class InternalStream;
+ friend class InternalStream;
+
+ virtual ~PseudoTcpChannel();
+
+ // Stream thread methods
+ talk_base::StreamState GetState() const;
+ talk_base::StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ talk_base::StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ void Close();
+
+ // Multi-thread methods
+ void OnMessage(talk_base::Message* pmsg);
+ void AdjustClock(bool clear = true);
+ void CheckDestroy();
+
+ // Signal thread methods
+ void OnChannelDestroyed(TransportChannel* channel);
+
+ // Worker thread methods
+ void OnChannelWritableState(TransportChannel* channel);
+ void OnChannelRead(TransportChannel* channel, const char* data, size_t size);
+ void OnChannelConnectionChanged(TransportChannel* channel,
+ const talk_base::SocketAddress& addr);
+
+ virtual void OnTcpOpen(PseudoTcp* ptcp);
+ virtual void OnTcpReadable(PseudoTcp* ptcp);
+ virtual void OnTcpWriteable(PseudoTcp* ptcp);
+ virtual void OnTcpClosed(PseudoTcp* ptcp, uint32 nError);
+ virtual IPseudoTcpNotify::WriteResult TcpWritePacket(PseudoTcp* tcp,
+ const char* buffer,
+ size_t len);
+
+ talk_base::Thread* signal_thread_, * worker_thread_, * stream_thread_;
+ Session* session_;
+ TransportChannel* channel_;
+ std::string channel_name_;
+ PseudoTcp* tcp_;
+ InternalStream* stream_;
+ bool stream_readable_, pending_read_event_;
+ bool ready_to_connect_;
+ mutable talk_base::CriticalSection cs_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __PSEUDOTCPCHANNEL_H__
diff --git a/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.cc b/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.cc
new file mode 100644
index 0000000..0e4f0c1
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.cc
@@ -0,0 +1,350 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// SecureTunnelSessionClient and SecureTunnelSession implementation.
+
+#include "talk/session/tunnel/securetunnelsessionclient.h"
+#include "talk/base/basicdefs.h"
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/sslidentity.h"
+#include "talk/base/sslstreamadapter.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/session/tunnel/pseudotcpchannel.h"
+
+namespace cricket {
+
+// XML elements and namespaces for XMPP stanzas used in session
+// description exchanges.
+
+const std::string NS_SECURE_TUNNEL("http://www.google.com/talk/securetunnel");
+const buzz::QName QN_SECURE_TUNNEL_DESCRIPTION(NS_SECURE_TUNNEL,
+ "description");
+const buzz::QName QN_SECURE_TUNNEL_TYPE(NS_SECURE_TUNNEL, "type");
+const buzz::QName QN_SECURE_TUNNEL_CLIENT_CERT(NS_SECURE_TUNNEL,
+ "client-cert");
+const buzz::QName QN_SECURE_TUNNEL_SERVER_CERT(NS_SECURE_TUNNEL,
+ "server-cert");
+
+// SecureTunnelSessionDescription
+
+// TunnelSessionDescription is extended to hold string forms of the
+// client and server certificate, PEM encoded.
+
+struct SecureTunnelSessionDescription : public SessionDescription {
+ std::string description;
+ std::string client_pem_certificate;
+ std::string server_pem_certificate;
+
+ SecureTunnelSessionDescription(const std::string& desc,
+ const std::string& client_pem_cert,
+ const std::string& server_pem_cert)
+ : description(desc),
+ client_pem_certificate(client_pem_cert),
+ server_pem_certificate(server_pem_cert) {
+ }
+};
+
+// SecureTunnelSessionClient
+
+SecureTunnelSessionClient::SecureTunnelSessionClient(
+ const buzz::Jid& jid, SessionManager* manager)
+ : TunnelSessionClient(jid, manager, NS_SECURE_TUNNEL) {
+}
+
+void SecureTunnelSessionClient::SetIdentity(talk_base::SSLIdentity* identity) {
+ ASSERT(identity_.get() == NULL);
+ identity_.reset(identity);
+}
+
+bool SecureTunnelSessionClient::GenerateIdentity() {
+ ASSERT(identity_.get() == NULL);
+ identity_.reset(talk_base::SSLIdentity::Generate(
+ // The name on the certificate does not matter: the peer will
+ // make sure the cert it gets during SSL negotiation matches the
+ // one it got from XMPP. It would be neat to put something
+ // recognizable in there such as the JID, except this will show
+ // in clear during the SSL negotiation and so it could be a
+ // privacy issue. Specifying an empty string here causes
+ // it to use a random string.
+#ifdef _DEBUG
+ jid().Str()
+#else
+ ""
+#endif
+ ));
+ if (identity_.get() == NULL) {
+ LOG(LS_ERROR) << "Failed to generate SSL identity";
+ return false;
+ }
+ return true;
+}
+
+talk_base::SSLIdentity& SecureTunnelSessionClient::GetIdentity() const {
+ ASSERT(identity_.get() != NULL);
+ return *identity_;
+}
+
+// Parses a certificate from a PEM encoded string.
+// Returns NULL on failure.
+// The caller is responsible for freeing the returned object.
+static talk_base::SSLCertificate* ParseCertificate(
+ const std::string& pem_cert) {
+ if (pem_cert.empty())
+ return NULL;
+ return talk_base::SSLCertificate::FromPEMString(pem_cert, NULL);
+}
+
+TunnelSession* SecureTunnelSessionClient::MakeTunnelSession(
+ Session* session, talk_base::Thread* stream_thread,
+ TunnelSessionRole role) {
+ return new SecureTunnelSession(this, session, stream_thread, role);
+}
+
+void SecureTunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid,
+ Session *session) {
+ const SecureTunnelSessionDescription *desc =
+ static_cast<const SecureTunnelSessionDescription*>(
+ session->remote_description());
+ // Validate the certificate
+ talk_base::scoped_ptr<talk_base::SSLCertificate> peer_cert(
+ ParseCertificate(desc->client_pem_certificate));
+ if (peer_cert.get() == NULL) {
+ LOG(LS_ERROR)
+ << "Rejecting incoming secure tunnel with invalid cetificate";
+ DeclineTunnel(session);
+ return;
+ }
+ // If there were a convenient place we could have cached the
+ // peer_cert so as not to have to parse it a second time when
+ // configuring the tunnel.
+ SignalIncomingTunnel(this, jid, desc->description, session);
+}
+
+// The XML representation of a session initiation request (XMPP IQ),
+// containing the initiator's SecureTunnelSessionDescription,
+// looks something like this:
+// <iq from="INITIATOR@gmail.com/pcpE101B7F4"
+// to="RECIPIENT@gmail.com/pcp8B87F0A3"
+// type="set" id="3">
+// <session xmlns="http://www.google.com/session"
+// type="initiate" id="2508605813"
+// initiator="INITIATOR@gmail.com/pcpE101B7F4">
+// <description xmlns="http://www.google.com/talk/securetunnel">
+// <type>send:filename</type>
+// <client-cert>
+// -----BEGIN CERTIFICATE-----
+// INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
+// -----END CERTIFICATE-----
+// </client-cert>
+// </description>
+// <transport xmlns="http://www.google.com/transport/p2p"/>
+// </session>
+// </iq>
+
+// The session accept iq, containing the recipient's certificate and
+// echoing the initiator's certificate, looks something like this:
+// <iq from="RECIPIENT@gmail.com/pcpE101B7F4"
+// to="INITIATOR@gmail.com/pcpE101B7F4"
+// type="set" id="5">
+// <session xmlns="http://www.google.com/session"
+// type="accept" id="2508605813"
+// initiator="sdoyon911@gmail.com/pcpE101B7F4">
+// <description xmlns="http://www.google.com/talk/securetunnel">
+// <type>send:FILENAME</type>
+// <client-cert>
+// -----BEGIN CERTIFICATE-----
+// INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
+// -----END CERTIFICATE-----
+// </client-cert>
+// <server-cert>
+// -----BEGIN CERTIFICATE-----
+// RECIPIENT'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
+// -----END CERTIFICATE-----
+// </server-cert>
+// </description>
+// </session>
+// </iq>
+
+// The below methods deal with the <description> component.
+
+const SessionDescription* SecureTunnelSessionClient::CreateSessionDescription(
+ const buzz::XmlElement* element) {
+ // Parse the XML representation of a SecureSessionDescription. See
+ // example above.
+ const buzz::XmlElement* type_elem =
+ element->FirstNamed(QN_SECURE_TUNNEL_TYPE);
+ if (type_elem == NULL)
+ // Missing mandatory XML element.
+ return NULL;
+ // Here we consider the certificate components to be optional. In
+ // practice the client certificate is always present, and the server
+ // certificate is initially missing from the session description
+ // sent during session initiation. OnAccept() will enforce that we
+ // have a certificate for our peer.
+ const buzz::XmlElement* client_cert_elem =
+ element->FirstNamed(QN_SECURE_TUNNEL_CLIENT_CERT);
+ const buzz::XmlElement* server_cert_elem =
+ element->FirstNamed(QN_SECURE_TUNNEL_SERVER_CERT);
+ return new SecureTunnelSessionDescription(
+ type_elem->BodyText(),
+ client_cert_elem ? client_cert_elem->BodyText() : "",
+ server_cert_elem ? server_cert_elem->BodyText() : "");
+}
+
+buzz::XmlElement* SecureTunnelSessionClient::TranslateSessionDescription(
+ const SessionDescription* description) {
+ // Generate an XML representation of a SecureSessionDescription. See
+ // example above.
+ const SecureTunnelSessionDescription* desc =
+ static_cast<const SecureTunnelSessionDescription*>(description);
+ buzz::XmlElement* root =
+ new buzz::XmlElement(QN_SECURE_TUNNEL_DESCRIPTION, true);
+ buzz::XmlElement* type_elem = new buzz::XmlElement(QN_SECURE_TUNNEL_TYPE);
+ type_elem->SetBodyText(desc->description);
+ root->AddElement(type_elem);
+ if (!desc->client_pem_certificate.empty()) {
+ buzz::XmlElement* client_cert_elem =
+ new buzz::XmlElement(QN_SECURE_TUNNEL_CLIENT_CERT);
+ client_cert_elem->SetBodyText(desc->client_pem_certificate);
+ root->AddElement(client_cert_elem);
+ }
+ if (!desc->server_pem_certificate.empty()) {
+ buzz::XmlElement* server_cert_elem =
+ new buzz::XmlElement(QN_SECURE_TUNNEL_SERVER_CERT);
+ server_cert_elem->SetBodyText(desc->server_pem_certificate);
+ root->AddElement(server_cert_elem);
+ }
+ return root;
+}
+
+// Makes up a secure session description to be sent out when
+// initiating a session.
+SessionDescription*
+SecureTunnelSessionClient::CreateOutgoingSessionDescription(
+ const buzz::Jid &jid, const std::string &description) {
+ // We are the initiator so we are the client. Put our cert into the
+ // description.
+ std::string pem_cert = GetIdentity().certificate().ToPEMString();
+ return new SecureTunnelSessionDescription(description, pem_cert, "");
+}
+
+// Makes up a secure session description to be sent out when accepting
+// a session request.
+SessionDescription*
+SecureTunnelSessionClient::CreateOutgoingSessionDescription(Session *session) {
+ const SecureTunnelSessionDescription* in_desc =
+ static_cast<const SecureTunnelSessionDescription*>(
+ session->remote_description());
+ // We are accepting a session request. We need to add our cert, the
+ // server cert, into the description. The client cert was validated
+ // in OnIncomingTunnel().
+ ASSERT(!in_desc->client_pem_certificate.empty());
+ return new SecureTunnelSessionDescription(
+ in_desc->description,
+ in_desc->client_pem_certificate,
+ GetIdentity().certificate().ToPEMString());
+}
+
+// SecureTunnelSession
+
+SecureTunnelSession::SecureTunnelSession(
+ SecureTunnelSessionClient* client, Session* session,
+ talk_base::Thread* stream_thread, TunnelSessionRole role)
+ : TunnelSession(client, session, stream_thread),
+ role_(role) {
+}
+
+talk_base::StreamInterface* SecureTunnelSession::MakeSecureStream(
+ talk_base::StreamInterface* stream) {
+ talk_base::SSLStreamAdapter* ssl_stream =
+ talk_base::SSLStreamAdapter::Create(stream);
+ talk_base::SSLIdentity* identity =
+ static_cast<SecureTunnelSessionClient*>(client_)->
+ GetIdentity().GetReference();
+ ssl_stream->SetIdentity(identity);
+ if (role_ == RESPONDER)
+ ssl_stream->SetServerRole();
+ ssl_stream->StartSSLWithPeer();
+
+ // SSL negotiation will start on the stream as soon as it
+ // opens. However our SSLStreamAdapter still hasn't been told what
+ // certificate to allow for our peer. If we are the initiator, we do
+ // not have the peer's certificate yet: we will obtain it from the
+ // session accept message which we will receive later (see
+ // OnAccept()). We won't Connect() the PseudoTcpChannel until we get
+ // that, so the stream will stay closed until then. Keep a handle
+ // on the streem so we can configure the peer certificate later.
+ ssl_stream_reference_.reset(new talk_base::StreamReference(ssl_stream));
+ return ssl_stream_reference_->NewReference();
+}
+
+talk_base::StreamInterface* SecureTunnelSession::GetStream() {
+ ASSERT(channel_ != NULL);
+ ASSERT(ssl_stream_reference_.get() == NULL);
+ return MakeSecureStream(channel_->GetStream());
+}
+
+void SecureTunnelSession::OnAccept() {
+ // We have either sent or received a session accept: it's time to
+ // connect the tunnel. First we must set the peer certificate.
+ ASSERT(channel_ != NULL);
+ ASSERT(session_ != NULL);
+ const SecureTunnelSessionDescription* remote_desc =
+ static_cast<const SecureTunnelSessionDescription*>
+ (session_->remote_description());
+ const std::string& cert_pem =
+ role_ == INITIATOR ? remote_desc->server_pem_certificate :
+ remote_desc->client_pem_certificate;
+ talk_base::SSLCertificate* peer_cert =
+ ParseCertificate(cert_pem);
+ if (peer_cert == NULL) {
+ ASSERT(role_ == INITIATOR); // when RESPONDER we validated it earlier
+ LOG(LS_ERROR)
+ << "Rejecting secure tunnel accept with invalid cetificate";
+ session_->Terminate();
+ return;
+ }
+ ASSERT(ssl_stream_reference_.get() != NULL);
+ talk_base::SSLStreamAdapter* ssl_stream =
+ static_cast<talk_base::SSLStreamAdapter*>(
+ ssl_stream_reference_->GetStream());
+ ssl_stream->SetPeerCertificate(peer_cert); // pass ownership of certificate.
+ // We no longer need our handle to the ssl stream.
+ ssl_stream_reference_.reset();
+ LOG(LS_INFO) << "Connecting tunnel";
+ // This will try to connect the PseudoTcpChannel. If and when that
+ // succeeds, then ssl negotiation will take place, and when that
+ // succeeds, the tunnel stream will finally open.
+ VERIFY(channel_->Connect("tcp"));
+}
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.h b/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.h
new file mode 100644
index 0000000..234e4bb
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.h
@@ -0,0 +1,161 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// SecureTunnelSessionClient and SecureTunnelSession.
+// SecureTunnelSessionClient extends TunnelSessionClient to exchange
+// certificates as part of the session description.
+// SecureTunnelSession is a TunnelSession that wraps the underlying
+// tunnel stream into an SSLStreamAdapter.
+
+#ifndef TALK_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_
+#define TALK_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_
+
+#include <string>
+
+#include "talk/base/sslidentity.h"
+#include "talk/base/sslstreamadapter.h"
+#include "talk/session/tunnel/tunnelsessionclient.h"
+
+namespace cricket {
+
+class SecureTunnelSession; // below
+
+// SecureTunnelSessionClient
+
+// This TunnelSessionClient establishes secure tunnels protected by
+// SSL/TLS. The PseudoTcpChannel stream is wrapped with an
+// SSLStreamAdapter. An SSLIdentity must be set or generated.
+//
+// The TunnelSessionDescription is extended to include the client and
+// server certificates. The initiator acts as the client. The session
+// initiate stanza carries a description that contains the client's
+// certificate, and the session accept response's description has the
+// server certificate added to it.
+
+class SecureTunnelSessionClient : public TunnelSessionClient {
+ public:
+ // The jid is used as the name for sessions for outgoing tunnels.
+ // manager is the SessionManager to which we register this client
+ // and its sessions.
+ SecureTunnelSessionClient(const buzz::Jid& jid, SessionManager* manager);
+
+ // Configures this client to use a preexisting SSLIdentity.
+ // The client takes ownership of the identity object.
+ // Use either SetIdentity or GenerateIdentity, and only once.
+ void SetIdentity(talk_base::SSLIdentity* identity);
+
+ // Generates an identity from nothing.
+ // Returns true if generation was successful.
+ // Use either SetIdentity or GenerateIdentity, and only once.
+ bool GenerateIdentity();
+
+ // Returns our identity for SSL purposes, as either set by
+ // SetIdentity() or generated by GenerateIdentity(). Call this
+ // method only after our identity has been successfully established
+ // by one of those methods.
+ talk_base::SSLIdentity& GetIdentity() const;
+
+ // Inherited methods
+ virtual void OnIncomingTunnel(const buzz::Jid& jid, Session *session);
+ virtual const SessionDescription* CreateSessionDescription(
+ const buzz::XmlElement* element);
+ virtual buzz::XmlElement* TranslateSessionDescription(
+ const SessionDescription* description);
+ virtual SessionDescription *CreateOutgoingSessionDescription(
+ const buzz::Jid &jid, const std::string &description);
+ virtual SessionDescription *CreateOutgoingSessionDescription(
+ Session *incoming);
+
+ protected:
+ virtual TunnelSession* MakeTunnelSession(
+ Session* session, talk_base::Thread* stream_thread,
+ TunnelSessionRole role);
+
+ private:
+ // Our identity (key and certificate) for SSL purposes. The
+ // certificate part will be communicated within the session
+ // description. The identity will be passed to the SSLStreamAdapter
+ // and used for SSL authentication.
+ talk_base::scoped_ptr<talk_base::SSLIdentity> identity_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SecureTunnelSessionClient);
+};
+
+// SecureTunnelSession:
+// A TunnelSession represents one session for one client. It
+// provides the actual tunnel stream and handles state changes.
+// A SecureTunnelSession is a TunnelSession that wraps the underlying
+// tunnel stream into an SSLStreamAdapter.
+
+class SecureTunnelSession : public TunnelSession {
+ public:
+ // This TunnelSession will tie together the given client and session.
+ // stream_thread is passed to the PseudoTCPChannel: it's the thread
+ // designated to interact with the tunnel stream.
+ // role is either INITIATOR or RESPONDER, depending on who is
+ // initiating the session.
+ SecureTunnelSession(SecureTunnelSessionClient* client, Session* session,
+ talk_base::Thread* stream_thread,
+ TunnelSessionRole role);
+
+ // Returns the stream that implements the actual P2P tunnel.
+ // This may be called only once. Caller is responsible for freeing
+ // the returned object.
+ virtual talk_base::StreamInterface* GetStream();
+
+ protected:
+ // Inherited method: callback on accepting a session.
+ virtual void OnAccept();
+
+ // Helper method for GetStream() that Instantiates the
+ // SSLStreamAdapter to wrap the PseudoTcpChannel's stream, and
+ // configures it with our identity and role.
+ talk_base::StreamInterface* MakeSecureStream(
+ talk_base::StreamInterface* stream);
+
+ // Our role in requesting the tunnel: INITIATOR or
+ // RESPONDER. Translates to our role in SSL negotiation:
+ // respectively client or server. Also indicates which slot of the
+ // SecureTunnelSessionDescription our cert goes into: client-cert or
+ // server-cert respectively.
+ TunnelSessionRole role_;
+
+ // This is the stream representing the usable tunnel endpoint. It's
+ // a StreamReference wrapping the SSLStreamAdapter instance, which
+ // further wraps a PseudoTcpChannel::InternalStream. The
+ // StreamReference is because in the case of CreateTunnel(), the
+ // stream endpoint is returned early, but we need to keep a handle
+ // on it so we can setup the peer certificate when we receive it
+ // later.
+ talk_base::scoped_ptr<talk_base::StreamReference> ssl_stream_reference_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SecureTunnelSession);
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_
diff --git a/third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.cc b/third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.cc
new file mode 100644
index 0000000..2dc9705
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.cc
@@ -0,0 +1,358 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/basicdefs.h"
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/xmllite/xmlelement.h"
+#include "pseudotcpchannel.h"
+#include "tunnelsessionclient.h"
+
+namespace cricket {
+
+const std::string NS_TUNNEL("http://www.google.com/talk/tunnel");
+const buzz::QName QN_TUNNEL_DESCRIPTION(NS_TUNNEL, "description");
+const buzz::QName QN_TUNNEL_TYPE(NS_TUNNEL, "type");
+
+enum {
+ MSG_CLOCK = 1,
+ MSG_DESTROY,
+ MSG_TERMINATE,
+ MSG_EVENT,
+ MSG_CREATE_TUNNEL,
+};
+
+struct EventData : public talk_base::MessageData {
+ int event, error;
+ EventData(int ev, int err = 0) : event(ev), error(err) { }
+};
+
+struct CreateTunnelData : public talk_base::MessageData {
+ buzz::Jid jid;
+ std::string description;
+ talk_base::Thread* thread;
+ talk_base::StreamInterface* stream;
+};
+
+extern const talk_base::ConstantLabel SESSION_STATES[];
+
+const talk_base::ConstantLabel SESSION_STATES[] = {
+ KLABEL(Session::STATE_INIT),
+ KLABEL(Session::STATE_SENTINITIATE),
+ KLABEL(Session::STATE_RECEIVEDINITIATE),
+ KLABEL(Session::STATE_SENTACCEPT),
+ KLABEL(Session::STATE_RECEIVEDACCEPT),
+ KLABEL(Session::STATE_SENTMODIFY),
+ KLABEL(Session::STATE_RECEIVEDMODIFY),
+ KLABEL(Session::STATE_SENTREJECT),
+ KLABEL(Session::STATE_RECEIVEDREJECT),
+ KLABEL(Session::STATE_SENTREDIRECT),
+ KLABEL(Session::STATE_SENTTERMINATE),
+ KLABEL(Session::STATE_RECEIVEDTERMINATE),
+ KLABEL(Session::STATE_INPROGRESS),
+ KLABEL(Session::STATE_DEINIT),
+ LASTLABEL
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionDescription
+///////////////////////////////////////////////////////////////////////////////
+
+struct TunnelSessionDescription : public SessionDescription {
+ std::string description;
+
+ TunnelSessionDescription(const std::string& desc) : description(desc) { }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClientBase
+///////////////////////////////////////////////////////////////////////////////
+
+TunnelSessionClientBase::TunnelSessionClientBase(const buzz::Jid& jid,
+ SessionManager* manager, const std::string &ns)
+ : jid_(jid), session_manager_(manager), namespace_(ns), shutdown_(false) {
+ session_manager_->AddClient(namespace_, this);
+}
+
+TunnelSessionClientBase::~TunnelSessionClientBase() {
+ shutdown_ = true;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ Session* session = (*it)->ReleaseSession(true);
+ session_manager_->DestroySession(session);
+ }
+ session_manager_->RemoveClient(namespace_);
+}
+
+void TunnelSessionClientBase::OnSessionCreate(Session* session, bool received) {
+ LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionCreate: received="
+ << received;
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (received)
+ sessions_.push_back(
+ MakeTunnelSession(session, talk_base::Thread::Current(), RESPONDER));
+}
+
+void TunnelSessionClientBase::OnSessionDestroy(Session* session) {
+ LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionDestroy";
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (shutdown_)
+ return;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ if ((*it)->HasSession(session)) {
+ VERIFY((*it)->ReleaseSession(false) == session);
+ sessions_.erase(it);
+ return;
+ }
+ }
+}
+
+talk_base::StreamInterface* TunnelSessionClientBase::CreateTunnel(
+ const buzz::Jid& to, const std::string& description) {
+ // Valid from any thread
+ CreateTunnelData data;
+ data.jid = to;
+ data.description = description;
+ data.thread = talk_base::Thread::Current();
+ session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
+ return data.stream;
+}
+
+talk_base::StreamInterface* TunnelSessionClientBase::AcceptTunnel(
+ Session* session) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ TunnelSession* tunnel = NULL;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ if ((*it)->HasSession(session)) {
+ tunnel = *it;
+ break;
+ }
+ }
+ ASSERT(tunnel != NULL);
+
+ session->Accept(CreateOutgoingSessionDescription(session));
+ return tunnel->GetStream();
+}
+
+void TunnelSessionClientBase::DeclineTunnel(Session* session) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ session->Reject();
+}
+
+void TunnelSessionClientBase::OnMessage(talk_base::Message* pmsg) {
+ if (pmsg->message_id == MSG_CREATE_TUNNEL) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata);
+ Session* session = session_manager_->CreateSession(jid_.Str(), namespace_);
+ TunnelSession* tunnel = MakeTunnelSession(session, data->thread,
+ INITIATOR);
+ sessions_.push_back(tunnel);
+ SessionDescription *desc =
+ CreateOutgoingSessionDescription(data->jid, data->description);
+ session->Initiate(data->jid.Str(), desc);
+ data->stream = tunnel->GetStream();
+ }
+}
+
+TunnelSession* TunnelSessionClientBase::MakeTunnelSession(
+ Session* session, talk_base::Thread* stream_thread,
+ TunnelSessionRole /*role*/) {
+ return new TunnelSession(this, session, stream_thread);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClient
+///////////////////////////////////////////////////////////////////////////////
+
+TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
+ SessionManager* manager,
+ const std::string &ns)
+ : TunnelSessionClientBase(jid, manager, ns) {
+}
+
+TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
+ SessionManager* manager)
+ : TunnelSessionClientBase(jid, manager, NS_TUNNEL) {
+}
+
+TunnelSessionClient::~TunnelSessionClient() {
+}
+
+const SessionDescription* TunnelSessionClient::CreateSessionDescription(
+ const buzz::XmlElement* element) {
+ if (const buzz::XmlElement* type_elem = element->FirstNamed(QN_TUNNEL_TYPE)) {
+ return new TunnelSessionDescription(type_elem->BodyText());
+ }
+ ASSERT(false);
+ return 0;
+}
+
+buzz::XmlElement* TunnelSessionClient::TranslateSessionDescription(
+ const SessionDescription* description) {
+ const TunnelSessionDescription* desc =
+ static_cast<const TunnelSessionDescription*>(description);
+
+ buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true);
+ buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE);
+ type_elem->SetBodyText(desc->description);
+ root->AddElement(type_elem);
+ return root;
+}
+
+void TunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid,
+ Session *session) {
+ const TunnelSessionDescription *desc =
+ static_cast<const TunnelSessionDescription*>(session->remote_description());
+
+ SignalIncomingTunnel(this, jid, desc->description, session);
+}
+
+SessionDescription *TunnelSessionClient::CreateOutgoingSessionDescription(
+ const buzz::Jid &jid, const std::string &description) {
+ return new TunnelSessionDescription(description);
+}
+
+SessionDescription *TunnelSessionClient::CreateOutgoingSessionDescription(
+ Session *session) {
+ const TunnelSessionDescription* in_desc =
+ static_cast<const TunnelSessionDescription*>(
+ session->remote_description());
+ TunnelSessionDescription* out_desc = new TunnelSessionDescription(
+ in_desc->description);
+
+ return out_desc;
+}
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSession
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Signalling thread methods
+//
+
+TunnelSession::TunnelSession(TunnelSessionClientBase* client, Session* session,
+ talk_base::Thread* stream_thread)
+ : client_(client), session_(session), channel_(NULL) {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ != NULL);
+ session_->SignalState.connect(this, &TunnelSession::OnSessionState);
+ channel_ = new PseudoTcpChannel(stream_thread, session_);
+ channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed);
+}
+
+TunnelSession::~TunnelSession() {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ == NULL);
+ ASSERT(channel_ == NULL);
+}
+
+talk_base::StreamInterface* TunnelSession::GetStream() {
+ ASSERT(channel_ != NULL);
+ return channel_->GetStream();
+}
+
+bool TunnelSession::HasSession(Session* session) {
+ ASSERT(NULL != session_);
+ return (session_ == session);
+}
+
+Session* TunnelSession::ReleaseSession(bool channel_exists) {
+ ASSERT(NULL != session_);
+ ASSERT(NULL != channel_);
+ Session* session = session_;
+ session_->SignalState.disconnect(this);
+ session_ = NULL;
+ if (channel_exists)
+ channel_->SignalChannelClosed.disconnect(this);
+ channel_ = NULL;
+ delete this;
+ return session;
+}
+
+void TunnelSession::OnSessionState(BaseSession* session,
+ BaseSession::State state) {
+ LOG(LS_INFO) << "TunnelSession::OnSessionState("
+ << talk_base::nonnull(
+ talk_base::FindLabel(state, SESSION_STATES), "Unknown")
+ << ")";
+ ASSERT(session == session_);
+
+ switch (state) {
+ case Session::STATE_RECEIVEDINITIATE:
+ OnInitiate();
+ break;
+ case Session::STATE_SENTACCEPT:
+ case Session::STATE_RECEIVEDACCEPT:
+ OnAccept();
+ break;
+ case Session::STATE_SENTTERMINATE:
+ case Session::STATE_RECEIVEDTERMINATE:
+ OnTerminate();
+ break;
+ case Session::STATE_DEINIT:
+ // ReleaseSession should have been called before this.
+ ASSERT(false);
+ break;
+ default:
+ break;
+ }
+}
+
+void TunnelSession::OnInitiate() {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ != NULL);
+ client_->OnIncomingTunnel(buzz::Jid(session_->remote_name()), session_);
+}
+
+void TunnelSession::OnAccept() {
+ ASSERT(channel_ != NULL);
+ VERIFY(channel_->Connect("tcp"));
+}
+
+void TunnelSession::OnTerminate() {
+ ASSERT(channel_ != NULL);
+ channel_->OnSessionTerminate(session_);
+}
+
+void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) {
+ ASSERT(channel_ == channel);
+ ASSERT(session_ != NULL);
+ session_->Terminate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
diff --git a/third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.h b/third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.h
new file mode 100644
index 0000000..2671458
--- /dev/null
+++ b/third_party/libjingle/source/talk/session/tunnel/tunnelsessionclient.h
@@ -0,0 +1,176 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __TUNNELSESSIONCLIENT_H__
+#define __TUNNELSESSIONCLIENT_H__
+
+#include <vector>
+#include "talk/base/criticalsection.h"
+#include "talk/base/stream.h"
+#include "talk/p2p/base/pseudotcp.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/constants.h"
+
+namespace cricket {
+
+class TunnelSession;
+class TunnelStream;
+
+enum TunnelSessionRole { INITIATOR, RESPONDER };
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClient
+///////////////////////////////////////////////////////////////////////////////
+
+// Base class is still abstract
+class TunnelSessionClientBase
+ : public SessionClient, public talk_base::MessageHandler {
+public:
+ TunnelSessionClientBase(const buzz::Jid& jid, SessionManager* manager,
+ const std::string &ns);
+ virtual ~TunnelSessionClientBase();
+
+ const buzz::Jid& jid() const { return jid_; }
+ SessionManager* session_manager() const { return session_manager_; }
+
+ void OnSessionCreate(Session* session, bool received);
+ void OnSessionDestroy(Session* session);
+
+ // This can be called on any thread. The stream interface is
+ // thread-safe, but notifications must be registered on the creating
+ // thread.
+ talk_base::StreamInterface* CreateTunnel(const buzz::Jid& to,
+ const std::string& description);
+
+ talk_base::StreamInterface* AcceptTunnel(Session* session);
+ void DeclineTunnel(Session* session);
+
+ // Invoked on an incoming tunnel
+ virtual void OnIncomingTunnel(const buzz::Jid &jid, Session *session) = 0;
+
+ // Invoked on an outgoing session request
+ virtual SessionDescription *CreateOutgoingSessionDescription(
+ const buzz::Jid &jid, const std::string &description) = 0;
+ // Invoked on a session request accept to create
+ // the local-side session description
+ virtual SessionDescription *CreateOutgoingSessionDescription(
+ Session *incoming) = 0;
+
+protected:
+
+ void OnMessage(talk_base::Message* pmsg);
+
+ // helper method to instantiate TunnelSession. By overriding this,
+ // subclasses of TunnelSessionClient are able to instantiate
+ // subclasses of TunnelSession instead.
+ virtual TunnelSession* MakeTunnelSession(Session* session,
+ talk_base::Thread* stream_thread,
+ TunnelSessionRole role);
+
+ buzz::Jid jid_;
+ SessionManager* session_manager_;
+ std::vector<TunnelSession*> sessions_;
+ std::string namespace_;
+ bool shutdown_;
+};
+
+class TunnelSessionClient
+ : public TunnelSessionClientBase, public sigslot::has_slots<> {
+public:
+ TunnelSessionClient(const buzz::Jid& jid, SessionManager* manager);
+ TunnelSessionClient(const buzz::Jid& jid, SessionManager* manager,
+ const std::string &ns);
+ virtual ~TunnelSessionClient();
+
+ virtual const SessionDescription* CreateSessionDescription(
+ const buzz::XmlElement* element);
+ virtual buzz::XmlElement* TranslateSessionDescription(
+ const SessionDescription* description);
+
+ // Signal arguments are this, initiator, description, session
+ sigslot::signal4<TunnelSessionClient*, buzz::Jid, std::string, Session*>
+ SignalIncomingTunnel;
+
+ virtual void OnIncomingTunnel(const buzz::Jid &jid,
+ Session *session);
+ virtual SessionDescription *CreateOutgoingSessionDescription(
+ const buzz::Jid &jid, const std::string &description);
+ virtual SessionDescription *CreateOutgoingSessionDescription(
+ Session *incoming);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSession
+// Note: The lifetime of TunnelSession is complicated. It needs to survive
+// until the following three conditions are true:
+// 1) TunnelStream has called Close (tracked via non-null stream_)
+// 2) PseudoTcp has completed (tracked via non-null tcp_)
+// 3) Session has been destroyed (tracked via non-null session_)
+// This is accomplished by calling CheckDestroy after these indicators change.
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// TunnelStream
+// Note: Because TunnelStream provides a stream interface, it's lifetime is
+// controlled by the owner of the stream pointer. As a result, we must support
+// both the TunnelSession disappearing before TunnelStream, and vice versa.
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel;
+
+class TunnelSession : public sigslot::has_slots<> {
+ public:
+ // Signalling thread methods
+ TunnelSession(TunnelSessionClientBase* client, Session* session,
+ talk_base::Thread* stream_thread);
+
+ virtual talk_base::StreamInterface* GetStream();
+ bool HasSession(Session* session);
+ Session* ReleaseSession(bool channel_exists);
+
+ protected:
+ virtual ~TunnelSession();
+
+ virtual void OnSessionState(BaseSession* session, BaseSession::State state);
+ virtual void OnInitiate();
+ virtual void OnAccept();
+ virtual void OnTerminate();
+ virtual void OnChannelClosed(PseudoTcpChannel* channel);
+
+ TunnelSessionClientBase* client_;
+ Session* session_;
+ PseudoTcpChannel* channel_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __TUNNELSESSIONCLIENT_H__
diff --git a/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.py b/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.py
new file mode 100644
index 0000000..bb8f106
--- /dev/null
+++ b/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.py
@@ -0,0 +1,20 @@
+# Copyright 2010 Google Inc.
+# All Rights Reserved.
+# Author: thaloun@google.com (Tim Haloun)
+
+"""Noop tool that defines builder functions for non-default platforms to
+ avoid errors when scanning sconsscripts."""
+
+import SCons.Builder
+
+
+def generate(env):
+ """SCons method."""
+ if not env.Bit('windows'):
+ builder = SCons.Builder.Builder(
+ action=''
+ )
+ env.Append(BUILDERS={'RES': builder, 'Grit': builder})
+
+def exists(env):
+ return 1
diff --git a/third_party/libjingle/source/talk/site_scons/talk.py b/third_party/libjingle/source/talk/site_scons/talk.py
new file mode 100644
index 0000000..b984415
--- /dev/null
+++ b/third_party/libjingle/source/talk/site_scons/talk.py
@@ -0,0 +1,441 @@
+# Copyright 2010 Google Inc.
+# All Rights Reserved.
+#
+# Author: Tim Haloun (thaloun@google.com)
+# Daniel Petersson (dape@google.com)
+#
+import os
+
+
+def Library(env, **kwargs):
+ """Extends ComponentLibrary to support multiplatform builds.
+
+ Args:
+ env: The current environment.
+ kwargs: The keyword arguments.
+ """
+ params = CombineDicts(kwargs, {'COMPONENT_STATIC': True})
+ return ExtendComponent(env, env.ComponentLibrary, **params)
+
+
+def DynamicLibrary(env, **kwargs):
+ """Extends ComponentLibrary to support multiplatform builds
+ of dynmic libraries. This use COMPONENT_STATIC = false.
+
+ Args:
+ env: The environment object.
+ kwargs: The keyword arguments.
+
+ Returns:
+ See swtoolkit ComponentLibrary
+ """
+ params = CombineDicts(kwargs, {'COMPONENT_STATIC': False})
+ return ExtendComponent(env, env.ComponentLibrary, **params)
+
+
+def Object(env, **kwargs):
+ return ExtendComponent(env, env.ComponentObject, **kwargs)
+
+
+def Unittest(env, **kwargs):
+ """Extends ComponentTestProgram to support unittest built
+ for multiple platforms.
+
+ Args:
+ env: The current environment.
+ kwargs: The keyword arguments.
+
+ Returns:
+ See swtoolkit ComponentProgram.
+ """
+ kwargs['name'] = kwargs['name'] + '_unittest'
+
+ common_test_params = {
+ 'posix_cppdefines': ['GUNIT_NO_GOOGLE3', 'GTEST_HAS_RTTI=0'],
+ 'libs': ['unittest_main', 'gunit']
+ }
+ if not kwargs.has_key('explicit_libs'):
+ common_test_params['win_libs'] = [
+ 'advapi32',
+ 'crypt32',
+ 'iphlpapi',
+ 'secur32',
+ 'shell32',
+ 'shlwapi',
+ 'user32',
+ 'wininet',
+ 'ws2_32'
+ ]
+ common_test_params['lin_libs'] = [
+ 'pthread',
+ ':libssl.so.0.9.8',
+ ':libcrypto.so.0.9.8',
+ ]
+
+ params = CombineDicts(kwargs, common_test_params)
+ return ExtendComponent(env, env.ComponentTestProgram, **params)
+
+
+def App(env, **kwargs):
+ """Extends ComponentProgram to support executables with platform specific
+ options.
+
+ Args:
+ env: The current environment.
+ kwargs: The keyword arguments.
+
+ Returns:
+ See swtoolkit ComponentProgram.
+ """
+ if not kwargs.has_key('explicit_libs'):
+ common_app_params = {
+ 'win_libs': [
+ 'advapi32',
+ 'crypt32',
+ 'iphlpapi',
+ 'secur32',
+ 'shell32',
+ 'shlwapi',
+ 'user32',
+ 'wininet',
+ 'ws2_32'
+ ]}
+ params = CombineDicts(kwargs, common_app_params)
+ else:
+ params = kwargs
+ return ExtendComponent(env, env.ComponentProgram, **params)
+
+
+def Repository(env, at, path):
+ """Maps a directory external to $MAIN_DIR to the given path so that sources
+ compiled from it end up in the correct place under $OBJ_DIR. NOT required
+ when only referring to header files.
+
+ Args:
+ env: The current environment object.
+ at: The 'mount point' within the current directory.
+ path: Path to the actual directory.
+ """
+ env.Dir(at).addRepository(env.Dir(path))
+
+
+def Components(*paths):
+ """Completes the directory paths with the correct file
+ names such that the directory/directory.scons name
+ convention can be used.
+
+ Args:
+ paths: The paths to complete. If it refers to an existing
+ file then it is ignored.
+
+ Returns:
+ The completed lif scons files that are needed to build talk.
+ """
+ files = []
+ for path in paths:
+ if os.path.isfile(path):
+ files.append(path)
+ else:
+ files.append(ExpandSconsPath(path))
+ return files
+
+
+def ExpandSconsPath(path):
+ """Expands a directory path into the path to the
+ scons file that our build uses.
+ Ex: magiflute/plugin/common => magicflute/plugin/common/common.scons
+
+ Args:
+ path: The directory path to expand.
+
+ Returns:
+ The expanded path.
+ """
+ return '%s/%s.scons' % (path, os.path.basename(path))
+
+
+def AddMediaLibs(env, **kwargs):
+ lmi_libdir = '$GOOGLE3/third_party/lmi/files/merged/lib/'
+ if env.Bit('windows'):
+ if env.get('COVERAGE_ENABLED'):
+ lmi_libdir += 'win32/c_only'
+ else:
+ lmi_libdir += 'win32/Release'
+ elif env.Bit('mac'):
+ lmi_libdir += 'macos'
+ elif env.Bit('linux'):
+ lmi_libdir += 'linux/x86'
+
+ ipp_libdir = '$GOOGLE3/third_party/Intel_ipp/%s/ia32/lib'
+ if env.Bit('windows'):
+ ipp_libdir %= 'v_5_2_windows'
+ elif env.Bit('mac'):
+ ipp_libdir %= 'v_5_3_mac_os_x'
+ elif env.Bit('linux'):
+ ipp_libdir %= 'v_5_2_linux'
+
+ AddToDict(kwargs, 'libdirs', [
+ '$MAIN_DIR/third_party/gips/Libraries/',
+ ipp_libdir,
+ lmi_libdir,
+ ])
+
+ gips_lib = ''
+ if env.Bit('windows'):
+ if env.Bit('debug'):
+ gips_lib = 'gipsvoiceenginelib_mtd'
+ else:
+ gips_lib = 'gipsvoiceenginelib_mt'
+ elif env.Bit('mac'):
+ gips_lib = 'VoiceEngine_mac_universal_gcc'
+ elif env.Bit('linux'):
+ gips_lib = 'VoiceEngine_Linux_external_gcc'
+
+ AddToDict(kwargs, 'libs', [
+ gips_lib,
+ 'LmiAudioCommon',
+ 'LmiClient',
+ 'LmiCmcp',
+ 'LmiDeviceManager',
+ 'LmiH263ClientPlugIn',
+ 'LmiH263CodecCommon',
+ 'LmiH263Decoder',
+ 'LmiH263Encoder',
+ 'LmiH264ClientPlugIn',
+ 'LmiH264CodecCommon',
+ 'LmiH264Common',
+ 'LmiH264Decoder',
+ 'LmiH264Encoder',
+ 'LmiIce',
+ 'LmiMediaPayload',
+ 'LmiOs',
+ 'LmiPacketCache',
+ 'LmiProtocolStack',
+ 'LmiRateShaper',
+ 'LmiRtp',
+ 'LmiSecurity',
+ 'LmiSignaling',
+ 'LmiStun',
+ 'LmiTransport',
+ 'LmiUtils',
+ 'LmiVideoCommon',
+ 'LmiXml',
+ 'ippsmerged',
+ 'ippsemerged',
+ 'ippvcmerged',
+ 'ippvcemerged',
+ 'ippimerged',
+ 'ippiemerged',
+ 'ippsrmerged',
+ 'ippsremerged',
+ ])
+
+ if env.Bit('windows'):
+ AddToDict(kwargs, 'libs', [
+ 'ippcorel',
+ 'ippscmerged',
+ 'ippscemerged',
+ 'strmiids',
+ 'dsound',
+ ])
+ else:
+ AddToDict(kwargs, 'libs', [
+ 'ippcore',
+ 'ippacmerged',
+ 'ippacemerged',
+ 'ippccmerged',
+ 'ippccemerged',
+ 'ippchmerged',
+ 'ippchemerged',
+ 'ippcvmerged',
+ 'ippcvemerged',
+ 'ippdcmerged',
+ 'ippdcemerged',
+ 'ippjmerged',
+ 'ippjemerged',
+ 'ippmmerged',
+ 'ippmemerged',
+ 'ipprmerged',
+ 'ippremerged',
+ ])
+
+ return kwargs
+
+
+def ReadVersion(filename):
+ """Executes the supplied file and pulls out a version definition from it. """
+ defs = {}
+ execfile(str(filename), defs)
+ if not defs.has_key('version'):
+ return '0.0.0.0'
+ version = defs['version']
+ parts = version.split(',')
+ build = os.environ.get('GOOGLE_VERSION_BUILDNUMBER')
+ if build:
+ parts[-1] = str(build)
+ return '.'.join(parts)
+
+
+#-------------------------------------------------------------------------------
+# Helper methods for translating talk.Foo() declarations in to manipulations of
+# environmuent construction variables, including parameter parsing and merging,
+#
+def Depends(env, name, kwargs):
+ depends = GetEntry(kwargs, 'depends')
+ if depends is not None:
+ env.Depends(name, depends)
+
+
+def GetEntry(dict, key):
+ """Get the value from a dictionary by key. If the key
+ isn't in the dictionary then None is returned. If it is in
+ the dictionaruy the value is fetched and then is it removed
+ from the dictionary.
+
+ Args:
+ key: The key to get the value for.
+ kwargs: The keyword argument dictionary.
+ Returns:
+ The value or None if the key is missing.
+ """
+ value = None
+ if dict.has_key(key):
+ value = dict[key]
+ dict.pop(key)
+
+ return value
+
+
+def MergeAndFilterByPlatform(env, params):
+ """Take a dictionary of arguments to lists of values, and, depending on
+ which platform we are targetting, merge the lists of associated keys.
+ Merge by combining value lists like so:
+ {win_foo = [a,b], lin_foo = [c,d], foo = [e], mac_bar = [f], bar = [g] }
+ becomes {foo = [a,b,e], bar = [g]} on windows, and
+ {foo = [e], bar = [f,g]} on mac
+
+ Args:
+ env: The hammer environment which knows which platforms are active
+ params: The keyword argument dictionary.
+ Returns:
+ A new dictionary with the filtered and combined entries of params
+ """
+ platforms = {
+ 'linux': 'lin_',
+ 'mac': 'mac_',
+ 'posix': 'posix_',
+ 'windows': 'win_',
+ }
+ active_prefixes = [
+ platforms[x] for x in iter(platforms) if env.Bit(x)
+ ]
+ inactive_prefixes = [
+ platforms[x] for x in iter(platforms) if not env.Bit(x)
+ ]
+
+ merged = {}
+ for arg, values in params.iteritems():
+ inactive_platform = False
+
+ key = arg
+
+ for prefix in active_prefixes:
+ if arg.startswith(prefix):
+ key = arg[len(prefix):]
+
+ for prefix in inactive_prefixes:
+ if arg.startswith(prefix):
+ inactive_platform = True
+
+ if inactive_platform:
+ continue
+
+ AddToDict(merged, key, values)
+
+ return merged
+
+
+def ExtendComponent(env, component, **kwargs):
+ # get our target identifier
+ name = GetEntry(kwargs, 'name')
+ log_env = GetEntry(kwargs, 'dump')
+
+ if (kwargs.has_key('include_talk_media_libs') and
+ kwargs['include_talk_media_libs']):
+ kwargs = AddMediaLibs(env, **kwargs)
+
+ # prune parameters for inactive platforms or modes
+ # and combine the rest of the platforms
+ params = MergeAndFilterByPlatform(env, kwargs)
+
+ # apply any explicit dependencies
+ Depends(env, name, kwargs)
+
+ AddToDict(params, 'CPPDEFINES', env.Dictionary('CPPDEFINES'), False)
+ AddToDict(params, 'CPPPATH', env.Dictionary('CPPPATH'), False)
+ AddToDict(params, 'CCFLAGS', env.Dictionary('CCFLAGS'), False)
+ AddToDict(params, 'LIBPATH', env.Dictionary('LIBPATH'), False)
+ AddToDict(params, 'LINKFLAGS', env.Dictionary('LINKFLAGS'), False)
+ if env.Bit('mac'):
+ AddToDict(params, 'FRAMEWORKS', env.Dictionary('FRAMEWORKS'), False)
+
+ RenameKey(params, 'cppdefines', 'CPPDEFINES')
+ if params.has_key('prepend_includedirs'):
+ RenameKey(params, 'includedirs', 'CPPPATH', False)
+ else:
+ RenameKey(params, 'includedirs', 'CPPPATH')
+ RenameKey(params, 'ccflags', 'CCFLAGS', False)
+ RenameKey(params, 'libdirs', 'LIBPATH')
+ RenameKey(params, 'link_flags', 'LINKFLAGS')
+ RenameKey(params, 'libs', 'LIBS')
+
+ srcs = GetEntry(params, 'srcs')
+ if srcs is None or len(srcs) == 0:
+ return None
+
+ # invoke the builder function
+ return component(name, srcs, **params)
+
+
+def AddToDict(dictionary, key, values, append=True):
+ """Merge the given key value(s) pair into a dictionary. If it contains an
+ entry with that key already, then combine by appending or prepending the
+ values as directed. Otherwise, assign a new keyvalue pair.
+ """
+ if values is None:
+ return
+
+ if not dictionary.has_key(key):
+ dictionary[key] = values
+ return
+
+ cur = dictionary[key]
+ # TODO(dape): Make sure that there are no duplicates
+ # in the list. I can't use python set for this since
+ # the nodes that are returned by the SCONS builders
+ # are not hashable.
+ # dictionary[key] = list(set(cur).union(set(values)))
+ if append:
+ dictionary[key] = cur + values
+ else:
+ dictionary[key] = values + cur
+
+
+def CombineDicts(a, b):
+ """Unions two dictionaries by combining values of keys shared between them.
+ """
+ c = {}
+ for key in a:
+ if b.has_key(key):
+ c[key] = a[key] + b.pop(key)
+ else:
+ c[key] = a[key]
+
+ for key in b:
+ c[key] = b[key]
+
+ return c
+
+
+def RenameKey(d, old, new, append=True):
+ AddToDict(d, new, GetEntry(d, old), append)
diff --git a/third_party/libjingle/source/talk/third_party/expat/BUILD b/third_party/libjingle/source/talk/third_party/expat/BUILD
new file mode 100644
index 0000000..1293685
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/BUILD
@@ -0,0 +1,9 @@
+# -*- mode: python; -*-
+#
+# Copyright 2007 Google Inc.
+# All Rights Reserved.
+#
+# Description:
+# Expat - a small open-source XML parser.
+
+licenses(['notice']) # MIT
diff --git a/third_party/libjingle/source/talk/third_party/expat/COPYING.txt b/third_party/libjingle/source/talk/third_party/expat/COPYING.txt
new file mode 100644
index 0000000..dcb4506
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/COPYING.txt
@@ -0,0 +1,22 @@
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+ and Clark Cooper
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/third_party/libjingle/source/talk/third_party/expat/OWNERS b/third_party/libjingle/source/talk/third_party/expat/OWNERS
new file mode 100644
index 0000000..82f5639
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/OWNERS
@@ -0,0 +1,3 @@
+dimich
+moishel
+
diff --git a/third_party/libjingle/source/talk/third_party/expat/README.google b/third_party/libjingle/source/talk/third_party/expat/README.google
new file mode 100644
index 0000000..21ef9ec
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/README.google
@@ -0,0 +1,14 @@
+URL: http://sourceforge.net/projects/expat/
+License: MIT
+License File: COPYING.txt
+
+Description:
+This is Expat XML parser - very lightweight C library for
+parsing XML, typically linked as a static lib.
+
+The typical usage of the library is to compile it in or compile it
+into a static lib and link in.
+
+Please refer to the specific version's README.google for more
+information on how to build/use etc.
+
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/BUILD b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/BUILD
new file mode 100644
index 0000000..1293685
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/BUILD
@@ -0,0 +1,9 @@
+# -*- mode: python; -*-
+#
+# Copyright 2007 Google Inc.
+# All Rights Reserved.
+#
+# Description:
+# Expat - a small open-source XML parser.
+
+licenses(['notice']) # MIT
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/COPYING.txt b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/COPYING.txt
new file mode 100644
index 0000000..dcb4506
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/COPYING.txt
@@ -0,0 +1,22 @@
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+ and Clark Cooper
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Changes.txt b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Changes.txt
new file mode 100644
index 0000000..1e885ab
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Changes.txt
@@ -0,0 +1,169 @@
+Release 2.0.1 Tue June 5 2007
+ - Fixed bugs #1515266, 1515600: The character data handler's calling
+ of XML_StopParser() was not handled properly; if the parser was
+ stopped and the handler set to NULL, the parser would segfault.
+ - Fixed bug #1690883: Expat failed on EBCDIC systems as it assumed
+ some character constants to be ASCII encoded.
+ - Minor cleanups of the test harness.
+ - Fixed xmlwf bug #1513566: "out of memory" error on file size zero.
+ - Fixed outline.c bug #1543233: missing a final XML_ParserFree() call.
+ - Fixes and improvements for Windows platform:
+ bugs #1409451, #1476160, 1548182, 1602769, 1717322.
+ - Build fixes for various platforms:
+ HP-UX, Tru64, Solaris 9: patch #1437840, bug #1196180.
+ All Unix: #1554618 (refreshed config.sub/config.guess).
+ #1490371, #1613457: support both, DESTDIR and INSTALL_ROOT,
+ without relying on GNU-Make specific features.
+ #1647805: Patched configure.in to work better with Intel compiler.
+ - Fixes to Makefile.in to have make check work correctly:
+ bugs #1408143, #1535603, #1536684.
+ - Added Open Watcom support: patch #1523242.
+
+Release 2.0.0 Wed Jan 11 2006
+ - We no longer use the "check" library for C unit testing; we
+ always use the (partial) internal implementation of the API.
+ - Report XML_NS setting via XML_GetFeatureList().
+ - Fixed headers for use from C++.
+ - XML_GetCurrentLineNumber() and XML_GetCurrentColumnNumber()
+ now return unsigned integers.
+ - Added XML_LARGE_SIZE switch to enable 64-bit integers for
+ byte indexes and line/column numbers.
+ - Updated to use libtool 1.5.22 (the most recent).
+ - Added support for AmigaOS.
+ - Some mostly minor bug fixes. SF issues include: 1006708,
+ 1021776, 1023646, 1114960, 1156398, 1221160, 1271642.
+
+Release 1.95.8 Fri Jul 23 2004
+ - Major new feature: suspend/resume. Handlers can now request
+ that a parse be suspended for later resumption or aborted
+ altogether. See "Temporarily Stopping Parsing" in the
+ documentation for more details.
+ - Some mostly minor bug fixes, but compilation should no
+ longer generate warnings on most platforms. SF issues
+ include: 827319, 840173, 846309, 888329, 896188, 923913,
+ 928113, 961698, 985192.
+
+Release 1.95.7 Mon Oct 20 2003
+ - Fixed enum XML_Status issue (reported on SourceForge many
+ times), so compilers that are properly picky will be happy.
+ - Introduced an XMLCALL macro to control the calling
+ convention used by the Expat API; this macro should be used
+ to annotate prototypes and definitions of callback
+ implementations in code compiled with a calling convention
+ other than the default convention for the host platform.
+ - Improved ability to build without the configure-generated
+ expat_config.h header. This is useful for applications
+ which embed Expat rather than linking in the library.
+ - Fixed a variety of bugs: see SF issues 458907, 609603,
+ 676844, 679754, 692878, 692964, 695401, 699323, 699487,
+ 820946.
+ - Improved hash table lookups.
+ - Added more regression tests and improved documentation.
+
+Release 1.95.6 Tue Jan 28 2003
+ - Added XML_FreeContentModel().
+ - Added XML_MemMalloc(), XML_MemRealloc(), XML_MemFree().
+ - Fixed a variety of bugs: see SF issues 615606, 616863,
+ 618199, 653180, 673791.
+ - Enhanced the regression test suite.
+ - Man page improvements: includes SF issue 632146.
+
+Release 1.95.5 Fri Sep 6 2002
+ - Added XML_UseForeignDTD() for improved SAX2 support.
+ - Added XML_GetFeatureList().
+ - Defined XML_Bool type and the values XML_TRUE and XML_FALSE.
+ - Use an incomplete struct instead of a void* for the parser
+ (may not retain).
+ - Fixed UTF-8 decoding bug that caused legal UTF-8 to be rejected.
+ - Finally fixed bug where default handler would report DTD
+ events that were already handled by another handler.
+ Initial patch contributed by Darryl Miles.
+ - Removed unnecessary DllMain() function that caused static
+ linking into a DLL to be difficult.
+ - Added VC++ projects for building static libraries.
+ - Reduced line-length for all source code and headers to be
+ no longer than 80 characters, to help with AS/400 support.
+ - Reduced memory copying during parsing (SF patch #600964).
+ - Fixed a variety of bugs: see SF issues 580793, 434664,
+ 483514, 580503, 581069, 584041, 584183, 584832, 585537,
+ 596555, 596678, 598352, 598944, 599715, 600479, 600971.
+
+Release 1.95.4 Fri Jul 12 2002
+ - Added support for VMS, contributed by Craig Berry. See
+ vms/README.vms for more information.
+ - Added Mac OS (classic) support, with a makefile for MPW,
+ contributed by Thomas Wegner and Daryle Walker.
+ - Added Borland C++ Builder 5 / BCC 5.5 support, contributed
+ by Patrick McConnell (SF patch #538032).
+ - Fixed a variety of bugs: see SF issues 441449, 563184,
+ 564342, 566334, 566901, 569461, 570263, 575168, 579196.
+ - Made skippedEntityHandler conform to SAX2 (see source comment)
+ - Re-implemented WFC: Entity Declared from XML 1.0 spec and
+ added a new error "entity declared in parameter entity":
+ see SF bug report 569461 and SF patch 578161
+ - Re-implemented section 5.1 from XML 1.0 spec:
+ see SF bug report 570263 and SF patch 578161
+
+Release 1.95.3 Mon Jun 3 2002
+ - Added a project to the MSVC workspace to create a wchar_t
+ version of the library; the DLLs are named libexpatw.dll.
+ - Changed the name of the Windows DLLs from expat.dll to
+ libexpat.dll; this fixes SF bug #432456.
+ - Added the XML_ParserReset() API function.
+ - Fixed XML_SetReturnNSTriplet() to work for element names.
+ - Made the XML_UNICODE builds usable (thanks, Karl!).
+ - Allow xmlwf to read from standard input.
+ - Install a man page for xmlwf on Unix systems.
+ - Fixed many bugs; see SF bug reports 231864, 461380, 464837,
+ 466885, 469226, 477667, 484419, 487840, 494749, 496505,
+ 547350. Other bugs which we can't test as easily may also
+ have been fixed, especially in the area of build support.
+
+Release 1.95.2 Fri Jul 27 2001
+ - More changes to make MSVC happy with the build; add a single
+ workspace to support both the library and xmlwf application.
+ - Added a Windows installer for Windows users; includes
+ xmlwf.exe.
+ - Added compile-time constants that can be used to determine the
+ Expat version
+ - Removed a lot of GNU-specific dependencies to aide portability
+ among the various Unix flavors.
+ - Fix the UTF-8 BOM bug.
+ - Cleaned up warning messages for several compilers.
+ - Added the -Wall, -Wstrict-prototypes options for GCC.
+
+Release 1.95.1 Sun Oct 22 15:11:36 EDT 2000
+ - Changes to get expat to build under Microsoft compiler
+ - Removed all aborts and instead return an UNEXPECTED_STATE error.
+ - Fixed a bug where a stray '%' in an entity value would cause an
+ abort.
+ - Defined XML_SetEndNamespaceDeclHandler. Thanks to Darryl Miles for
+ finding this oversight.
+ - Changed default patterns in lib/Makefile.in to fit non-GNU makes
+ Thanks to robin@unrated.net for reporting and providing an
+ account to test on.
+ - The reference had the wrong label for XML_SetStartNamespaceDecl.
+ Reported by an anonymous user.
+
+Release 1.95.0 Fri Sep 29 2000
+ - XML_ParserCreate_MM
+ Allows you to set a memory management suite to replace the
+ standard malloc,realloc, and free.
+ - XML_SetReturnNSTriplet
+ If you turn this feature on when namespace processing is in
+ effect, then qualified, prefixed element and attribute names
+ are returned as "uri|name|prefix" where '|' is whatever
+ separator character is used in namespace processing.
+ - Merged in features from perl-expat
+ o XML_SetElementDeclHandler
+ o XML_SetAttlistDeclHandler
+ o XML_SetXmlDeclHandler
+ o XML_SetEntityDeclHandler
+ o StartDoctypeDeclHandler takes 3 additional parameters:
+ sysid, pubid, has_internal_subset
+ o Many paired handler setters (like XML_SetElementHandler)
+ now have corresponding individual handler setters
+ o XML_GetInputContext for getting the input context of
+ the current parse position.
+ - Added reference material
+ - Packaged into a distribution that builds a sharable library
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/expat.png b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/expat.png
new file mode 100644
index 0000000..5bc0726
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/expat.png
Binary files differ
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/reference.html b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/reference.html
new file mode 100644
index 0000000..a315870
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/reference.html
@@ -0,0 +1,2341 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<!-- Copyright 1999,2000 Clark Cooper <coopercc@netheaven.com>
+ All rights reserved.
+ This is free software. You may distribute or modify according to
+ the terms of the MIT/X License -->
+ <title>Expat XML Parser</title>
+ <meta name="author" content="Clark Cooper, coopercc@netheaven.com" />
+ <meta http-equiv="Content-Style-Type" content="text/css" />
+ <link href="style.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+ <table cellspacing="0" cellpadding="0" width="100%">
+ <tr>
+ <td class="corner"><img src="expat.png" alt="(Expat logo)" /></td>
+ <td class="banner"><h1>The Expat XML Parser</h1></td>
+ </tr>
+ <tr>
+ <td class="releaseno">Release 2.0.1</td>
+ <td></td>
+ </tr>
+ </table>
+<div class="content">
+
+<p>Expat is a library, written in C, for parsing XML documents. It's
+the underlying XML parser for the open source Mozilla project, Perl's
+<code>XML::Parser</code>, Python's <code>xml.parsers.expat</code>, and
+other open-source XML parsers.</p>
+
+<p>This library is the creation of James Clark, who's also given us
+groff (an nroff look-alike), Jade (an implemention of ISO's DSSSL
+stylesheet language for SGML), XP (a Java XML parser package), XT (a
+Java XSL engine). James was also the technical lead on the XML
+Working Group at W3C that produced the XML specification.</p>
+
+<p>This is free software, licensed under the <a
+href="../COPYING">MIT/X Consortium license</a>. You may download it
+from <a href="http://www.libexpat.org/">the Expat home page</a>.
+</p>
+
+<p>The bulk of this document was originally commissioned as an article
+by <a href="http://www.xml.com/">XML.com</a>. They graciously allowed
+Clark Cooper to retain copyright and to distribute it with Expat.
+This version has been substantially extended to include documentation
+on features which have been added since the original article was
+published, and additional information on using the original
+interface.</p>
+
+<hr />
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#overview">Overview</a></li>
+ <li><a href="#building">Building and Installing</a></li>
+ <li><a href="#using">Using Expat</a></li>
+ <li><a href="#reference">Reference</a>
+ <ul>
+ <li><a href="#creation">Parser Creation Functions</a>
+ <ul>
+ <li><a href="#XML_ParserCreate">XML_ParserCreate</a></li>
+ <li><a href="#XML_ParserCreateNS">XML_ParserCreateNS</a></li>
+ <li><a href="#XML_ParserCreate_MM">XML_ParserCreate_MM</a></li>
+ <li><a href="#XML_ExternalEntityParserCreate">XML_ExternalEntityParserCreate</a></li>
+ <li><a href="#XML_ParserFree">XML_ParserFree</a></li>
+ <li><a href="#XML_ParserReset">XML_ParserReset</a></li>
+ </ul>
+ </li>
+ <li><a href="#parsing">Parsing Functions</a>
+ <ul>
+ <li><a href="#XML_Parse">XML_Parse</a></li>
+ <li><a href="#XML_ParseBuffer">XML_ParseBuffer</a></li>
+ <li><a href="#XML_GetBuffer">XML_GetBuffer</a></li>
+ <li><a href="#XML_StopParser">XML_StopParser</a></li>
+ <li><a href="#XML_ResumeParser">XML_ResumeParser</a></li>
+ <li><a href="#XML_GetParsingStatus">XML_GetParsingStatus</a></li>
+ </ul>
+ </li>
+ <li><a href="#setting">Handler Setting Functions</a>
+ <ul>
+ <li><a href="#XML_SetStartElementHandler">XML_SetStartElementHandler</a></li>
+ <li><a href="#XML_SetEndElementHandler">XML_SetEndElementHandler</a></li>
+ <li><a href="#XML_SetElementHandler">XML_SetElementHandler</a></li>
+ <li><a href="#XML_SetCharacterDataHandler">XML_SetCharacterDataHandler</a></li>
+ <li><a href="#XML_SetProcessingInstructionHandler">XML_SetProcessingInstructionHandler</a></li>
+ <li><a href="#XML_SetCommentHandler">XML_SetCommentHandler</a></li>
+ <li><a href="#XML_SetStartCdataSectionHandler">XML_SetStartCdataSectionHandler</a></li>
+ <li><a href="#XML_SetEndCdataSectionHandler">XML_SetEndCdataSectionHandler</a></li>
+ <li><a href="#XML_SetCdataSectionHandler">XML_SetCdataSectionHandler</a></li>
+ <li><a href="#XML_SetDefaultHandler">XML_SetDefaultHandler</a></li>
+ <li><a href="#XML_SetDefaultHandlerExpand">XML_SetDefaultHandlerExpand</a></li>
+ <li><a href="#XML_SetExternalEntityRefHandler">XML_SetExternalEntityRefHandler</a></li>
+ <li><a href="#XML_SetExternalEntityRefHandlerArg">XML_SetExternalEntityRefHandlerArg</a></li>
+ <li><a href="#XML_SetSkippedEntityHandler">XML_SetSkippedEntityHandler</a></li>
+ <li><a href="#XML_SetUnknownEncodingHandler">XML_SetUnknownEncodingHandler</a></li>
+ <li><a href="#XML_SetStartNamespaceDeclHandler">XML_SetStartNamespaceDeclHandler</a></li>
+ <li><a href="#XML_SetEndNamespaceDeclHandler">XML_SetEndNamespaceDeclHandler</a></li>
+ <li><a href="#XML_SetNamespaceDeclHandler">XML_SetNamespaceDeclHandler</a></li>
+ <li><a href="#XML_SetXmlDeclHandler">XML_SetXmlDeclHandler</a></li>
+ <li><a href="#XML_SetStartDoctypeDeclHandler">XML_SetStartDoctypeDeclHandler</a></li>
+ <li><a href="#XML_SetEndDoctypeDeclHandler">XML_SetEndDoctypeDeclHandler</a></li>
+ <li><a href="#XML_SetDoctypeDeclHandler">XML_SetDoctypeDeclHandler</a></li>
+ <li><a href="#XML_SetElementDeclHandler">XML_SetElementDeclHandler</a></li>
+ <li><a href="#XML_SetAttlistDeclHandler">XML_SetAttlistDeclHandler</a></li>
+ <li><a href="#XML_SetEntityDeclHandler">XML_SetEntityDeclHandler</a></li>
+ <li><a href="#XML_SetUnparsedEntityDeclHandler">XML_SetUnparsedEntityDeclHandler</a></li>
+ <li><a href="#XML_SetNotationDeclHandler">XML_SetNotationDeclHandler</a></li>
+ <li><a href="#XML_SetNotStandaloneHandler">XML_SetNotStandaloneHandler</a></li>
+ </ul>
+ </li>
+ <li><a href="#position">Parse Position and Error Reporting Functions</a>
+ <ul>
+ <li><a href="#XML_GetErrorCode">XML_GetErrorCode</a></li>
+ <li><a href="#XML_ErrorString">XML_ErrorString</a></li>
+ <li><a href="#XML_GetCurrentByteIndex">XML_GetCurrentByteIndex</a></li>
+ <li><a href="#XML_GetCurrentLineNumber">XML_GetCurrentLineNumber</a></li>
+ <li><a href="#XML_GetCurrentColumnNumber">XML_GetCurrentColumnNumber</a></li>
+ <li><a href="#XML_GetCurrentByteCount">XML_GetCurrentByteCount</a></li>
+ <li><a href="#XML_GetInputContext">XML_GetInputContext</a></li>
+ </ul>
+ </li>
+ <li><a href="#miscellaneous">Miscellaneous Functions</a>
+ <ul>
+ <li><a href="#XML_SetUserData">XML_SetUserData</a></li>
+ <li><a href="#XML_GetUserData">XML_GetUserData</a></li>
+ <li><a href="#XML_UseParserAsHandlerArg">XML_UseParserAsHandlerArg</a></li>
+ <li><a href="#XML_SetBase">XML_SetBase</a></li>
+ <li><a href="#XML_GetBase">XML_GetBase</a></li>
+ <li><a href="#XML_GetSpecifiedAttributeCount">XML_GetSpecifiedAttributeCount</a></li>
+ <li><a href="#XML_GetIdAttributeIndex">XML_GetIdAttributeIndex</a></li>
+ <li><a href="#XML_SetEncoding">XML_SetEncoding</a></li>
+ <li><a href="#XML_SetParamEntityParsing">XML_SetParamEntityParsing</a></li>
+ <li><a href="#XML_UseForeignDTD">XML_UseForeignDTD</a></li>
+ <li><a href="#XML_SetReturnNSTriplet">XML_SetReturnNSTriplet</a></li>
+ <li><a href="#XML_DefaultCurrent">XML_DefaultCurrent</a></li>
+ <li><a href="#XML_ExpatVersion">XML_ExpatVersion</a></li>
+ <li><a href="#XML_ExpatVersionInfo">XML_ExpatVersionInfo</a></li>
+ <li><a href="#XML_GetFeatureList">XML_GetFeatureList</a></li>
+ <li><a href="#XML_FreeContentModel">XML_FreeContentModel</a></li>
+ <li><a href="#XML_MemMalloc">XML_MemMalloc</a></li>
+ <li><a href="#XML_MemRealloc">XML_MemRealloc</a></li>
+ <li><a href="#XML_MemFree">XML_MemFree</a></li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<hr />
+<h2><a name="overview">Overview</a></h2>
+
+<p>Expat is a stream-oriented parser. You register callback (or
+handler) functions with the parser and then start feeding it the
+document. As the parser recognizes parts of the document, it will
+call the appropriate handler for that part (if you've registered one.)
+The document is fed to the parser in pieces, so you can start parsing
+before you have all the document. This also allows you to parse really
+huge documents that won't fit into memory.</p>
+
+<p>Expat can be intimidating due to the many kinds of handlers and
+options you can set. But you only need to learn four functions in
+order to do 90% of what you'll want to do with it:</p>
+
+<dl>
+
+<dt><code><a href= "#XML_ParserCreate"
+ >XML_ParserCreate</a></code></dt>
+ <dd>Create a new parser object.</dd>
+
+<dt><code><a href= "#XML_SetElementHandler"
+ >XML_SetElementHandler</a></code></dt>
+ <dd>Set handlers for start and end tags.</dd>
+
+<dt><code><a href= "#XML_SetCharacterDataHandler"
+ >XML_SetCharacterDataHandler</a></code></dt>
+ <dd>Set handler for text.</dd>
+
+<dt><code><a href= "#XML_Parse"
+ >XML_Parse</a></code></dt>
+ <dd>Pass a buffer full of document to the parser</dd>
+</dl>
+
+<p>These functions and others are described in the <a
+href="#reference">reference</a> part of this document. The reference
+section also describes in detail the parameters passed to the
+different types of handlers.</p>
+
+<p>Let's look at a very simple example program that only uses 3 of the
+above functions (it doesn't need to set a character handler.) The
+program <a href="../examples/outline.c">outline.c</a> prints an
+element outline, indenting child elements to distinguish them from the
+parent element that contains them. The start handler does all the
+work. It prints two indenting spaces for every level of ancestor
+elements, then it prints the element and attribute
+information. Finally it increments the global <code>Depth</code>
+variable.</p>
+
+<pre class="eg">
+int Depth;
+
+void XMLCALL
+start(void *data, const char *el, const char **attr) {
+ int i;
+
+ for (i = 0; i &lt; Depth; i++)
+ printf(" ");
+
+ printf("%s", el);
+
+ for (i = 0; attr[i]; i += 2) {
+ printf(" %s='%s'", attr[i], attr[i + 1]);
+ }
+
+ printf("\n");
+ Depth++;
+} /* End of start handler */
+</pre>
+
+<p>The end tag simply does the bookkeeping work of decrementing
+<code>Depth</code>.</p>
+<pre class="eg">
+void XMLCALL
+end(void *data, const char *el) {
+ Depth--;
+} /* End of end handler */
+</pre>
+
+<p>Note the <code>XMLCALL</code> annotation used for the callbacks.
+This is used to ensure that the Expat and the callbacks are using the
+same calling convention in case the compiler options used for Expat
+itself and the client code are different. Expat tries not to care
+what the default calling convention is, though it may require that it
+be compiled with a default convention of "cdecl" on some platforms.
+For code which uses Expat, however, the calling convention is
+specified by the <code>XMLCALL</code> annotation on most platforms;
+callbacks should be defined using this annotation.</p>
+
+<p>The <code>XMLCALL</code> annotation was added in Expat 1.95.7, but
+existing working Expat applications don't need to add it (since they
+are already using the "cdecl" calling convention, or they wouldn't be
+working). The annotation is only needed if the default calling
+convention may be something other than "cdecl". To use the annotation
+safely with older versions of Expat, you can conditionally define it
+<em>after</em> including Expat's header file:</p>
+
+<pre class="eg">
+#include &lt;expat.h&gt;
+
+#ifndef XMLCALL
+#if defined(_MSC_EXTENSIONS) &amp;&amp; !defined(__BEOS__) &amp;&amp; !defined(__CYGWIN__)
+#define XMLCALL __cdecl
+#elif defined(__GNUC__)
+#define XMLCALL __attribute__((cdecl))
+#else
+#define XMLCALL
+#endif
+#endif
+</pre>
+
+<p>After creating the parser, the main program just has the job of
+shoveling the document to the parser so that it can do its work.</p>
+
+<hr />
+<h2><a name="building">Building and Installing Expat</a></h2>
+
+<p>The Expat distribution comes as a compressed (with GNU gzip) tar
+file. You may download the latest version from <a href=
+"http://sourceforge.net/projects/expat/" >Source Forge</a>. After
+unpacking this, cd into the directory. Then follow either the Win32
+directions or Unix directions below.</p>
+
+<h3>Building under Win32</h3>
+
+<p>If you're using the GNU compiler under cygwin, follow the Unix
+directions in the next section. Otherwise if you have Microsoft's
+Developer Studio installed, then from Windows Explorer double-click on
+"expat.dsp" in the lib directory and build and install in the usual
+manner.</p>
+
+<p>Alternatively, you may download the Win32 binary package that
+contains the "expat.h" include file and a pre-built DLL.</p>
+
+<h3>Building under Unix (or GNU)</h3>
+
+<p>First you'll need to run the configure shell script in order to
+configure the Makefiles and headers for your system.</p>
+
+<p>If you're happy with all the defaults that configure picks for you,
+and you have permission on your system to install into /usr/local, you
+can install Expat with this sequence of commands:</p>
+
+<pre class="eg">
+./configure
+make
+make install
+</pre>
+
+<p>There are some options that you can provide to this script, but the
+only one we'll mention here is the <code>--prefix</code> option. You
+can find out all the options available by running configure with just
+the <code>--help</code> option.</p>
+
+<p>By default, the configure script sets things up so that the library
+gets installed in <code>/usr/local/lib</code> and the associated
+header file in <code>/usr/local/include</code>. But if you were to
+give the option, <code>--prefix=/home/me/mystuff</code>, then the
+library and header would get installed in
+<code>/home/me/mystuff/lib</code> and
+<code>/home/me/mystuff/include</code> respectively.</p>
+
+<h3>Configuring Expat Using the Pre-Processor</h3>
+
+<p>Expat's feature set can be configured using a small number of
+pre-processor definitions. The definition of this symbols does not
+affect the set of entry points for Expat, only the behavior of the API
+and the definition of character types in the case of
+<code>XML_UNICODE_WCHAR_T</code>. The symbols are:</p>
+
+<dl class="cpp-symbols">
+<dt>XML_DTD</dt>
+<dd>Include support for using and reporting DTD-based content. If
+this is defined, default attribute values from an external DTD subset
+are reported and attribute value normalization occurs based on the
+type of attributes defined in the external subset. Without
+this, Expat has a smaller memory footprint and can be faster, but will
+not load external entities or process conditional sections. This does
+not affect the set of functions available in the API.</dd>
+
+<dt>XML_NS</dt>
+<dd>When defined, support for the <cite><a href=
+"http://www.w3.org/TR/REC-xml-names/" >Namespaces in XML</a></cite>
+specification is included.</dd>
+
+<dt>XML_UNICODE</dt>
+<dd>When defined, character data reported to the application is
+encoded in UTF-16 using wide characters of the type
+<code>XML_Char</code>. This is implied if
+<code>XML_UNICODE_WCHAR_T</code> is defined.</dd>
+
+<dt>XML_UNICODE_WCHAR_T</dt>
+<dd>If defined, causes the <code>XML_Char</code> character type to be
+defined using the <code>wchar_t</code> type; otherwise, <code>unsigned
+short</code> is used. Defining this implies
+<code>XML_UNICODE</code>.</dd>
+
+<dt>XML_LARGE_SIZE</dt>
+<dd>If defined, causes the <code>XML_Size</code> and <code>XML_Index</code>
+integer types to be at least 64 bits in size. This is intended to support
+processing of very large input streams, where the return values of
+<code><a href="#XML_GetCurrentByteIndex" >XML_GetCurrentByteIndex</a></code>,
+<code><a href="#XML_GetCurrentLineNumber" >XML_GetCurrentLineNumber</a></code> and
+<code><a href="#XML_GetCurrentColumnNumber" >XML_GetCurrentColumnNumber</a></code>
+could overflow. It may not be supported by all compilers, and is turned
+off by default.</dd>
+
+<dt>XML_CONTEXT_BYTES</dt>
+<dd>The number of input bytes of markup context which the parser will
+ensure are available for reporting via <code><a href=
+"#XML_GetInputContext" >XML_GetInputContext</a></code>. This is
+normally set to 1024, and must be set to a positive interger. If this
+is not defined, the input context will not be available and <code><a
+href= "#XML_GetInputContext" >XML_GetInputContext</a></code> will
+always report NULL. Without this, Expat has a smaller memory
+footprint and can be faster.</dd>
+
+<dt>XML_STATIC</dt>
+<dd>On Windows, this should be set if Expat is going to be linked
+statically with the code that calls it; this is required to get all
+the right MSVC magic annotations correct. This is ignored on other
+platforms.</dd>
+</dl>
+
+<hr />
+<h2><a name="using">Using Expat</a></h2>
+
+<h3>Compiling and Linking Against Expat</h3>
+
+<p>Unless you installed Expat in a location not expected by your
+compiler and linker, all you have to do to use Expat in your programs
+is to include the Expat header (<code>#include &lt;expat.h&gt;</code>)
+in your files that make calls to it and to tell the linker that it
+needs to link against the Expat library. On Unix systems, this would
+usually be done with the <code>-lexpat</code> argument. Otherwise,
+you'll need to tell the compiler where to look for the Expat header
+and the linker where to find the Expat library. You may also need to
+take steps to tell the operating system where to find this library at
+run time.</p>
+
+<p>On a Unix-based system, here's what a Makefile might look like when
+Expat is installed in a standard location:</p>
+
+<pre class="eg">
+CC=cc
+LDFLAGS=
+LIBS= -lexpat
+xmlapp: xmlapp.o
+ $(CC) $(LDFLAGS) -o xmlapp xmlapp.o $(LIBS)
+</pre>
+
+<p>If you installed Expat in, say, <code>/home/me/mystuff</code>, then
+the Makefile would look like this:</p>
+
+<pre class="eg">
+CC=cc
+CFLAGS= -I/home/me/mystuff/include
+LDFLAGS=
+LIBS= -L/home/me/mystuff/lib -lexpat
+xmlapp: xmlapp.o
+ $(CC) $(LDFLAGS) -o xmlapp xmlapp.o $(LIBS)
+</pre>
+
+<p>You'd also have to set the environment variable
+<code>LD_LIBRARY_PATH</code> to <code>/home/me/mystuff/lib</code> (or
+to <code>${LD_LIBRARY_PATH}:/home/me/mystuff/lib</code> if
+LD_LIBRARY_PATH already has some directories in it) in order to run
+your application.</p>
+
+<h3>Expat Basics</h3>
+
+<p>As we saw in the example in the overview, the first step in parsing
+an XML document with Expat is to create a parser object. There are <a
+href="#creation">three functions</a> in the Expat API for creating a
+parser object. However, only two of these (<code><a href=
+"#XML_ParserCreate" >XML_ParserCreate</a></code> and <code><a href=
+"#XML_ParserCreateNS" >XML_ParserCreateNS</a></code>) can be used for
+constructing a parser for a top-level document. The object returned
+by these functions is an opaque pointer (i.e. "expat.h" declares it as
+void *) to data with further internal structure. In order to free the
+memory associated with this object you must call <code><a href=
+"#XML_ParserFree" >XML_ParserFree</a></code>. Note that if you have
+provided any <a href="#userdata">user data</a> that gets stored in the
+parser, then your application is responsible for freeing it prior to
+calling <code>XML_ParserFree</code>.</p>
+
+<p>The objects returned by the parser creation functions are good for
+parsing only one XML document or external parsed entity. If your
+application needs to parse many XML documents, then it needs to create
+a parser object for each one. The best way to deal with this is to
+create a higher level object that contains all the default
+initialization you want for your parser objects.</p>
+
+<p>Walking through a document hierarchy with a stream oriented parser
+will require a good stack mechanism in order to keep track of current
+context. For instance, to answer the simple question, "What element
+does this text belong to?" requires a stack, since the parser may have
+descended into other elements that are children of the current one and
+has encountered this text on the way out.</p>
+
+<p>The things you're likely to want to keep on a stack are the
+currently opened element and it's attributes. You push this
+information onto the stack in the start handler and you pop it off in
+the end handler.</p>
+
+<p>For some tasks, it is sufficient to just keep information on what
+the depth of the stack is (or would be if you had one.) The outline
+program shown above presents one example. Another such task would be
+skipping over a complete element. When you see the start tag for the
+element you want to skip, you set a skip flag and record the depth at
+which the element started. When the end tag handler encounters the
+same depth, the skipped element has ended and the flag may be
+cleared. If you follow the convention that the root element starts at
+1, then you can use the same variable for skip flag and skip
+depth.</p>
+
+<pre class="eg">
+void
+init_info(Parseinfo *info) {
+ info->skip = 0;
+ info->depth = 1;
+ /* Other initializations here */
+} /* End of init_info */
+
+void XMLCALL
+rawstart(void *data, const char *el, const char **attr) {
+ Parseinfo *inf = (Parseinfo *) data;
+
+ if (! inf->skip) {
+ if (should_skip(inf, el, attr)) {
+ inf->skip = inf->depth;
+ }
+ else
+ start(inf, el, attr); /* This does rest of start handling */
+ }
+
+ inf->depth++;
+} /* End of rawstart */
+
+void XMLCALL
+rawend(void *data, const char *el) {
+ Parseinfo *inf = (Parseinfo *) data;
+
+ inf->depth--;
+
+ if (! inf->skip)
+ end(inf, el); /* This does rest of end handling */
+
+ if (inf->skip == inf->depth)
+ inf->skip = 0;
+} /* End rawend */
+</pre>
+
+<p>Notice in the above example the difference in how depth is
+manipulated in the start and end handlers. The end tag handler should
+be the mirror image of the start tag handler. This is necessary to
+properly model containment. Since, in the start tag handler, we
+incremented depth <em>after</em> the main body of start tag code, then
+in the end handler, we need to manipulate it <em>before</em> the main
+body. If we'd decided to increment it first thing in the start
+handler, then we'd have had to decrement it last thing in the end
+handler.</p>
+
+<h3 id="userdata">Communicating between handlers</h3>
+
+<p>In order to be able to pass information between different handlers
+without using globals, you'll need to define a data structure to hold
+the shared variables. You can then tell Expat (with the <code><a href=
+"#XML_SetUserData" >XML_SetUserData</a></code> function) to pass a
+pointer to this structure to the handlers. This is the first
+argument received by most handlers. In the <a href="#reference"
+>reference section</a>, an argument to a callback function is named
+<code>userData</code> and have type <code>void *</code> if the user
+data is passed; it will have the type <code>XML_Parser</code> if the
+parser itself is passed. When the parser is passed, the user data may
+be retrieved using <code><a href="#XML_GetUserData"
+>XML_GetUserData</a></code>.</p>
+
+<p>One common case where multiple calls to a single handler may need
+to communicate using an application data structure is the case when
+content passed to the character data handler (set by <code><a href=
+"#XML_SetCharacterDataHandler"
+>XML_SetCharacterDataHandler</a></code>) needs to be accumulated. A
+common first-time mistake with any of the event-oriented interfaces to
+an XML parser is to expect all the text contained in an element to be
+reported by a single call to the character data handler. Expat, like
+many other XML parsers, reports such data as a sequence of calls;
+there's no way to know when the end of the sequence is reached until a
+different callback is made. A buffer referenced by the user data
+structure proves both an effective and convenient place to accumulate
+character data.</p>
+
+<!-- XXX example needed here -->
+
+
+<h3>XML Version</h3>
+
+<p>Expat is an XML 1.0 parser, and as such never complains based on
+the value of the <code>version</code> pseudo-attribute in the XML
+declaration, if present.</p>
+
+<p>If an application needs to check the version number (to support
+alternate processing), it should use the <code><a href=
+"#XML_SetXmlDeclHandler" >XML_SetXmlDeclHandler</a></code> function to
+set a handler that uses the information in the XML declaration to
+determine what to do. This example shows how to check that only a
+version number of <code>"1.0"</code> is accepted:</p>
+
+<pre class="eg">
+static int wrong_version;
+static XML_Parser parser;
+
+static void XMLCALL
+xmldecl_handler(void *userData,
+ const XML_Char *version,
+ const XML_Char *encoding,
+ int standalone)
+{
+ static const XML_Char Version_1_0[] = {'1', '.', '0', 0};
+
+ int i;
+
+ for (i = 0; i &lt; (sizeof(Version_1_0) / sizeof(Version_1_0[0])); ++i) {
+ if (version[i] != Version_1_0[i]) {
+ wrong_version = 1;
+ /* also clear all other handlers: */
+ XML_SetCharacterDataHandler(parser, NULL);
+ ...
+ return;
+ }
+ }
+ ...
+}
+</pre>
+
+<h3>Namespace Processing</h3>
+
+<p>When the parser is created using the <code><a href=
+"#XML_ParserCreateNS" >XML_ParserCreateNS</a></code>, function, Expat
+performs namespace processing. Under namespace processing, Expat
+consumes <code>xmlns</code> and <code>xmlns:...</code> attributes,
+which declare namespaces for the scope of the element in which they
+occur. This means that your start handler will not see these
+attributes. Your application can still be informed of these
+declarations by setting namespace declaration handlers with <a href=
+"#XML_SetNamespaceDeclHandler"
+><code>XML_SetNamespaceDeclHandler</code></a>.</p>
+
+<p>Element type and attribute names that belong to a given namespace
+are passed to the appropriate handler in expanded form. By default
+this expanded form is a concatenation of the namespace URI, the
+separator character (which is the 2nd argument to <code><a href=
+"#XML_ParserCreateNS" >XML_ParserCreateNS</a></code>), and the local
+name (i.e. the part after the colon). Names with undeclared prefixes
+are not well-formed when namespace processing is enabled, and will
+trigger an error. Unprefixed attribute names are never expanded,
+and unprefixed element names are only expanded when they are in the
+scope of a default namespace.</p>
+
+<p>However if <code><a href= "#XML_SetReturnNSTriplet"
+>XML_SetReturnNSTriplet</a></code> has been called with a non-zero
+<code>do_nst</code> parameter, then the expanded form for names with
+an explicit prefix is a concatenation of: URI, separator, local name,
+separator, prefix.</p>
+
+<p>You can set handlers for the start of a namespace declaration and
+for the end of a scope of a declaration with the <code><a href=
+"#XML_SetNamespaceDeclHandler" >XML_SetNamespaceDeclHandler</a></code>
+function. The StartNamespaceDeclHandler is called prior to the start
+tag handler and the EndNamespaceDeclHandler is called after the
+corresponding end tag that ends the namespace's scope. The namespace
+start handler gets passed the prefix and URI for the namespace. For a
+default namespace declaration (xmlns='...'), the prefix will be null.
+The URI will be null for the case where the default namespace is being
+unset. The namespace end handler just gets the prefix for the closing
+scope.</p>
+
+<p>These handlers are called for each declaration. So if, for
+instance, a start tag had three namespace declarations, then the
+StartNamespaceDeclHandler would be called three times before the start
+tag handler is called, once for each declaration.</p>
+
+<h3>Character Encodings</h3>
+
+<p>While XML is based on Unicode, and every XML processor is required
+to recognized UTF-8 and UTF-16 (1 and 2 byte encodings of Unicode),
+other encodings may be declared in XML documents or entities. For the
+main document, an XML declaration may contain an encoding
+declaration:</p>
+<pre>
+&lt;?xml version="1.0" encoding="ISO-8859-2"?&gt;
+</pre>
+
+<p>External parsed entities may begin with a text declaration, which
+looks like an XML declaration with just an encoding declaration:</p>
+<pre>
+&lt;?xml encoding="Big5"?&gt;
+</pre>
+
+<p>With Expat, you may also specify an encoding at the time of
+creating a parser. This is useful when the encoding information may
+come from a source outside the document itself (like a higher level
+protocol.)</p>
+
+<p><a name="builtin_encodings"></a>There are four built-in encodings
+in Expat:</p>
+<ul>
+<li>UTF-8</li>
+<li>UTF-16</li>
+<li>ISO-8859-1</li>
+<li>US-ASCII</li>
+</ul>
+
+<p>Anything else discovered in an encoding declaration or in the
+protocol encoding specified in the parser constructor, triggers a call
+to the <code>UnknownEncodingHandler</code>. This handler gets passed
+the encoding name and a pointer to an <code>XML_Encoding</code> data
+structure. Your handler must fill in this structure and return
+<code>XML_STATUS_OK</code> if it knows how to deal with the
+encoding. Otherwise the handler should return
+<code>XML_STATUS_ERROR</code>. The handler also gets passed a pointer
+to an optional application data structure that you may indicate when
+you set the handler.</p>
+
+<p>Expat places restrictions on character encodings that it can
+support by filling in the <code>XML_Encoding</code> structure.
+include file:</p>
+<ol>
+<li>Every ASCII character that can appear in a well-formed XML document
+must be represented by a single byte, and that byte must correspond to
+it's ASCII encoding (except for the characters $@\^'{}~)</li>
+<li>Characters must be encoded in 4 bytes or less.</li>
+<li>All characters encoded must have Unicode scalar values less than or
+equal to 65535 (0xFFFF)<em>This does not apply to the built-in support
+for UTF-16 and UTF-8</em></li>
+<li>No character may be encoded by more that one distinct sequence of
+bytes</li>
+</ol>
+
+<p><code>XML_Encoding</code> contains an array of integers that
+correspond to the 1st byte of an encoding sequence. If the value in
+the array for a byte is zero or positive, then the byte is a single
+byte encoding that encodes the Unicode scalar value contained in the
+array. A -1 in this array indicates a malformed byte. If the value is
+-2, -3, or -4, then the byte is the beginning of a 2, 3, or 4 byte
+sequence respectively. Multi-byte sequences are sent to the convert
+function pointed at in the <code>XML_Encoding</code> structure. This
+function should return the Unicode scalar value for the sequence or -1
+if the sequence is malformed.</p>
+
+<p>One pitfall that novice Expat users are likely to fall into is that
+although Expat may accept input in various encodings, the strings that
+it passes to the handlers are always encoded in UTF-8 or UTF-16
+(depending on how Expat was compiled). Your application is responsible
+for any translation of these strings into other encodings.</p>
+
+<h3>Handling External Entity References</h3>
+
+<p>Expat does not read or parse external entities directly. Note that
+any external DTD is a special case of an external entity. If you've
+set no <code>ExternalEntityRefHandler</code>, then external entity
+references are silently ignored. Otherwise, it calls your handler with
+the information needed to read and parse the external entity.</p>
+
+<p>Your handler isn't actually responsible for parsing the entity, but
+it is responsible for creating a subsidiary parser with <code><a href=
+"#XML_ExternalEntityParserCreate"
+>XML_ExternalEntityParserCreate</a></code> that will do the job. This
+returns an instance of <code>XML_Parser</code> that has handlers and
+other data structures initialized from the parent parser. You may then
+use <code><a href= "#XML_Parse" >XML_Parse</a></code> or <code><a
+href= "#XML_ParseBuffer">XML_ParseBuffer</a></code> calls against this
+parser. Since external entities my refer to other external entities,
+your handler should be prepared to be called recursively.</p>
+
+<h3>Parsing DTDs</h3>
+
+<p>In order to parse parameter entities, before starting the parse,
+you must call <code><a href= "#XML_SetParamEntityParsing"
+>XML_SetParamEntityParsing</a></code> with one of the following
+arguments:</p>
+<dl>
+<dt><code>XML_PARAM_ENTITY_PARSING_NEVER</code></dt>
+<dd>Don't parse parameter entities or the external subset</dd>
+<dt><code>XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE</code></dt>
+<dd>Parse parameter entites and the external subset unless
+<code>standalone</code> was set to "yes" in the XML declaration.</dd>
+<dt><code>XML_PARAM_ENTITY_PARSING_ALWAYS</code></dt>
+<dd>Always parse parameter entities and the external subset</dd>
+</dl>
+
+<p>In order to read an external DTD, you also have to set an external
+entity reference handler as described above.</p>
+
+<h3 id="stop-resume">Temporarily Stopping Parsing</h3>
+
+<p>Expat 1.95.8 introduces a new feature: its now possible to stop
+parsing temporarily from within a handler function, even if more data
+has already been passed into the parser. Applications for this
+include</p>
+
+<ul>
+ <li>Supporting the <a href= "http://www.w3.org/TR/xinclude/"
+ >XInclude</a> specification.</li>
+
+ <li>Delaying further processing until additional information is
+ available from some other source.</li>
+
+ <li>Adjusting processor load as task priorities shift within an
+ application.</li>
+
+ <li>Stopping parsing completely (simply free or reset the parser
+ instead of resuming in the outer parsing loop). This can be useful
+ if a application-domain error is found in the XML being parsed or if
+ the result of the parse is determined not to be useful after
+ all.</li>
+</ul>
+
+<p>To take advantage of this feature, the main parsing loop of an
+application needs to support this specifically. It cannot be
+supported with a parsing loop compatible with Expat 1.95.7 or
+earlier (though existing loops will continue to work without
+supporting the stop/resume feature).</p>
+
+<p>An application that uses this feature for a single parser will have
+the rough structure (in pseudo-code):</p>
+
+<pre class="pseudocode">
+fd = open_input()
+p = create_parser()
+
+if parse_xml(p, fd) {
+ /* suspended */
+
+ int suspended = 1;
+
+ while (suspended) {
+ do_something_else()
+ if ready_to_resume() {
+ suspended = continue_parsing(p, fd);
+ }
+ }
+}
+</pre>
+
+<p>An application that may resume any of several parsers based on
+input (either from the XML being parsed or some other source) will
+certainly have more interesting control structures.</p>
+
+<p>This C function could be used for the <code>parse_xml</code>
+function mentioned in the pseudo-code above:</p>
+
+<pre class="eg">
+#define BUFF_SIZE 10240
+
+/* Parse a document from the open file descriptor 'fd' until the parse
+ is complete (the document has been completely parsed, or there's
+ been an error), or the parse is stopped. Return non-zero when
+ the parse is merely suspended.
+*/
+int
+parse_xml(XML_Parser p, int fd)
+{
+ for (;;) {
+ int last_chunk;
+ int bytes_read;
+ enum XML_Status status;
+
+ void *buff = XML_GetBuffer(p, BUFF_SIZE);
+ if (buff == NULL) {
+ /* handle error... */
+ return 0;
+ }
+ bytes_read = read(fd, buff, BUFF_SIZE);
+ if (bytes_read &lt; 0) {
+ /* handle error... */
+ return 0;
+ }
+ status = XML_ParseBuffer(p, bytes_read, bytes_read == 0);
+ switch (status) {
+ case XML_STATUS_ERROR:
+ /* handle error... */
+ return 0;
+ case XML_STATUS_SUSPENDED:
+ return 1;
+ }
+ if (bytes_read == 0)
+ return 0;
+ }
+}
+</pre>
+
+<p>The corresponding <code>continue_parsing</code> function is
+somewhat simpler, since it only need deal with the return code from
+<code><a href= "#XML_ResumeParser">XML_ResumeParser</a></code>; it can
+delegate the input handling to the <code>parse_xml</code>
+function:</p>
+
+<pre class="eg">
+/* Continue parsing a document which had been suspended. The 'p' and
+ 'fd' arguments are the same as passed to parse_xml(). Return
+ non-zero when the parse is suspended.
+*/
+int
+continue_parsing(XML_Parser p, int fd)
+{
+ enum XML_Status status = XML_ResumeParser(p);
+ switch (status) {
+ case XML_STATUS_ERROR:
+ /* handle error... */
+ return 0;
+ case XML_ERROR_NOT_SUSPENDED:
+ /* handle error... */
+ return 0;.
+ case XML_STATUS_SUSPENDED:
+ return 1;
+ }
+ return parse_xml(p, fd);
+}
+</pre>
+
+<p>Now that we've seen what a mess the top-level parsing loop can
+become, what have we gained? Very simply, we can now use the <code><a
+href= "#XML_StopParser" >XML_StopParser</a></code> function to stop
+parsing, without having to go to great lengths to avoid additional
+processing that we're expecting to ignore. As a bonus, we get to stop
+parsing <em>temporarily</em>, and come back to it when we're
+ready.</p>
+
+<p>To stop parsing from a handler function, use the <code><a href=
+"#XML_StopParser" >XML_StopParser</a></code> function. This function
+takes two arguments; the parser being stopped and a flag indicating
+whether the parse can be resumed in the future.</p>
+
+<!-- XXX really need more here -->
+
+
+<hr />
+<!-- ================================================================ -->
+
+<h2><a name="reference">Expat Reference</a></h2>
+
+<h3><a name="creation">Parser Creation</a></h3>
+
+<pre class="fcndec" id="XML_ParserCreate">
+XML_Parser XMLCALL
+XML_ParserCreate(const XML_Char *encoding);
+</pre>
+<div class="fcndef">
+Construct a new parser. If encoding is non-null, it specifies a
+character encoding to use for the document. This overrides the document
+encoding declaration. There are four built-in encodings:
+<ul>
+<li>US-ASCII</li>
+<li>UTF-8</li>
+<li>UTF-16</li>
+<li>ISO-8859-1</li>
+</ul>
+Any other value will invoke a call to the UnknownEncodingHandler.
+</div>
+
+<pre class="fcndec" id="XML_ParserCreateNS">
+XML_Parser XMLCALL
+XML_ParserCreateNS(const XML_Char *encoding,
+ XML_Char sep);
+</pre>
+<div class="fcndef">
+Constructs a new parser that has namespace processing in effect. Namespace
+expanded element names and attribute names are returned as a concatenation
+of the namespace URI, <em>sep</em>, and the local part of the name. This
+means that you should pick a character for <em>sep</em> that can't be
+part of a legal URI. There is a special case when <em>sep</em> is the null
+character <code>'\0'</code>: the namespace URI and the local part will be
+concatenated without any separator - this is intended to support RDF processors.
+It is a programming error to use the null separator with
+<a href= "#XML_SetReturnNSTriplet">namespace triplets</a>.</div>
+
+<pre class="fcndec" id="XML_ParserCreate_MM">
+XML_Parser XMLCALL
+XML_ParserCreate_MM(const XML_Char *encoding,
+ const XML_Memory_Handling_Suite *ms,
+ const XML_Char *sep);
+</pre>
+<pre class="signature">
+typedef struct {
+ void *(XMLCALL *malloc_fcn)(size_t size);
+ void *(XMLCALL *realloc_fcn)(void *ptr, size_t size);
+ void (XMLCALL *free_fcn)(void *ptr);
+} XML_Memory_Handling_Suite;
+</pre>
+<div class="fcndef">
+<p>Construct a new parser using the suite of memory handling functions
+specified in <code>ms</code>. If <code>ms</code> is NULL, then use the
+standard set of memory management functions. If <code>sep</code> is
+non NULL, then namespace processing is enabled in the created parser
+and the character pointed at by sep is used as the separator between
+the namespace URI and the local part of the name.</p>
+</div>
+
+<pre class="fcndec" id="XML_ExternalEntityParserCreate">
+XML_Parser XMLCALL
+XML_ExternalEntityParserCreate(XML_Parser p,
+ const XML_Char *context,
+ const XML_Char *encoding);
+</pre>
+<div class="fcndef">
+Construct a new <code>XML_Parser</code> object for parsing an external
+general entity. Context is the context argument passed in a call to a
+ExternalEntityRefHandler. Other state information such as handlers,
+user data, namespace processing is inherited from the parser passed as
+the 1st argument. So you shouldn't need to call any of the behavior
+changing functions on this parser (unless you want it to act
+differently than the parent parser).
+</div>
+
+<pre class="fcndec" id="XML_ParserFree">
+void XMLCALL
+XML_ParserFree(XML_Parser p);
+</pre>
+<div class="fcndef">
+Free memory used by the parser. Your application is responsible for
+freeing any memory associated with <a href="#userdata">user data</a>.
+</div>
+
+<pre class="fcndec" id="XML_ParserReset">
+XML_Bool XMLCALL
+XML_ParserReset(XML_Parser p,
+ const XML_Char *encoding);
+</pre>
+<div class="fcndef">
+Clean up the memory structures maintained by the parser so that it may
+be used again. After this has been called, <code>parser</code> is
+ready to start parsing a new document. All handlers are cleared from
+the parser, except for the unknownEncodingHandler. The parser's external
+state is re-initialized except for the values of ns and ns_triplets.
+This function may not be used on a parser created using <code><a href=
+"#XML_ExternalEntityParserCreate" >XML_ExternalEntityParserCreate</a
+></code>; it will return <code>XML_FALSE</code> in that case. Returns
+<code>XML_TRUE</code> on success. Your application is responsible for
+dealing with any memory associated with <a href="#userdata">user data</a>.
+</div>
+
+<h3><a name="parsing">Parsing</a></h3>
+
+<p>To state the obvious: the three parsing functions <code><a href=
+"#XML_Parse" >XML_Parse</a></code>, <code><a href= "#XML_ParseBuffer">
+XML_ParseBuffer</a></code> and <code><a href= "#XML_GetBuffer">
+XML_GetBuffer</a></code> must not be called from within a handler
+unless they operate on a separate parser instance, that is, one that
+did not call the handler. For example, it is OK to call the parsing
+functions from within an <code>XML_ExternalEntityRefHandler</code>,
+if they apply to the parser created by
+<code><a href= "#XML_ExternalEntityParserCreate"
+>XML_ExternalEntityParserCreate</a></code>.</p>
+
+<p>Note: the <code>len</code> argument passed to these functions
+should be considerably less than the maximum value for an integer,
+as it could create an integer overflow situation if the added
+lengths of a buffer and the unprocessed portion of the previous buffer
+exceed the maximum integer value. Input data at the end of a buffer
+will remain unprocessed if it is part of an XML token for which the
+end is not part of that buffer.</p>
+
+<pre class="fcndec" id="XML_Parse">
+enum XML_Status XMLCALL
+XML_Parse(XML_Parser p,
+ const char *s,
+ int len,
+ int isFinal);
+</pre>
+<pre class="signature">
+enum XML_Status {
+ XML_STATUS_ERROR = 0,
+ XML_STATUS_OK = 1
+};
+</pre>
+<div class="fcndef">
+Parse some more of the document. The string <code>s</code> is a buffer
+containing part (or perhaps all) of the document. The number of bytes of s
+that are part of the document is indicated by <code>len</code>. This means
+that <code>s</code> doesn't have to be null terminated. It also means that
+if <code>len</code> is larger than the number of bytes in the block of
+memory that <code>s</code> points at, then a memory fault is likely. The
+<code>isFinal</code> parameter informs the parser that this is the last
+piece of the document. Frequently, the last piece is empty (i.e.
+<code>len</code> is zero.)
+If a parse error occurred, it returns <code>XML_STATUS_ERROR</code>.
+Otherwise it returns <code>XML_STATUS_OK</code> value.
+</div>
+
+<pre class="fcndec" id="XML_ParseBuffer">
+enum XML_Status XMLCALL
+XML_ParseBuffer(XML_Parser p,
+ int len,
+ int isFinal);
+</pre>
+<div class="fcndef">
+This is just like <code><a href= "#XML_Parse" >XML_Parse</a></code>,
+except in this case Expat provides the buffer. By obtaining the
+buffer from Expat with the <code><a href= "#XML_GetBuffer"
+>XML_GetBuffer</a></code> function, the application can avoid double
+copying of the input.
+</div>
+
+<pre class="fcndec" id="XML_GetBuffer">
+void * XMLCALL
+XML_GetBuffer(XML_Parser p,
+ int len);
+</pre>
+<div class="fcndef">
+Obtain a buffer of size <code>len</code> to read a piece of the document
+into. A NULL value is returned if Expat can't allocate enough memory for
+this buffer. This has to be called prior to every call to
+<code><a href= "#XML_ParseBuffer" >XML_ParseBuffer</a></code>. A
+typical use would look like this:
+
+<pre class="eg">
+for (;;) {
+ int bytes_read;
+ void *buff = XML_GetBuffer(p, BUFF_SIZE);
+ if (buff == NULL) {
+ /* handle error */
+ }
+
+ bytes_read = read(docfd, buff, BUFF_SIZE);
+ if (bytes_read &lt; 0) {
+ /* handle error */
+ }
+
+ if (! XML_ParseBuffer(p, bytes_read, bytes_read == 0)) {
+ /* handle parse error */
+ }
+
+ if (bytes_read == 0)
+ break;
+}
+</pre>
+</div>
+
+<pre class="fcndec" id="XML_StopParser">
+enum XML_Status XMLCALL
+XML_StopParser(XML_Parser p,
+ XML_Bool resumable);
+</pre>
+<div class="fcndef">
+
+<p>Stops parsing, causing <code><a href= "#XML_Parse"
+>XML_Parse</a></code> or <code><a href= "#XML_ParseBuffer"
+>XML_ParseBuffer</a></code> to return. Must be called from within a
+call-back handler, except when aborting (when <code>resumable</code>
+is <code>XML_FALSE</code>) an already suspended parser. Some
+call-backs may still follow because they would otherwise get
+lost, including
+<ul>
+ <li> the end element handler for empty elements when stopped in the
+ start element handler,</li>
+ <li> the end namespace declaration handler when stopped in the end
+ element handler,</li>
+ <li> the character data handler when stopped in the character data handler
+ while making multiple call-backs on a contiguous chunk of characters,</li>
+</ul>
+and possibly others.</p>
+
+<p>This can be called from most handlers, including DTD related
+call-backs, except when parsing an external parameter entity and
+<code>resumable</code> is <code>XML_TRUE</code>. Returns
+<code>XML_STATUS_OK</code> when successful,
+<code>XML_STATUS_ERROR</code> otherwise. The possible error codes
+are:</p>
+<dl>
+ <dt><code>XML_ERROR_SUSPENDED</code></dt>
+ <dd>when suspending an already suspended parser.</dd>
+ <dt><code>XML_ERROR_FINISHED</code></dt>
+ <dd>when the parser has already finished.</dd>
+ <dt><code>XML_ERROR_SUSPEND_PE</code></dt>
+ <dd>when suspending while parsing an external PE.</dd>
+</dl>
+
+<p>Since the stop/resume feature requires application support in the
+outer parsing loop, it is an error to call this function for a parser
+not being handled appropriately; see <a href= "#stop-resume"
+>Temporarily Stopping Parsing</a> for more information.</p>
+
+<p>When <code>resumable</code> is <code>XML_TRUE</code> then parsing
+is <em>suspended</em>, that is, <code><a href= "#XML_Parse"
+>XML_Parse</a></code> and <code><a href= "#XML_ParseBuffer"
+>XML_ParseBuffer</a></code> return <code>XML_STATUS_SUSPENDED</code>.
+Otherwise, parsing is <em>aborted</em>, that is, <code><a href=
+"#XML_Parse" >XML_Parse</a></code> and <code><a href=
+"#XML_ParseBuffer" >XML_ParseBuffer</a></code> return
+<code>XML_STATUS_ERROR</code> with error code
+<code>XML_ERROR_ABORTED</code>.</p>
+
+<p><strong>Note:</strong>
+This will be applied to the current parser instance only, that is, if
+there is a parent parser then it will continue parsing when the
+external entity reference handler returns. It is up to the
+implementation of that handler to call <code><a href=
+"#XML_StopParser" >XML_StopParser</a></code> on the parent parser
+(recursively), if one wants to stop parsing altogether.</p>
+
+<p>When suspended, parsing can be resumed by calling <code><a href=
+"#XML_ResumeParser" >XML_ResumeParser</a></code>.</p>
+
+<p>New in Expat 1.95.8.</p>
+</div>
+
+<pre class="fcndec" id="XML_ResumeParser">
+enum XML_Status XMLCALL
+XML_ResumeParser(XML_Parser p);
+</pre>
+<div class="fcndef">
+<p>Resumes parsing after it has been suspended with <code><a href=
+"#XML_StopParser" >XML_StopParser</a></code>. Must not be called from
+within a handler call-back. Returns same status codes as <code><a
+href= "#XML_Parse">XML_Parse</a></code> or <code><a href=
+"#XML_ParseBuffer" >XML_ParseBuffer</a></code>. An additional error
+code, <code>XML_ERROR_NOT_SUSPENDED</code>, will be returned if the
+parser was not currently suspended.</p>
+
+<p><strong>Note:</strong>
+This must be called on the most deeply nested child parser instance
+first, and on its parent parser only after the child parser has
+finished, to be applied recursively until the document entity's parser
+is restarted. That is, the parent parser will not resume by itself
+and it is up to the application to call <code><a href=
+"#XML_ResumeParser" >XML_ResumeParser</a></code> on it at the
+appropriate moment.</p>
+
+<p>New in Expat 1.95.8.</p>
+</div>
+
+<pre class="fcndec" id="XML_GetParsingStatus">
+void XMLCALL
+XML_GetParsingStatus(XML_Parser p,
+ XML_ParsingStatus *status);
+</pre>
+<pre class="signature">
+enum XML_Parsing {
+ XML_INITIALIZED,
+ XML_PARSING,
+ XML_FINISHED,
+ XML_SUSPENDED
+};
+
+typedef struct {
+ enum XML_Parsing parsing;
+ XML_Bool finalBuffer;
+} XML_ParsingStatus;
+</pre>
+<div class="fcndef">
+<p>Returns status of parser with respect to being initialized,
+parsing, finished, or suspended, and whether the final buffer is being
+processed. The <code>status</code> parameter <em>must not</em> be
+NULL.</p>
+
+<p>New in Expat 1.95.8.</p>
+</div>
+
+
+<h3><a name="setting">Handler Setting</a></h3>
+
+<p>Although handlers are typically set prior to parsing and left alone, an
+application may choose to set or change the handler for a parsing event
+while the parse is in progress. For instance, your application may choose
+to ignore all text not descended from a <code>para</code> element. One
+way it could do this is to set the character handler when a para start tag
+is seen, and unset it for the corresponding end tag.</p>
+
+<p>A handler may be <em>unset</em> by providing a NULL pointer to the
+appropriate handler setter. None of the handler setting functions have
+a return value.</p>
+
+<p>Your handlers will be receiving strings in arrays of type
+<code>XML_Char</code>. This type is conditionally defined in expat.h as
+either <code>char</code>, <code>wchar_t</code> or <code>unsigned short</code>.
+The former implies UTF-8 encoding, the latter two imply UTF-16 encoding.
+Note that you'll receive them in this form independent of the original
+encoding of the document.</p>
+
+<div class="handler">
+<pre class="setter" id="XML_SetStartElementHandler">
+void XMLCALL
+XML_SetStartElementHandler(XML_Parser p,
+ XML_StartElementHandler start);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_StartElementHandler)(void *userData,
+ const XML_Char *name,
+ const XML_Char **atts);
+</pre>
+<p>Set handler for start (and empty) tags. Attributes are passed to the start
+handler as a pointer to a vector of char pointers. Each attribute seen in
+a start (or empty) tag occupies 2 consecutive places in this vector: the
+attribute name followed by the attribute value. These pairs are terminated
+by a null pointer.</p>
+<p>Note that an empty tag generates a call to both start and end handlers
+(in that order).</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetEndElementHandler">
+void XMLCALL
+XML_SetEndElementHandler(XML_Parser p,
+ XML_EndElementHandler);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_EndElementHandler)(void *userData,
+ const XML_Char *name);
+</pre>
+<p>Set handler for end (and empty) tags. As noted above, an empty tag
+generates a call to both start and end handlers.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetElementHandler">
+void XMLCALL
+XML_SetElementHandler(XML_Parser p,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end);
+</pre>
+<p>Set handlers for start and end tags with one call.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetCharacterDataHandler">
+void XMLCALL
+XML_SetCharacterDataHandler(XML_Parser p,
+ XML_CharacterDataHandler charhndl)
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_CharacterDataHandler)(void *userData,
+ const XML_Char *s,
+ int len);
+</pre>
+<p>Set a text handler. The string your handler receives
+is <em>NOT nul-terminated</em>. You have to use the length argument
+to deal with the end of the string. A single block of contiguous text
+free of markup may still result in a sequence of calls to this handler.
+In other words, if you're searching for a pattern in the text, it may
+be split across calls to this handler. Note: Setting this handler to NULL
+may <em>NOT immediately</em> terminate call-backs if the parser is currently
+processing such a single block of contiguous markup-free text, as the parser
+will continue calling back until the end of the block is reached.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetProcessingInstructionHandler">
+void XMLCALL
+XML_SetProcessingInstructionHandler(XML_Parser p,
+ XML_ProcessingInstructionHandler proc)
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_ProcessingInstructionHandler)(void *userData,
+ const XML_Char *target,
+ const XML_Char *data);
+
+</pre>
+<p>Set a handler for processing instructions. The target is the first word
+in the processing instruction. The data is the rest of the characters in
+it after skipping all whitespace after the initial word.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetCommentHandler">
+void XMLCALL
+XML_SetCommentHandler(XML_Parser p,
+ XML_CommentHandler cmnt)
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_CommentHandler)(void *userData,
+ const XML_Char *data);
+</pre>
+<p>Set a handler for comments. The data is all text inside the comment
+delimiters.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetStartCdataSectionHandler">
+void XMLCALL
+XML_SetStartCdataSectionHandler(XML_Parser p,
+ XML_StartCdataSectionHandler start);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_StartCdataSectionHandler)(void *userData);
+</pre>
+<p>Set a handler that gets called at the beginning of a CDATA section.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetEndCdataSectionHandler">
+void XMLCALL
+XML_SetEndCdataSectionHandler(XML_Parser p,
+ XML_EndCdataSectionHandler end);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_EndCdataSectionHandler)(void *userData);
+</pre>
+<p>Set a handler that gets called at the end of a CDATA section.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetCdataSectionHandler">
+void XMLCALL
+XML_SetCdataSectionHandler(XML_Parser p,
+ XML_StartCdataSectionHandler start,
+ XML_EndCdataSectionHandler end)
+</pre>
+<p>Sets both CDATA section handlers with one call.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetDefaultHandler">
+void XMLCALL
+XML_SetDefaultHandler(XML_Parser p,
+ XML_DefaultHandler hndl)
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_DefaultHandler)(void *userData,
+ const XML_Char *s,
+ int len);
+</pre>
+
+<p>Sets a handler for any characters in the document which wouldn't
+otherwise be handled. This includes both data for which no handlers
+can be set (like some kinds of DTD declarations) and data which could
+be reported but which currently has no handler set. The characters
+are passed exactly as they were present in the XML document except
+that they will be encoded in UTF-8 or UTF-16. Line boundaries are not
+normalized. Note that a byte order mark character is not passed to the
+default handler. There are no guarantees about how characters are
+divided between calls to the default handler: for example, a comment
+might be split between multiple calls. Setting the handler with
+this call has the side effect of turning off expansion of references
+to internally defined general entities. Instead these references are
+passed to the default handler.</p>
+
+<p>See also <code><a
+href="#XML_DefaultCurrent">XML_DefaultCurrent</a></code>.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetDefaultHandlerExpand">
+void XMLCALL
+XML_SetDefaultHandlerExpand(XML_Parser p,
+ XML_DefaultHandler hndl)
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_DefaultHandler)(void *userData,
+ const XML_Char *s,
+ int len);
+</pre>
+<p>This sets a default handler, but doesn't inhibit the expansion of
+internal entity references. The entity reference will not be passed
+to the default handler.</p>
+
+<p>See also <code><a
+href="#XML_DefaultCurrent">XML_DefaultCurrent</a></code>.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetExternalEntityRefHandler">
+void XMLCALL
+XML_SetExternalEntityRefHandler(XML_Parser p,
+ XML_ExternalEntityRefHandler hndl)
+</pre>
+<pre class="signature">
+typedef int
+(XMLCALL *XML_ExternalEntityRefHandler)(XML_Parser p,
+ const XML_Char *context,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+</pre>
+<p>Set an external entity reference handler. This handler is also
+called for processing an external DTD subset if parameter entity parsing
+is in effect. (See <a href="#XML_SetParamEntityParsing">
+<code>XML_SetParamEntityParsing</code></a>.)</p>
+
+<p>The <code>context</code> parameter specifies the parsing context in
+the format expected by the <code>context</code> argument to <code><a
+href="#XML_ExternalEntityParserCreate"
+>XML_ExternalEntityParserCreate</a></code>. <code>code</code> is
+valid only until the handler returns, so if the referenced entity is
+to be parsed later, it must be copied. <code>context</code> is NULL
+only when the entity is a parameter entity, which is how one can
+differentiate between general and parameter entities.</p>
+
+<p>The <code>base</code> parameter is the base to use for relative
+system identifiers. It is set by <code><a
+href="#XML_SetBase">XML_SetBase</a></code> and may be NULL. The
+<code>publicId</code> parameter is the public id given in the entity
+declaration and may be NULL. <code>systemId</code> is the system
+identifier specified in the entity declaration and is never NULL.</p>
+
+<p>There are a couple of ways in which this handler differs from
+others. First, this handler returns a status indicator (an
+integer). <code>XML_STATUS_OK</code> should be returned for successful
+handling of the external entity reference. Returning
+<code>XML_STATUS_ERROR</code> indicates failure, and causes the
+calling parser to return an
+<code>XML_ERROR_EXTERNAL_ENTITY_HANDLING</code> error.</p>
+
+<p>Second, instead of having the user data as its first argument, it
+receives the parser that encountered the entity reference. This, along
+with the context parameter, may be used as arguments to a call to
+<code><a href= "#XML_ExternalEntityParserCreate"
+>XML_ExternalEntityParserCreate</a></code>. Using the returned
+parser, the body of the external entity can be recursively parsed.</p>
+
+<p>Since this handler may be called recursively, it should not be saving
+information into global or static variables.</p>
+</div>
+
+<pre class="fcndec" id="XML_SetExternalEntityRefHandlerArg">
+void XMLCALL
+XML_SetExternalEntityRefHandlerArg(XML_Parser p,
+ void *arg)
+</pre>
+<div class="fcndef">
+<p>Set the argument passed to the ExternalEntityRefHandler. If
+<code>arg</code> is not NULL, it is the new value passed to the
+handler set using <code><a href="#XML_SetExternalEntityRefHandler"
+>XML_SetExternalEntityRefHandler</a></code>; if <code>arg</code> is
+NULL, the argument passed to the handler function will be the parser
+object itself.</p>
+
+<p><strong>Note:</strong>
+The type of <code>arg</code> and the type of the first argument to the
+ExternalEntityRefHandler do not match. This function takes a
+<code>void *</code> to be passed to the handler, while the handler
+accepts an <code>XML_Parser</code>. This is a historical accident,
+but will not be corrected before Expat 2.0 (at the earliest) to avoid
+causing compiler warnings for code that's known to work with this
+API. It is the responsibility of the application code to know the
+actual type of the argument passed to the handler and to manage it
+properly.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetSkippedEntityHandler">
+void XMLCALL
+XML_SetSkippedEntityHandler(XML_Parser p,
+ XML_SkippedEntityHandler handler)
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_SkippedEntityHandler)(void *userData,
+ const XML_Char *entityName,
+ int is_parameter_entity);
+</pre>
+<p>Set a skipped entity handler. This is called in two situations:</p>
+<ol>
+ <li>An entity reference is encountered for which no declaration
+ has been read <em>and</em> this is not an error.</li>
+ <li>An internal entity reference is read, but not expanded, because
+ <a href="#XML_SetDefaultHandler"><code>XML_SetDefaultHandler</code></a>
+ has been called.</li>
+</ol>
+<p>The <code>is_parameter_entity</code> argument will be non-zero for
+a parameter entity and zero for a general entity.</p> <p>Note: skipped
+parameter entities in declarations and skipped general entities in
+attribute values cannot be reported, because the event would be out of
+sync with the reporting of the declarations or attribute values</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetUnknownEncodingHandler">
+void XMLCALL
+XML_SetUnknownEncodingHandler(XML_Parser p,
+ XML_UnknownEncodingHandler enchandler,
+ void *encodingHandlerData)
+</pre>
+<pre class="signature">
+typedef int
+(XMLCALL *XML_UnknownEncodingHandler)(void *encodingHandlerData,
+ const XML_Char *name,
+ XML_Encoding *info);
+
+typedef struct {
+ int map[256];
+ void *data;
+ int (XMLCALL *convert)(void *data, const char *s);
+ void (XMLCALL *release)(void *data);
+} XML_Encoding;
+</pre>
+<p>Set a handler to deal with encodings other than the <a
+href="#builtin_encodings">built in set</a>. This should be done before
+<code><a href= "#XML_Parse" >XML_Parse</a></code> or <code><a href=
+"#XML_ParseBuffer" >XML_ParseBuffer</a></code> have been called on the
+given parser.</p> <p>If the handler knows how to deal with an encoding
+with the given name, it should fill in the <code>info</code> data
+structure and return <code>XML_STATUS_OK</code>. Otherwise it
+should return <code>XML_STATUS_ERROR</code>. The handler will be called
+at most once per parsed (external) entity. The optional application
+data pointer <code>encodingHandlerData</code> will be passed back to
+the handler.</p>
+
+<p>The map array contains information for every possible possible leading
+byte in a byte sequence. If the corresponding value is &gt;= 0, then it's
+a single byte sequence and the byte encodes that Unicode value. If the
+value is -1, then that byte is invalid as the initial byte in a sequence.
+If the value is -n, where n is an integer &gt; 1, then n is the number of
+bytes in the sequence and the actual conversion is accomplished by a
+call to the function pointed at by convert. This function may return -1
+if the sequence itself is invalid. The convert pointer may be null if
+there are only single byte codes. The data parameter passed to the convert
+function is the data pointer from <code>XML_Encoding</code>. The
+string s is <em>NOT</em> nul-terminated and points at the sequence of
+bytes to be converted.</p>
+
+<p>The function pointed at by <code>release</code> is called by the
+parser when it is finished with the encoding. It may be NULL.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetStartNamespaceDeclHandler">
+void XMLCALL
+XML_SetStartNamespaceDeclHandler(XML_Parser p,
+ XML_StartNamespaceDeclHandler start);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_StartNamespaceDeclHandler)(void *userData,
+ const XML_Char *prefix,
+ const XML_Char *uri);
+</pre>
+<p>Set a handler to be called when a namespace is declared. Namespace
+declarations occur inside start tags. But the namespace declaration start
+handler is called before the start tag handler for each namespace declared
+in that start tag.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetEndNamespaceDeclHandler">
+void XMLCALL
+XML_SetEndNamespaceDeclHandler(XML_Parser p,
+ XML_EndNamespaceDeclHandler end);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_EndNamespaceDeclHandler)(void *userData,
+ const XML_Char *prefix);
+</pre>
+<p>Set a handler to be called when leaving the scope of a namespace
+declaration. This will be called, for each namespace declaration,
+after the handler for the end tag of the element in which the
+namespace was declared.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetNamespaceDeclHandler">
+void XMLCALL
+XML_SetNamespaceDeclHandler(XML_Parser p,
+ XML_StartNamespaceDeclHandler start,
+ XML_EndNamespaceDeclHandler end)
+</pre>
+<p>Sets both namespace declaration handlers with a single call.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetXmlDeclHandler">
+void XMLCALL
+XML_SetXmlDeclHandler(XML_Parser p,
+ XML_XmlDeclHandler xmldecl);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_XmlDeclHandler)(void *userData,
+ const XML_Char *version,
+ const XML_Char *encoding,
+ int standalone);
+</pre>
+<p>Sets a handler that is called for XML declarations and also for
+text declarations discovered in external entities. The way to
+distinguish is that the <code>version</code> parameter will be NULL
+for text declarations. The <code>encoding</code> parameter may be NULL
+for an XML declaration. The <code>standalone</code> argument will
+contain -1, 0, or 1 indicating respectively that there was no
+standalone parameter in the declaration, that it was given as no, or
+that it was given as yes.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetStartDoctypeDeclHandler">
+void XMLCALL
+XML_SetStartDoctypeDeclHandler(XML_Parser p,
+ XML_StartDoctypeDeclHandler start);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_StartDoctypeDeclHandler)(void *userData,
+ const XML_Char *doctypeName,
+ const XML_Char *sysid,
+ const XML_Char *pubid,
+ int has_internal_subset);
+</pre>
+<p>Set a handler that is called at the start of a DOCTYPE declaration,
+before any external or internal subset is parsed. Both <code>sysid</code>
+and <code>pubid</code> may be NULL. The <code>has_internal_subset</code>
+will be non-zero if the DOCTYPE declaration has an internal subset.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetEndDoctypeDeclHandler">
+void XMLCALL
+XML_SetEndDoctypeDeclHandler(XML_Parser p,
+ XML_EndDoctypeDeclHandler end);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_EndDoctypeDeclHandler)(void *userData);
+</pre>
+<p>Set a handler that is called at the end of a DOCTYPE declaration,
+after parsing any external subset.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetDoctypeDeclHandler">
+void XMLCALL
+XML_SetDoctypeDeclHandler(XML_Parser p,
+ XML_StartDoctypeDeclHandler start,
+ XML_EndDoctypeDeclHandler end);
+</pre>
+<p>Set both doctype handlers with one call.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetElementDeclHandler">
+void XMLCALL
+XML_SetElementDeclHandler(XML_Parser p,
+ XML_ElementDeclHandler eldecl);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_ElementDeclHandler)(void *userData,
+ const XML_Char *name,
+ XML_Content *model);
+</pre>
+<pre class="signature">
+enum XML_Content_Type {
+ XML_CTYPE_EMPTY = 1,
+ XML_CTYPE_ANY,
+ XML_CTYPE_MIXED,
+ XML_CTYPE_NAME,
+ XML_CTYPE_CHOICE,
+ XML_CTYPE_SEQ
+};
+
+enum XML_Content_Quant {
+ XML_CQUANT_NONE,
+ XML_CQUANT_OPT,
+ XML_CQUANT_REP,
+ XML_CQUANT_PLUS
+};
+
+typedef struct XML_cp XML_Content;
+
+struct XML_cp {
+ enum XML_Content_Type type;
+ enum XML_Content_Quant quant;
+ const XML_Char * name;
+ unsigned int numchildren;
+ XML_Content * children;
+};
+</pre>
+<p>Sets a handler for element declarations in a DTD. The handler gets
+called with the name of the element in the declaration and a pointer
+to a structure that contains the element model. It is the
+application's responsibility to free this data structure using
+<code><a href="#XML_FreeContentModel"
+>XML_FreeContentModel</a></code>.</p>
+
+<p>The <code>model</code> argument is the root of a tree of
+<code>XML_Content</code> nodes. If <code>type</code> equals
+<code>XML_CTYPE_EMPTY</code> or <code>XML_CTYPE_ANY</code>, then
+<code>quant</code> will be <code>XML_CQUANT_NONE</code>, and the other
+fields will be zero or NULL. If <code>type</code> is
+<code>XML_CTYPE_MIXED</code>, then <code>quant</code> will be
+<code>XML_CQUANT_NONE</code> or <code>XML_CQUANT_REP</code> and
+<code>numchildren</code> will contain the number of elements that are
+allowed to be mixed in and <code>children</code> points to an array of
+<code>XML_Content</code> structures that will all have type
+XML_CTYPE_NAME with no quantification. Only the root node can be type
+<code>XML_CTYPE_EMPTY</code>, <code>XML_CTYPE_ANY</code>, or
+<code>XML_CTYPE_MIXED</code>.</p>
+
+<p>For type <code>XML_CTYPE_NAME</code>, the <code>name</code> field
+points to the name and the <code>numchildren</code> and
+<code>children</code> fields will be zero and NULL. The
+<code>quant</code> field will indicate any quantifiers placed on the
+name.</p>
+
+<p>Types <code>XML_CTYPE_CHOICE</code> and <code>XML_CTYPE_SEQ</code>
+indicate a choice or sequence respectively. The
+<code>numchildren</code> field indicates how many nodes in the choice
+or sequence and <code>children</code> points to the nodes.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetAttlistDeclHandler">
+void XMLCALL
+XML_SetAttlistDeclHandler(XML_Parser p,
+ XML_AttlistDeclHandler attdecl);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_AttlistDeclHandler)(void *userData,
+ const XML_Char *elname,
+ const XML_Char *attname,
+ const XML_Char *att_type,
+ const XML_Char *dflt,
+ int isrequired);
+</pre>
+<p>Set a handler for attlist declarations in the DTD. This handler is
+called for <em>each</em> attribute. So a single attlist declaration
+with multiple attributes declared will generate multiple calls to this
+handler. The <code>elname</code> parameter returns the name of the
+element for which the attribute is being declared. The attribute name
+is in the <code>attname</code> parameter. The attribute type is in the
+<code>att_type</code> parameter. It is the string representing the
+type in the declaration with whitespace removed.</p>
+
+<p>The <code>dflt</code> parameter holds the default value. It will be
+NULL in the case of "#IMPLIED" or "#REQUIRED" attributes. You can
+distinguish these two cases by checking the <code>isrequired</code>
+parameter, which will be true in the case of "#REQUIRED" attributes.
+Attributes which are "#FIXED" will have also have a true
+<code>isrequired</code>, but they will have the non-NULL fixed value
+in the <code>dflt</code> parameter.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetEntityDeclHandler">
+void XMLCALL
+XML_SetEntityDeclHandler(XML_Parser p,
+ XML_EntityDeclHandler handler);
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_EntityDeclHandler)(void *userData,
+ const XML_Char *entityName,
+ int is_parameter_entity,
+ const XML_Char *value,
+ int value_length,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName);
+</pre>
+<p>Sets a handler that will be called for all entity declarations.
+The <code>is_parameter_entity</code> argument will be non-zero in the
+case of parameter entities and zero otherwise.</p>
+
+<p>For internal entities (<code>&lt;!ENTITY foo "bar"&gt;</code>),
+<code>value</code> will be non-NULL and <code>systemId</code>,
+<code>publicId</code>, and <code>notationName</code> will all be NULL.
+The value string is <em>not</em> NULL terminated; the length is
+provided in the <code>value_length</code> parameter. Do not use
+<code>value_length</code> to test for internal entities, since it is
+legal to have zero-length values. Instead check for whether or not
+<code>value</code> is NULL.</p> <p>The <code>notationName</code>
+argument will have a non-NULL value only for unparsed entity
+declarations.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetUnparsedEntityDeclHandler">
+void XMLCALL
+XML_SetUnparsedEntityDeclHandler(XML_Parser p,
+ XML_UnparsedEntityDeclHandler h)
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_UnparsedEntityDeclHandler)(void *userData,
+ const XML_Char *entityName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName);
+</pre>
+<p>Set a handler that receives declarations of unparsed entities. These
+are entity declarations that have a notation (NDATA) field:</p>
+
+<div id="eg"><pre>
+&lt;!ENTITY logo SYSTEM "images/logo.gif" NDATA gif&gt;
+</pre></div>
+<p>This handler is obsolete and is provided for backwards
+compatibility. Use instead <a href= "#XML_SetEntityDeclHandler"
+>XML_SetEntityDeclHandler</a>.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetNotationDeclHandler">
+void XMLCALL
+XML_SetNotationDeclHandler(XML_Parser p,
+ XML_NotationDeclHandler h)
+</pre>
+<pre class="signature">
+typedef void
+(XMLCALL *XML_NotationDeclHandler)(void *userData,
+ const XML_Char *notationName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+</pre>
+<p>Set a handler that receives notation declarations.</p>
+</div>
+
+<div class="handler">
+<pre class="setter" id="XML_SetNotStandaloneHandler">
+void XMLCALL
+XML_SetNotStandaloneHandler(XML_Parser p,
+ XML_NotStandaloneHandler h)
+</pre>
+<pre class="signature">
+typedef int
+(XMLCALL *XML_NotStandaloneHandler)(void *userData);
+</pre>
+<p>Set a handler that is called if the document is not "standalone".
+This happens when there is an external subset or a reference to a
+parameter entity, but does not have standalone set to "yes" in an XML
+declaration. If this handler returns <code>XML_STATUS_ERROR</code>,
+then the parser will throw an <code>XML_ERROR_NOT_STANDALONE</code>
+error.</p>
+</div>
+
+<h3><a name="position">Parse position and error reporting functions</a></h3>
+
+<p>These are the functions you'll want to call when the parse
+functions return <code>XML_STATUS_ERROR</code> (a parse error has
+occurred), although the position reporting functions are useful outside
+of errors. The position reported is the byte position (in the original
+document or entity encoding) of the first of the sequence of
+characters that generated the current event (or the error that caused
+the parse functions to return <code>XML_STATUS_ERROR</code>.) The
+exceptions are callbacks trigged by declarations in the document
+prologue, in which case they exact position reported is somewhere in the
+relevant markup, but not necessarily as meaningful as for other
+events.</p>
+
+<p>The position reporting functions are accurate only outside of the
+DTD. In other words, they usually return bogus information when
+called from within a DTD declaration handler.</p>
+
+<pre class="fcndec" id="XML_GetErrorCode">
+enum XML_Error XMLCALL
+XML_GetErrorCode(XML_Parser p);
+</pre>
+<div class="fcndef">
+Return what type of error has occurred.
+</div>
+
+<pre class="fcndec" id="XML_ErrorString">
+const XML_LChar * XMLCALL
+XML_ErrorString(enum XML_Error code);
+</pre>
+<div class="fcndef">
+Return a string describing the error corresponding to code.
+The code should be one of the enums that can be returned from
+<code><a href= "#XML_GetErrorCode" >XML_GetErrorCode</a></code>.
+</div>
+
+<pre class="fcndec" id="XML_GetCurrentByteIndex">
+XML_Index XMLCALL
+XML_GetCurrentByteIndex(XML_Parser p);
+</pre>
+<div class="fcndef">
+Return the byte offset of the position. This always corresponds to
+the values returned by <code><a href= "#XML_GetCurrentLineNumber"
+>XML_GetCurrentLineNumber</a></code> and <code><a href=
+"#XML_GetCurrentColumnNumber" >XML_GetCurrentColumnNumber</a></code>.
+</div>
+
+<pre class="fcndec" id="XML_GetCurrentLineNumber">
+XML_Size XMLCALL
+XML_GetCurrentLineNumber(XML_Parser p);
+</pre>
+<div class="fcndef">
+Return the line number of the position. The first line is reported as
+<code>1</code>.
+</div>
+
+<pre class="fcndec" id="XML_GetCurrentColumnNumber">
+XML_Size XMLCALL
+XML_GetCurrentColumnNumber(XML_Parser p);
+</pre>
+<div class="fcndef">
+Return the offset, from the beginning of the current line, of
+the position.
+</div>
+
+<pre class="fcndec" id="XML_GetCurrentByteCount">
+int XMLCALL
+XML_GetCurrentByteCount(XML_Parser p);
+</pre>
+<div class="fcndef">
+Return the number of bytes in the current event. Returns
+<code>0</code> if the event is inside a reference to an internal
+entity and for the end-tag event for empty element tags (the later can
+be used to distinguish empty-element tags from empty elements using
+separate start and end tags).
+</div>
+
+<pre class="fcndec" id="XML_GetInputContext">
+const char * XMLCALL
+XML_GetInputContext(XML_Parser p,
+ int *offset,
+ int *size);
+</pre>
+<div class="fcndef">
+
+<p>Returns the parser's input buffer, sets the integer pointed at by
+<code>offset</code> to the offset within this buffer of the current
+parse position, and set the integer pointed at by <code>size</code> to
+the size of the returned buffer.</p>
+
+<p>This should only be called from within a handler during an active
+parse and the returned buffer should only be referred to from within
+the handler that made the call. This input buffer contains the
+untranslated bytes of the input.</p>
+
+<p>Only a limited amount of context is kept, so if the event
+triggering a call spans over a very large amount of input, the actual
+parse position may be before the beginning of the buffer.</p>
+
+<p>If <code>XML_CONTEXT_BYTES</code> is not defined, this will always
+return NULL.</p>
+</div>
+
+<h3><a name="miscellaneous">Miscellaneous functions</a></h3>
+
+<p>The functions in this section either obtain state information from
+the parser or can be used to dynamicly set parser options.</p>
+
+<pre class="fcndec" id="XML_SetUserData">
+void XMLCALL
+XML_SetUserData(XML_Parser p,
+ void *userData);
+</pre>
+<div class="fcndef">
+This sets the user data pointer that gets passed to handlers. It
+overwrites any previous value for this pointer. Note that the
+application is responsible for freeing the memory associated with
+<code>userData</code> when it is finished with the parser. So if you
+call this when there's already a pointer there, and you haven't freed
+the memory associated with it, then you've probably just leaked
+memory.
+</div>
+
+<pre class="fcndec" id="XML_GetUserData">
+void * XMLCALL
+XML_GetUserData(XML_Parser p);
+</pre>
+<div class="fcndef">
+This returns the user data pointer that gets passed to handlers.
+It is actually implemented as a macro.
+</div>
+
+<pre class="fcndec" id="XML_UseParserAsHandlerArg">
+void XMLCALL
+XML_UseParserAsHandlerArg(XML_Parser p);
+</pre>
+<div class="fcndef">
+After this is called, handlers receive the parser in their
+<code>userData</code> arguments. The user data can still be obtained
+using the <code><a href= "#XML_GetUserData"
+>XML_GetUserData</a></code> function.
+</div>
+
+<pre class="fcndec" id="XML_SetBase">
+enum XML_Status XMLCALL
+XML_SetBase(XML_Parser p,
+ const XML_Char *base);
+</pre>
+<div class="fcndef">
+Set the base to be used for resolving relative URIs in system
+identifiers. The return value is <code>XML_STATUS_ERROR</code> if
+there's no memory to store base, otherwise it's
+<code>XML_STATUS_OK</code>.
+</div>
+
+<pre class="fcndec" id="XML_GetBase">
+const XML_Char * XMLCALL
+XML_GetBase(XML_Parser p);
+</pre>
+<div class="fcndef">
+Return the base for resolving relative URIs.
+</div>
+
+<pre class="fcndec" id="XML_GetSpecifiedAttributeCount">
+int XMLCALL
+XML_GetSpecifiedAttributeCount(XML_Parser p);
+</pre>
+<div class="fcndef">
+When attributes are reported to the start handler in the atts vector,
+attributes that were explicitly set in the element occur before any
+attributes that receive their value from default information in an
+ATTLIST declaration. This function returns the number of attributes
+that were explicitly set times two, thus giving the offset in the
+<code>atts</code> array passed to the start tag handler of the first
+attribute set due to defaults. It supplies information for the last
+call to a start handler. If called inside a start handler, then that
+means the current call.
+</div>
+
+<pre class="fcndec" id="XML_GetIdAttributeIndex">
+int XMLCALL
+XML_GetIdAttributeIndex(XML_Parser p);
+</pre>
+<div class="fcndef">
+Returns the index of the ID attribute passed in the atts array in the
+last call to <code><a href= "#XML_StartElementHandler"
+>XML_StartElementHandler</a></code>, or -1 if there is no ID
+attribute. If called inside a start handler, then that means the
+current call.
+</div>
+
+<pre class="fcndec" id="XML_SetEncoding">
+enum XML_Status XMLCALL
+XML_SetEncoding(XML_Parser p,
+ const XML_Char *encoding);
+</pre>
+<div class="fcndef">
+Set the encoding to be used by the parser. It is equivalent to
+passing a non-null encoding argument to the parser creation functions.
+It must not be called after <code><a href= "#XML_Parse"
+>XML_Parse</a></code> or <code><a href= "#XML_ParseBuffer"
+>XML_ParseBuffer</a></code> have been called on the given parser.
+Returns <code>XML_STATUS_OK</code> on success or
+<code>XML_STATUS_ERROR</code> on error.
+</div>
+
+<pre class="fcndec" id="XML_SetParamEntityParsing">
+int XMLCALL
+XML_SetParamEntityParsing(XML_Parser p,
+ enum XML_ParamEntityParsing code);
+</pre>
+<div class="fcndef">
+This enables parsing of parameter entities, including the external
+parameter entity that is the external DTD subset, according to
+<code>code</code>.
+The choices for <code>code</code> are:
+<ul>
+<li><code>XML_PARAM_ENTITY_PARSING_NEVER</code></li>
+<li><code>XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE</code></li>
+<li><code>XML_PARAM_ENTITY_PARSING_ALWAYS</code></li>
+</ul>
+</div>
+
+<pre class="fcndec" id="XML_UseForeignDTD">
+enum XML_Error XMLCALL
+XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD);
+</pre>
+<div class="fcndef">
+<p>This function allows an application to provide an external subset
+for the document type declaration for documents which do not specify
+an external subset of their own. For documents which specify an
+external subset in their DOCTYPE declaration, the application-provided
+subset will be ignored. If the document does not contain a DOCTYPE
+declaration at all and <code>useDTD</code> is true, the
+application-provided subset will be parsed, but the
+<code>startDoctypeDeclHandler</code> and
+<code>endDoctypeDeclHandler</code> functions, if set, will not be
+called. The setting of parameter entity parsing, controlled using
+<code><a href= "#XML_SetParamEntityParsing"
+>XML_SetParamEntityParsing</a></code>, will be honored.</p>
+
+<p>The application-provided external subset is read by calling the
+external entity reference handler set via <code><a href=
+"#XML_SetExternalEntityRefHandler"
+>XML_SetExternalEntityRefHandler</a></code> with both
+<code>publicId</code> and <code>systemId</code> set to NULL.</p>
+
+<p>If this function is called after parsing has begun, it returns
+<code>XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING</code> and ignores
+<code>useDTD</code>. If called when Expat has been compiled without
+DTD support, it returns
+<code>XML_ERROR_FEATURE_REQUIRES_XML_DTD</code>. Otherwise, it
+returns <code>XML_ERROR_NONE</code>.</p>
+
+<p><b>Note:</b> For the purpose of checking WFC: Entity Declared, passing
+<code>useDTD == XML_TRUE</code> will make the parser behave as if
+the document had a DTD with an external subset. This holds true even if
+the external entity reference handler returns without action.</p>
+</div>
+
+<pre class="fcndec" id="XML_SetReturnNSTriplet">
+void XMLCALL
+XML_SetReturnNSTriplet(XML_Parser parser,
+ int do_nst);
+</pre>
+<div class="fcndef">
+<p>
+This function only has an effect when using a parser created with
+<code><a href= "#XML_ParserCreateNS" >XML_ParserCreateNS</a></code>,
+i.e. when namespace processing is in effect. The <code>do_nst</code>
+sets whether or not prefixes are returned with names qualified with a
+namespace prefix. If this function is called with <code>do_nst</code>
+non-zero, then afterwards namespace qualified names (that is qualified
+with a prefix as opposed to belonging to a default namespace) are
+returned as a triplet with the three parts separated by the namespace
+separator specified when the parser was created. The order of
+returned parts is URI, local name, and prefix.</p> <p>If
+<code>do_nst</code> is zero, then namespaces are reported in the
+default manner, URI then local_name separated by the namespace
+separator.</p>
+</div>
+
+<pre class="fcndec" id="XML_DefaultCurrent">
+void XMLCALL
+XML_DefaultCurrent(XML_Parser parser);
+</pre>
+<div class="fcndef">
+This can be called within a handler for a start element, end element,
+processing instruction or character data. It causes the corresponding
+markup to be passed to the default handler set by <code><a
+href="#XML_SetDefaultHandler" >XML_SetDefaultHandler</a></code> or
+<code><a href="#XML_SetDefaultHandlerExpand"
+>XML_SetDefaultHandlerExpand</a></code>. It does nothing if there is
+not a default handler.
+</div>
+
+<pre class="fcndec" id="XML_ExpatVersion">
+XML_LChar * XMLCALL
+XML_ExpatVersion();
+</pre>
+<div class="fcndef">
+Return the library version as a string (e.g. <code>"expat_1.95.1"</code>).
+</div>
+
+<pre class="fcndec" id="XML_ExpatVersionInfo">
+struct XML_Expat_Version XMLCALL
+XML_ExpatVersionInfo();
+</pre>
+<pre class="signature">
+typedef struct {
+ int major;
+ int minor;
+ int micro;
+} XML_Expat_Version;
+</pre>
+<div class="fcndef">
+Return the library version information as a structure.
+Some macros are also defined that support compile-time tests of the
+library version:
+<ul>
+<li><code>XML_MAJOR_VERSION</code></li>
+<li><code>XML_MINOR_VERSION</code></li>
+<li><code>XML_MICRO_VERSION</code></li>
+</ul>
+Testing these constants is currently the best way to determine if
+particular parts of the Expat API are available.
+</div>
+
+<pre class="fcndec" id="XML_GetFeatureList">
+const XML_Feature * XMLCALL
+XML_GetFeatureList();
+</pre>
+<pre class="signature">
+enum XML_FeatureEnum {
+ XML_FEATURE_END = 0,
+ XML_FEATURE_UNICODE,
+ XML_FEATURE_UNICODE_WCHAR_T,
+ XML_FEATURE_DTD,
+ XML_FEATURE_CONTEXT_BYTES,
+ XML_FEATURE_MIN_SIZE,
+ XML_FEATURE_SIZEOF_XML_CHAR,
+ XML_FEATURE_SIZEOF_XML_LCHAR,
+ XML_FEATURE_NS,
+ XML_FEATURE_LARGE_SIZE
+};
+
+typedef struct {
+ enum XML_FeatureEnum feature;
+ XML_LChar *name;
+ long int value;
+} XML_Feature;
+</pre>
+<div class="fcndef">
+<p>Returns a list of "feature" records, providing details on how
+Expat was configured at compile time. Most applications should not
+need to worry about this, but this information is otherwise not
+available from Expat. This function allows code that does need to
+check these features to do so at runtime.</p>
+
+<p>The return value is an array of <code>XML_Feature</code>,
+terminated by a record with a <code>feature</code> of
+<code>XML_FEATURE_END</code> and <code>name</code> of NULL,
+identifying the feature-test macros Expat was compiled with. Since an
+application that requires this kind of information needs to determine
+the type of character the <code>name</code> points to, records for the
+<code>XML_FEATURE_SIZEOF_XML_CHAR</code> and
+<code>XML_FEATURE_SIZEOF_XML_LCHAR</code> will be located at the
+beginning of the list, followed by <code>XML_FEATURE_UNICODE</code>
+and <code>XML_FEATURE_UNICODE_WCHAR_T</code>, if they are present at
+all.</p>
+
+<p>Some features have an associated value. If there isn't an
+associated value, the <code>value</code> field is set to 0. At this
+time, the following features have been defined to have values:</p>
+
+<dl>
+ <dt><code>XML_FEATURE_SIZEOF_XML_CHAR</code></dt>
+ <dd>The number of bytes occupied by one <code>XML_Char</code>
+ character.</dd>
+ <dt><code>XML_FEATURE_SIZEOF_XML_LCHAR</code></dt>
+ <dd>The number of bytes occupied by one <code>XML_LChar</code>
+ character.</dd>
+ <dt><code>XML_FEATURE_CONTEXT_BYTES</code></dt>
+ <dd>The maximum number of characters of context which can be
+ reported by <code><a href= "#XML_GetInputContext"
+ >XML_GetInputContext</a></code>.</dd>
+</dl>
+</div>
+
+<pre class="fcndec" id="XML_FreeContentModel">
+void XMLCALL
+XML_FreeContentModel(XML_Parser parser, XML_Content *model);
+</pre>
+<div class="fcndef">
+Function to deallocate the <code>model</code> argument passed to the
+<code>XML_ElementDeclHandler</code> callback set using <code><a
+href="#XML_SetElementDeclHandler" >XML_ElementDeclHandler</a></code>.
+This function should not be used for any other purpose.
+</div>
+
+<p>The following functions allow external code to share the memory
+allocator an <code>XML_Parser</code> has been configured to use. This
+is especially useful for third-party libraries that interact with a
+parser object created by application code, or heavily layered
+applications. This can be essential when using dynamically loaded
+libraries which use different C standard libraries (this can happen on
+Windows, at least).</p>
+
+<pre class="fcndec" id="XML_MemMalloc">
+void * XMLCALL
+XML_MemMalloc(XML_Parser parser, size_t size);
+</pre>
+<div class="fcndef">
+Allocate <code>size</code> bytes of memory using the allocator the
+<code>parser</code> object has been configured to use. Returns a
+pointer to the memory or NULL on failure. Memory allocated in this
+way must be freed using <code><a href="#XML_MemFree"
+>XML_MemFree</a></code>.
+</div>
+
+<pre class="fcndec" id="XML_MemRealloc">
+void * XMLCALL
+XML_MemRealloc(XML_Parser parser, void *ptr, size_t size);
+</pre>
+<div class="fcndef">
+Allocate <code>size</code> bytes of memory using the allocator the
+<code>parser</code> object has been configured to use.
+<code>ptr</code> must point to a block of memory allocated by <code><a
+href="#XML_MemMalloc" >XML_MemMalloc</a></code> or
+<code>XML_MemRealloc</code>, or be NULL. This function tries to
+expand the block pointed to by <code>ptr</code> if possible. Returns
+a pointer to the memory or NULL on failure. On success, the original
+block has either been expanded or freed. On failure, the original
+block has not been freed; the caller is responsible for freeing the
+original block. Memory allocated in this way must be freed using
+<code><a href="#XML_MemFree"
+>XML_MemFree</a></code>.
+</div>
+
+<pre class="fcndec" id="XML_MemFree">
+void XMLCALL
+XML_MemFree(XML_Parser parser, void *ptr);
+</pre>
+<div class="fcndef">
+Free a block of memory pointed to by <code>ptr</code>. The block must
+have been allocated by <code><a href="#XML_MemMalloc"
+>XML_MemMalloc</a></code> or <code>XML_MemRealloc</code>, or be NULL.
+</div>
+
+<hr />
+<p><a href="http://validator.w3.org/check/referer"><img
+ src="valid-xhtml10.png" alt="Valid XHTML 1.0!"
+ height="31" width="88" class="noborder" /></a></p>
+</div>
+</body>
+</html>
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/style.css b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/style.css
new file mode 100644
index 0000000..69df30b
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/style.css
@@ -0,0 +1,101 @@
+body {
+ background-color: white;
+ border: 0px;
+ margin: 0px;
+ padding: 0px;
+}
+
+.corner {
+ width: 200px;
+ height: 80px;
+ text-align: center;
+}
+
+.banner {
+ background-color: rgb(110,139,61);
+ color: rgb(255,236,176);
+ padding-left: 2em;
+}
+
+.banner h1 {
+ font-size: 200%;
+}
+
+.content {
+ padding: 0em 2em 1em 2em;
+}
+
+.releaseno {
+ background-color: rgb(110,139,61);
+ color: rgb(255,236,176);
+ padding-bottom: 0.3em;
+ padding-top: 0.5em;
+ text-align: center;
+ font-weight: bold;
+}
+
+.noborder {
+ border-width: 0px;
+}
+
+.eg {
+ padding-left: 1em;
+ padding-top: .5em;
+ padding-bottom: .5em;
+ border: solid thin;
+ margin: 1em 0;
+ background-color: tan;
+ margin-left: 2em;
+ margin-right: 10%;
+}
+
+.pseudocode {
+ padding-left: 1em;
+ padding-top: .5em;
+ padding-bottom: .5em;
+ border: solid thin;
+ margin: 1em 0;
+ background-color: rgb(250,220,180);
+ margin-left: 2em;
+ margin-right: 10%;
+}
+
+.handler {
+ width: 100%;
+ border-top-width: thin;
+ margin-bottom: 1em;
+}
+
+.handler p {
+ margin-left: 2em;
+}
+
+.setter {
+ font-weight: bold;
+}
+
+.signature {
+ color: navy;
+}
+
+.fcndec {
+ width: 100%;
+ border-top-width: thin;
+ font-weight: bold;
+}
+
+.fcndef {
+ margin-left: 2em;
+ margin-bottom: 2em;
+}
+
+dd {
+ margin-bottom: 2em;
+}
+
+.cpp-symbols dt {
+ font-family: monospace;
+}
+.cpp-symbols dd {
+ margin-bottom: 1em;
+}
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/valid-xhtml10.png b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/valid-xhtml10.png
new file mode 100644
index 0000000..4c23f48
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Doc/valid-xhtml10.png
Binary files differ
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/MANIFEST.txt b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/MANIFEST.txt
new file mode 100644
index 0000000..b7cd395
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/MANIFEST.txt
@@ -0,0 +1,27 @@
+ Overview of the Expat distribution
+
+The Expat distribution creates several subdirectories on your system.
+Some of these directories contain components of interest to all Expat
+users, and some contain material of interest to developers who wish to
+use Expat in their applications. In the list below, <top> is the
+directory you specified to the installer.
+
+ Directory Contents
+ ---------------------------------------------------------------------
+ <top>\ Some general information files.
+
+ <top>\Doc\ API documentation for developers.
+
+ <top>\Bin\ Pre-compiled dynamic libraries for developers.
+ Pre-compiled static libraries for developers (*MT.lib).
+ The XML well-formedness checker xmlwf.
+
+ <top>\Source\ Source code, which may interest some developers,
+ including a workspace for Microsft Visual C++.
+ The source code includes the parser, the well-
+ formedness checker, and a couple of small sample
+ applications.
+
+ <top>\Source\bcb5\ Project files for Borland C++ Builder 5 and BCC 5.5.
+
+
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/README.google b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/README.google
new file mode 100644
index 0000000..b1359aa
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/README.google
@@ -0,0 +1,39 @@
+URL: http://downloads.sourceforge.net/expat/expat-win32bin-2.0.1.exe
+Version: 2.0.1
+License: MIT
+License File: COPYING.txt
+
+Description:
+This is Expat XML parser - very lightweight C library for
+parsing XML, typically linked as a static lib.
+File tree is expanded from the win32-specific download of Expat from:
+ http://downloads.sourceforge.net/expat/expat-win32bin-2.0.1.exe
+Some directories are not checked in (bin, examples, tests)
+
+Local Modifications:
+The only modifications from the downloaded version is a patch to an
+internally discovered bug with the handling of utf-8 characters,
+that leads to a crash (CL 6144603 etc.), and a change has been made to
+winconfig.h to prevent a compiler warning when compiling with
+WIN32_LEAN_AND_MEAN defined.
+
+The typical usage of the library is to compile it in or compile it
+into a static lib and link in.
+
+The files you need to compile are:
+lib\xmlparse.c
+lib\xmltok.c
+lib\xmlrole.c
+
+Following compile-time #define variables can be used:
+XML_DTD - if you need to parse DTDs and validate
+XML_UNICODE - if defined, all strings are 16-bit unicode, if not then UTF8.
+ defines XML_Char appropriately
+XML_UNICODE_WCHAR_T - if XML_UNICODE is defined, controls whether XML_Char
+ is wchar_t or unsigned short
+COMPILED_FROM_DSP - if defined, automatically defines XML_UNICODE
+ and includes windows headers
+XML_STATIC - should be defined to produce statically-linked DLL
+XML_MIN_SIZE - if defined, produces smaller but slower code
+
+
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/README.txt b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/README.txt
new file mode 100644
index 0000000..fda282a
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/README.txt
@@ -0,0 +1,137 @@
+
+ Expat, Release 2.0.1
+
+This is Expat, a C library for parsing XML, written by James Clark.
+Expat is a stream-oriented XML parser. This means that you register
+handlers with the parser before starting the parse. These handlers
+are called when the parser discovers the associated structures in the
+document being parsed. A start tag is an example of the kind of
+structures for which you may register handlers.
+
+Windows users should use the expat_win32bin package, which includes
+both precompiled libraries and executables, and source code for
+developers.
+
+Expat is free software. You may copy, distribute, and modify it under
+the terms of the License contained in the file COPYING distributed
+with this package. This license is the same as the MIT/X Consortium
+license.
+
+Versions of Expat that have an odd minor version (the middle number in
+the release above), are development releases and should be considered
+as beta software. Releases with even minor version numbers are
+intended to be production grade software.
+
+If you are building Expat from a check-out from the CVS repository,
+you need to run a script that generates the configure script using the
+GNU autoconf and libtool tools. To do this, you need to have
+autoconf 2.52 or newer and libtool 1.4 or newer (1.5 or newer preferred).
+Run the script like this:
+
+ ./buildconf.sh
+
+Once this has been done, follow the same instructions as for building
+from a source distribution.
+
+To build Expat from a source distribution, you first run the
+configuration shell script in the top level distribution directory:
+
+ ./configure
+
+There are many options which you may provide to configure (which you
+can discover by running configure with the --help option). But the
+one of most interest is the one that sets the installation directory.
+By default, the configure script will set things up to install
+libexpat into /usr/local/lib, expat.h into /usr/local/include, and
+xmlwf into /usr/local/bin. If, for example, you'd prefer to install
+into /home/me/mystuff/lib, /home/me/mystuff/include, and
+/home/me/mystuff/bin, you can tell configure about that with:
+
+ ./configure --prefix=/home/me/mystuff
+
+Another interesting option is to enable 64-bit integer support for
+line and column numbers and the over-all byte index:
+
+ ./configure CPPFLAGS=-DXML_LARGE_SIZE
+
+However, such a modification would be a breaking change to the ABI
+and is therefore not recommended for general use - e.g. as part of
+a Linux distribution - but rather for builds with special requirements.
+
+After running the configure script, the "make" command will build
+things and "make install" will install things into their proper
+location. Have a look at the "Makefile" to learn about additional
+"make" options. Note that you need to have write permission into
+the directories into which things will be installed.
+
+If you are interested in building Expat to provide document
+information in UTF-16 rather than the default UTF-8, follow these
+instructions (after having run "make distclean"):
+
+ 1. For UTF-16 output as unsigned short (and version/error
+ strings as char), run:
+
+ ./configure CPPFLAGS=-DXML_UNICODE
+
+ For UTF-16 output as wchar_t (incl. version/error strings),
+ run:
+
+ ./configure CFLAGS="-g -O2 -fshort-wchar" \
+ CPPFLAGS=-DXML_UNICODE_WCHAR_T
+
+ 2. Edit the MakeFile, changing:
+
+ LIBRARY = libexpat.la
+
+ to:
+
+ LIBRARY = libexpatw.la
+
+ (Note the additional "w" in the library name.)
+
+ 3. Run "make buildlib" (which builds the library only).
+ Or, to save step 2, run "make buildlib LIBRARY=libexpatw.la".
+
+ 4. Run "make installlib" (which installs the library only).
+ Or, if step 2 was omitted, run "make installlib LIBRARY=libexpatw.la".
+
+Using DESTDIR or INSTALL_ROOT is enabled, with INSTALL_ROOT being the default
+value for DESTDIR, and the rest of the make file using only DESTDIR.
+It works as follows:
+ $ make install DESTDIR=/path/to/image
+overrides the in-makefile set DESTDIR, while both
+ $ INSTALL_ROOT=/path/to/image make install
+ $ make install INSTALL_ROOT=/path/to/image
+use DESTDIR=$(INSTALL_ROOT), even if DESTDIR eventually is defined in the
+environment, because variable-setting priority is
+1) commandline
+2) in-makefile
+3) environment
+
+Note for Solaris users: The "ar" command is usually located in
+"/usr/ccs/bin", which is not in the default PATH. You will need to
+add this to your path for the "make" command, and probably also switch
+to GNU make (the "make" found in /usr/ccs/bin does not seem to work
+properly -- appearantly it does not understand .PHONY directives). If
+you're using ksh or bash, use this command to build:
+
+ PATH=/usr/ccs/bin:$PATH make
+
+When using Expat with a project using autoconf for configuration, you
+can use the probing macro in conftools/expat.m4 to determine how to
+include Expat. See the comments at the top of that file for more
+information.
+
+A reference manual is available in the file doc/reference.html in this
+distribution.
+
+The homepage for this project is http://www.libexpat.org/. There
+are links there to connect you to the bug reports page. If you need
+to report a bug when you don't have access to a browser, you may also
+send a bug report by email to expat-bugs@mail.libexpat.org.
+
+Discussion related to the direction of future expat development takes
+place on expat-discuss@mail.libexpat.org. Archives of this list and
+other Expat-related lists may be found at:
+
+ http://mail.libexpat.org/mailman/listinfo/
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/README.txt b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/README.txt
new file mode 100644
index 0000000..77a94bc
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/README.txt
@@ -0,0 +1,80 @@
+
+Expat can be built on Windows in three ways:
+ using MS Visual C++ (6.0 or .NET), Borland C++ Builder 5 or Cygwin.
+
+* Cygwin:
+ This follows the Unix build procedures.
+
+* C++ Builder 5:
+ Possible with make files in the BCB5 subdirectory.
+ Details can be found in the ReadMe file located there.
+
+* MS Visual C++ 6:
+ Based on the workspace file expat.dsw. The related project
+ files (.dsp) are located in the lib subdirectory.
+
+* MS Visual Studio .NET 2002, 2003, 2005:
+ The VC++ 6 workspace file (expat.dsw) and project files (.dsp)
+ can be opened and imported in VS.NET without problems.
+
+* All MS C/C++ compilers:
+ The output for all projects will be generated in the win32\bin
+ directory, intermediate files will be located in project-specific
+ subdirectories of win32\tmp.
+
+* Creating MinGW dynamic libraries from MS VC++ DLLs:
+
+ On the command line, execute these steps:
+ pexports libexpat.dll > expat.def
+ pexports libexpatw.dll > expatw.def
+ dlltool -d expat.def -l libexpat.a
+ dlltool -d expatw.def -l libexpatw.a
+
+ The *.a files are mingw libraries.
+
+* Special note about MS VC++ and runtime libraries:
+
+ There are three possible configurations: using the
+ single threaded or multithreaded run-time library,
+ or using the multi-threaded run-time Dll. That is,
+ one can build three different Expat libraries depending
+ on the needs of the application.
+
+ Dynamic Linking:
+
+ By default the Expat Dlls are built to link statically
+ with the multi-threaded run-time library.
+ The libraries are named
+ - libexpat(w).dll
+ - libexpat(w).lib (import library)
+ The "w" indicates the UTF-16 version of the library.
+
+ One rarely uses other versions of the Dll, but they can
+ be built easily by specifying a different RTL linkage in
+ the IDE on the C/C++ tab under the category Code Generation.
+
+ Static Linking:
+
+ The libraries should be named like this:
+ Single-theaded: libexpat(w)ML.lib
+ Multi-threaded: libexpat(w)MT.lib
+ Multi-threaded Dll: libexpat(w)MD.lib
+ The suffixes conform to the compiler switch settings
+ /ML, /MT and /MD for MS VC++.
+
+ Note: In Visual Studio 2005 (Visual C++ 8.0) and later, the
+ single-threaded runtime library is not supported anymore.
+
+ By default, the expat-static and expatw-static projects are set up
+ to link statically against the multithreaded run-time library,
+ so they will build libexpatMT.lib or libexpatwMT.lib files.
+
+ To build the other versions of the static library,
+ go to Project - Settings:
+ - specify a different RTL linkage on the C/C++ tab
+ under the category Code Generation.
+ - then, on the Library tab, change the output file name
+ accordingly, as described above
+
+ An application linking to the static libraries must
+ have the global macro XML_STATIC defined.
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/amigaconfig.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/amigaconfig.h
new file mode 100644
index 0000000..86c6115
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/amigaconfig.h
@@ -0,0 +1,32 @@
+#ifndef AMIGACONFIG_H
+#define AMIGACONFIG_H
+
+/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */
+#define BYTEORDER 4321
+
+/* Define to 1 if you have the `bcopy' function. */
+#define HAVE_BCOPY 1
+
+/* Define to 1 if you have the <check.h> header file. */
+#undef HAVE_CHECK_H
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* whether byteorder is bigendian */
+#define WORDS_BIGENDIAN
+
+/* Define to specify how much context to retain around the current parse
+ point. */
+#define XML_CONTEXT_BYTES 1024
+
+/* Define to make parameter entity parsing functionality available. */
+#define XML_DTD
+
+/* Define to make XML Namespaces functionality available. */
+#define XML_NS
+
+#endif /* AMIGACONFIG_H */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/ascii.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/ascii.h
new file mode 100644
index 0000000..d10530b
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/ascii.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+#define ASCII_A 0x41
+#define ASCII_B 0x42
+#define ASCII_C 0x43
+#define ASCII_D 0x44
+#define ASCII_E 0x45
+#define ASCII_F 0x46
+#define ASCII_G 0x47
+#define ASCII_H 0x48
+#define ASCII_I 0x49
+#define ASCII_J 0x4A
+#define ASCII_K 0x4B
+#define ASCII_L 0x4C
+#define ASCII_M 0x4D
+#define ASCII_N 0x4E
+#define ASCII_O 0x4F
+#define ASCII_P 0x50
+#define ASCII_Q 0x51
+#define ASCII_R 0x52
+#define ASCII_S 0x53
+#define ASCII_T 0x54
+#define ASCII_U 0x55
+#define ASCII_V 0x56
+#define ASCII_W 0x57
+#define ASCII_X 0x58
+#define ASCII_Y 0x59
+#define ASCII_Z 0x5A
+
+#define ASCII_a 0x61
+#define ASCII_b 0x62
+#define ASCII_c 0x63
+#define ASCII_d 0x64
+#define ASCII_e 0x65
+#define ASCII_f 0x66
+#define ASCII_g 0x67
+#define ASCII_h 0x68
+#define ASCII_i 0x69
+#define ASCII_j 0x6A
+#define ASCII_k 0x6B
+#define ASCII_l 0x6C
+#define ASCII_m 0x6D
+#define ASCII_n 0x6E
+#define ASCII_o 0x6F
+#define ASCII_p 0x70
+#define ASCII_q 0x71
+#define ASCII_r 0x72
+#define ASCII_s 0x73
+#define ASCII_t 0x74
+#define ASCII_u 0x75
+#define ASCII_v 0x76
+#define ASCII_w 0x77
+#define ASCII_x 0x78
+#define ASCII_y 0x79
+#define ASCII_z 0x7A
+
+#define ASCII_0 0x30
+#define ASCII_1 0x31
+#define ASCII_2 0x32
+#define ASCII_3 0x33
+#define ASCII_4 0x34
+#define ASCII_5 0x35
+#define ASCII_6 0x36
+#define ASCII_7 0x37
+#define ASCII_8 0x38
+#define ASCII_9 0x39
+
+#define ASCII_TAB 0x09
+#define ASCII_SPACE 0x20
+#define ASCII_EXCL 0x21
+#define ASCII_QUOT 0x22
+#define ASCII_AMP 0x26
+#define ASCII_APOS 0x27
+#define ASCII_MINUS 0x2D
+#define ASCII_PERIOD 0x2E
+#define ASCII_COLON 0x3A
+#define ASCII_SEMI 0x3B
+#define ASCII_LT 0x3C
+#define ASCII_EQUALS 0x3D
+#define ASCII_GT 0x3E
+#define ASCII_LSQB 0x5B
+#define ASCII_RSQB 0x5D
+#define ASCII_UNDERSCORE 0x5F
+#define ASCII_LPAREN 0x28
+#define ASCII_RPAREN 0x29
+#define ASCII_FF 0x0C
+#define ASCII_SLASH 0x2F
+#define ASCII_HASH 0x23
+#define ASCII_PIPE 0x7C
+#define ASCII_COMMA 0x2C
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/asciitab.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/asciitab.h
new file mode 100644
index 0000000..79a15c2
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/asciitab.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat.dsp b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat.dsp
new file mode 100644
index 0000000..1fc17d4
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat.dsp
@@ -0,0 +1,185 @@
+# Microsoft Developer Studio Project File - Name="expat" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=expat - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "expat.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "expat.mak" CFG="expat - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "expat - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "expat - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "expat - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\win32\bin\Release"
+# PROP Intermediate_Dir "..\win32\tmp\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXPAT_EXPORTS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILED_FROM_DSP" /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /machine:I386 /out:"..\win32\bin\Release/libexpat.dll"
+
+!ELSEIF "$(CFG)" == "expat - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\win32\bin\Debug"
+# PROP Intermediate_Dir "..\win32\tmp\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXPAT_EXPORTS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "_DEBUG" /D "COMPILED_FROM_DSP" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /debug /machine:I386 /out:"..\win32\bin\Debug/libexpat.dll"
+
+!ENDIF
+
+# Begin Target
+
+# Name "expat - Win32 Release"
+# Name "expat - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\libexpat.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlparse.c
+
+!IF "$(CFG)" == "expat - Win32 Release"
+
+!ELSEIF "$(CFG)" == "expat - Win32 Debug"
+
+# ADD CPP /GX- /Od
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlrole.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_impl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_ns.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ascii.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\asciitab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expat_external.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\iasciitab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\latin1tab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\nametab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8tab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlrole.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_impl.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat.h
new file mode 100644
index 0000000..20a8278
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat.h
@@ -0,0 +1,1014 @@
+/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+#ifndef Expat_INCLUDED
+#define Expat_INCLUDED 1
+
+#ifdef __VMS
+/* 0 1 2 3 0 1 2 3
+ 1234567890123456789012345678901 1234567890123456789012345678901 */
+#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler
+#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler
+#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler
+#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg
+#endif
+
+#include <stdlib.h>
+#include "expat_external.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct XML_ParserStruct;
+typedef struct XML_ParserStruct *XML_Parser;
+
+/* Should this be defined using stdbool.h when C99 is available? */
+typedef unsigned char XML_Bool;
+#define XML_TRUE ((XML_Bool) 1)
+#define XML_FALSE ((XML_Bool) 0)
+
+/* The XML_Status enum gives the possible return values for several
+ API functions. The preprocessor #defines are included so this
+ stanza can be added to code that still needs to support older
+ versions of Expat 1.95.x:
+
+ #ifndef XML_STATUS_OK
+ #define XML_STATUS_OK 1
+ #define XML_STATUS_ERROR 0
+ #endif
+
+ Otherwise, the #define hackery is quite ugly and would have been
+ dropped.
+*/
+enum XML_Status {
+ XML_STATUS_ERROR = 0,
+#define XML_STATUS_ERROR XML_STATUS_ERROR
+ XML_STATUS_OK = 1,
+#define XML_STATUS_OK XML_STATUS_OK
+ XML_STATUS_SUSPENDED = 2
+#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED
+};
+
+enum XML_Error {
+ XML_ERROR_NONE,
+ XML_ERROR_NO_MEMORY,
+ XML_ERROR_SYNTAX,
+ XML_ERROR_NO_ELEMENTS,
+ XML_ERROR_INVALID_TOKEN,
+ XML_ERROR_UNCLOSED_TOKEN,
+ XML_ERROR_PARTIAL_CHAR,
+ XML_ERROR_TAG_MISMATCH,
+ XML_ERROR_DUPLICATE_ATTRIBUTE,
+ XML_ERROR_JUNK_AFTER_DOC_ELEMENT,
+ XML_ERROR_PARAM_ENTITY_REF,
+ XML_ERROR_UNDEFINED_ENTITY,
+ XML_ERROR_RECURSIVE_ENTITY_REF,
+ XML_ERROR_ASYNC_ENTITY,
+ XML_ERROR_BAD_CHAR_REF,
+ XML_ERROR_BINARY_ENTITY_REF,
+ XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
+ XML_ERROR_MISPLACED_XML_PI,
+ XML_ERROR_UNKNOWN_ENCODING,
+ XML_ERROR_INCORRECT_ENCODING,
+ XML_ERROR_UNCLOSED_CDATA_SECTION,
+ XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+ XML_ERROR_NOT_STANDALONE,
+ XML_ERROR_UNEXPECTED_STATE,
+ XML_ERROR_ENTITY_DECLARED_IN_PE,
+ XML_ERROR_FEATURE_REQUIRES_XML_DTD,
+ XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING,
+ /* Added in 1.95.7. */
+ XML_ERROR_UNBOUND_PREFIX,
+ /* Added in 1.95.8. */
+ XML_ERROR_UNDECLARING_PREFIX,
+ XML_ERROR_INCOMPLETE_PE,
+ XML_ERROR_XML_DECL,
+ XML_ERROR_TEXT_DECL,
+ XML_ERROR_PUBLICID,
+ XML_ERROR_SUSPENDED,
+ XML_ERROR_NOT_SUSPENDED,
+ XML_ERROR_ABORTED,
+ XML_ERROR_FINISHED,
+ XML_ERROR_SUSPEND_PE,
+ /* Added in 2.0. */
+ XML_ERROR_RESERVED_PREFIX_XML,
+ XML_ERROR_RESERVED_PREFIX_XMLNS,
+ XML_ERROR_RESERVED_NAMESPACE_URI
+};
+
+enum XML_Content_Type {
+ XML_CTYPE_EMPTY = 1,
+ XML_CTYPE_ANY,
+ XML_CTYPE_MIXED,
+ XML_CTYPE_NAME,
+ XML_CTYPE_CHOICE,
+ XML_CTYPE_SEQ
+};
+
+enum XML_Content_Quant {
+ XML_CQUANT_NONE,
+ XML_CQUANT_OPT,
+ XML_CQUANT_REP,
+ XML_CQUANT_PLUS
+};
+
+/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be
+ XML_CQUANT_NONE, and the other fields will be zero or NULL.
+ If type == XML_CTYPE_MIXED, then quant will be NONE or REP and
+ numchildren will contain number of elements that may be mixed in
+ and children point to an array of XML_Content cells that will be
+ all of XML_CTYPE_NAME type with no quantification.
+
+ If type == XML_CTYPE_NAME, then the name points to the name, and
+ the numchildren field will be zero and children will be NULL. The
+ quant fields indicates any quantifiers placed on the name.
+
+ CHOICE and SEQ will have name NULL, the number of children in
+ numchildren and children will point, recursively, to an array
+ of XML_Content cells.
+
+ The EMPTY, ANY, and MIXED types will only occur at top level.
+*/
+
+typedef struct XML_cp XML_Content;
+
+struct XML_cp {
+ enum XML_Content_Type type;
+ enum XML_Content_Quant quant;
+ XML_Char * name;
+ unsigned int numchildren;
+ XML_Content * children;
+};
+
+
+/* This is called for an element declaration. See above for
+ description of the model argument. It's the caller's responsibility
+ to free model when finished with it.
+*/
+typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData,
+ const XML_Char *name,
+ XML_Content *model);
+
+XMLPARSEAPI(void)
+XML_SetElementDeclHandler(XML_Parser parser,
+ XML_ElementDeclHandler eldecl);
+
+/* The Attlist declaration handler is called for *each* attribute. So
+ a single Attlist declaration with multiple attributes declared will
+ generate multiple calls to this handler. The "default" parameter
+ may be NULL in the case of the "#IMPLIED" or "#REQUIRED"
+ keyword. The "isrequired" parameter will be true and the default
+ value will be NULL in the case of "#REQUIRED". If "isrequired" is
+ true and default is non-NULL, then this is a "#FIXED" default.
+*/
+typedef void (XMLCALL *XML_AttlistDeclHandler) (
+ void *userData,
+ const XML_Char *elname,
+ const XML_Char *attname,
+ const XML_Char *att_type,
+ const XML_Char *dflt,
+ int isrequired);
+
+XMLPARSEAPI(void)
+XML_SetAttlistDeclHandler(XML_Parser parser,
+ XML_AttlistDeclHandler attdecl);
+
+/* The XML declaration handler is called for *both* XML declarations
+ and text declarations. The way to distinguish is that the version
+ parameter will be NULL for text declarations. The encoding
+ parameter may be NULL for XML declarations. The standalone
+ parameter will be -1, 0, or 1 indicating respectively that there
+ was no standalone parameter in the declaration, that it was given
+ as no, or that it was given as yes.
+*/
+typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData,
+ const XML_Char *version,
+ const XML_Char *encoding,
+ int standalone);
+
+XMLPARSEAPI(void)
+XML_SetXmlDeclHandler(XML_Parser parser,
+ XML_XmlDeclHandler xmldecl);
+
+
+typedef struct {
+ void *(*malloc_fcn)(size_t size);
+ void *(*realloc_fcn)(void *ptr, size_t size);
+ void (*free_fcn)(void *ptr);
+} XML_Memory_Handling_Suite;
+
+/* Constructs a new parser; encoding is the encoding specified by the
+ external protocol or NULL if there is none specified.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreate(const XML_Char *encoding);
+
+/* Constructs a new parser and namespace processor. Element type
+ names and attribute names that belong to a namespace will be
+ expanded; unprefixed attribute names are never expanded; unprefixed
+ element type names are expanded only if there is a default
+ namespace. The expanded name is the concatenation of the namespace
+ URI, the namespace separator character, and the local part of the
+ name. If the namespace separator is '\0' then the namespace URI
+ and the local part will be concatenated without any separator.
+ It is a programming error to use the separator '\0' with namespace
+ triplets (see XML_SetReturnNSTriplet).
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator);
+
+
+/* Constructs a new parser using the memory management suite referred to
+ by memsuite. If memsuite is NULL, then use the standard library memory
+ suite. If namespaceSeparator is non-NULL it creates a parser with
+ namespace processing as described above. The character pointed at
+ will serve as the namespace separator.
+
+ All further memory operations used for the created parser will come from
+ the given suite.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreate_MM(const XML_Char *encoding,
+ const XML_Memory_Handling_Suite *memsuite,
+ const XML_Char *namespaceSeparator);
+
+/* Prepare a parser object to be re-used. This is particularly
+ valuable when memory allocation overhead is disproportionatly high,
+ such as when a large number of small documnents need to be parsed.
+ All handlers are cleared from the parser, except for the
+ unknownEncodingHandler. The parser's external state is re-initialized
+ except for the values of ns and ns_triplets.
+
+ Added in Expat 1.95.3.
+*/
+XMLPARSEAPI(XML_Bool)
+XML_ParserReset(XML_Parser parser, const XML_Char *encoding);
+
+/* atts is array of name/value pairs, terminated by 0;
+ names and values are 0 terminated.
+*/
+typedef void (XMLCALL *XML_StartElementHandler) (void *userData,
+ const XML_Char *name,
+ const XML_Char **atts);
+
+typedef void (XMLCALL *XML_EndElementHandler) (void *userData,
+ const XML_Char *name);
+
+
+/* s is not 0 terminated. */
+typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData,
+ const XML_Char *s,
+ int len);
+
+/* target and data are 0 terminated */
+typedef void (XMLCALL *XML_ProcessingInstructionHandler) (
+ void *userData,
+ const XML_Char *target,
+ const XML_Char *data);
+
+/* data is 0 terminated */
+typedef void (XMLCALL *XML_CommentHandler) (void *userData,
+ const XML_Char *data);
+
+typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData);
+typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData);
+
+/* This is called for any characters in the XML document for which
+ there is no applicable handler. This includes both characters that
+ are part of markup which is of a kind that is not reported
+ (comments, markup declarations), or characters that are part of a
+ construct which could be reported but for which no handler has been
+ supplied. The characters are passed exactly as they were in the XML
+ document except that they will be encoded in UTF-8 or UTF-16.
+ Line boundaries are not normalized. Note that a byte order mark
+ character is not passed to the default handler. There are no
+ guarantees about how characters are divided between calls to the
+ default handler: for example, a comment might be split between
+ multiple calls.
+*/
+typedef void (XMLCALL *XML_DefaultHandler) (void *userData,
+ const XML_Char *s,
+ int len);
+
+/* This is called for the start of the DOCTYPE declaration, before
+ any DTD or internal subset is parsed.
+*/
+typedef void (XMLCALL *XML_StartDoctypeDeclHandler) (
+ void *userData,
+ const XML_Char *doctypeName,
+ const XML_Char *sysid,
+ const XML_Char *pubid,
+ int has_internal_subset);
+
+/* This is called for the start of the DOCTYPE declaration when the
+ closing > is encountered, but after processing any external
+ subset.
+*/
+typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData);
+
+/* This is called for entity declarations. The is_parameter_entity
+ argument will be non-zero if the entity is a parameter entity, zero
+ otherwise.
+
+ For internal entities (<!ENTITY foo "bar">), value will
+ be non-NULL and systemId, publicID, and notationName will be NULL.
+ The value string is NOT nul-terminated; the length is provided in
+ the value_length argument. Since it is legal to have zero-length
+ values, do not use this argument to test for internal entities.
+
+ For external entities, value will be NULL and systemId will be
+ non-NULL. The publicId argument will be NULL unless a public
+ identifier was provided. The notationName argument will have a
+ non-NULL value only for unparsed entity declarations.
+
+ Note that is_parameter_entity can't be changed to XML_Bool, since
+ that would break binary compatibility.
+*/
+typedef void (XMLCALL *XML_EntityDeclHandler) (
+ void *userData,
+ const XML_Char *entityName,
+ int is_parameter_entity,
+ const XML_Char *value,
+ int value_length,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName);
+
+XMLPARSEAPI(void)
+XML_SetEntityDeclHandler(XML_Parser parser,
+ XML_EntityDeclHandler handler);
+
+/* OBSOLETE -- OBSOLETE -- OBSOLETE
+ This handler has been superceded by the EntityDeclHandler above.
+ It is provided here for backward compatibility.
+
+ This is called for a declaration of an unparsed (NDATA) entity.
+ The base argument is whatever was set by XML_SetBase. The
+ entityName, systemId and notationName arguments will never be
+ NULL. The other arguments may be.
+*/
+typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) (
+ void *userData,
+ const XML_Char *entityName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName);
+
+/* This is called for a declaration of notation. The base argument is
+ whatever was set by XML_SetBase. The notationName will never be
+ NULL. The other arguments can be.
+*/
+typedef void (XMLCALL *XML_NotationDeclHandler) (
+ void *userData,
+ const XML_Char *notationName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+
+/* When namespace processing is enabled, these are called once for
+ each namespace declaration. The call to the start and end element
+ handlers occur between the calls to the start and end namespace
+ declaration handlers. For an xmlns attribute, prefix will be
+ NULL. For an xmlns="" attribute, uri will be NULL.
+*/
+typedef void (XMLCALL *XML_StartNamespaceDeclHandler) (
+ void *userData,
+ const XML_Char *prefix,
+ const XML_Char *uri);
+
+typedef void (XMLCALL *XML_EndNamespaceDeclHandler) (
+ void *userData,
+ const XML_Char *prefix);
+
+/* This is called if the document is not standalone, that is, it has an
+ external subset or a reference to a parameter entity, but does not
+ have standalone="yes". If this handler returns XML_STATUS_ERROR,
+ then processing will not continue, and the parser will return a
+ XML_ERROR_NOT_STANDALONE error.
+ If parameter entity parsing is enabled, then in addition to the
+ conditions above this handler will only be called if the referenced
+ entity was actually read.
+*/
+typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData);
+
+/* This is called for a reference to an external parsed general
+ entity. The referenced entity is not automatically parsed. The
+ application can parse it immediately or later using
+ XML_ExternalEntityParserCreate.
+
+ The parser argument is the parser parsing the entity containing the
+ reference; it can be passed as the parser argument to
+ XML_ExternalEntityParserCreate. The systemId argument is the
+ system identifier as specified in the entity declaration; it will
+ not be NULL.
+
+ The base argument is the system identifier that should be used as
+ the base for resolving systemId if systemId was relative; this is
+ set by XML_SetBase; it may be NULL.
+
+ The publicId argument is the public identifier as specified in the
+ entity declaration, or NULL if none was specified; the whitespace
+ in the public identifier will have been normalized as required by
+ the XML spec.
+
+ The context argument specifies the parsing context in the format
+ expected by the context argument to XML_ExternalEntityParserCreate;
+ context is valid only until the handler returns, so if the
+ referenced entity is to be parsed later, it must be copied.
+ context is NULL only when the entity is a parameter entity.
+
+ The handler should return XML_STATUS_ERROR if processing should not
+ continue because of a fatal error in the handling of the external
+ entity. In this case the calling parser will return an
+ XML_ERROR_EXTERNAL_ENTITY_HANDLING error.
+
+ Note that unlike other handlers the first argument is the parser,
+ not userData.
+*/
+typedef int (XMLCALL *XML_ExternalEntityRefHandler) (
+ XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+
+/* This is called in two situations:
+ 1) An entity reference is encountered for which no declaration
+ has been read *and* this is not an error.
+ 2) An internal entity reference is read, but not expanded, because
+ XML_SetDefaultHandler has been called.
+ Note: skipped parameter entities in declarations and skipped general
+ entities in attribute values cannot be reported, because
+ the event would be out of sync with the reporting of the
+ declarations or attribute values
+*/
+typedef void (XMLCALL *XML_SkippedEntityHandler) (
+ void *userData,
+ const XML_Char *entityName,
+ int is_parameter_entity);
+
+/* This structure is filled in by the XML_UnknownEncodingHandler to
+ provide information to the parser about encodings that are unknown
+ to the parser.
+
+ The map[b] member gives information about byte sequences whose
+ first byte is b.
+
+ If map[b] is c where c is >= 0, then b by itself encodes the
+ Unicode scalar value c.
+
+ If map[b] is -1, then the byte sequence is malformed.
+
+ If map[b] is -n, where n >= 2, then b is the first byte of an
+ n-byte sequence that encodes a single Unicode scalar value.
+
+ The data member will be passed as the first argument to the convert
+ function.
+
+ The convert function is used to convert multibyte sequences; s will
+ point to a n-byte sequence where map[(unsigned char)*s] == -n. The
+ convert function must return the Unicode scalar value represented
+ by this byte sequence or -1 if the byte sequence is malformed.
+
+ The convert function may be NULL if the encoding is a single-byte
+ encoding, that is if map[b] >= -1 for all bytes b.
+
+ When the parser is finished with the encoding, then if release is
+ not NULL, it will call release passing it the data member; once
+ release has been called, the convert function will not be called
+ again.
+
+ Expat places certain restrictions on the encodings that are supported
+ using this mechanism.
+
+ 1. Every ASCII character that can appear in a well-formed XML document,
+ other than the characters
+
+ $@\^`{}~
+
+ must be represented by a single byte, and that byte must be the
+ same byte that represents that character in ASCII.
+
+ 2. No character may require more than 4 bytes to encode.
+
+ 3. All characters encoded must have Unicode scalar values <=
+ 0xFFFF, (i.e., characters that would be encoded by surrogates in
+ UTF-16 are not allowed). Note that this restriction doesn't
+ apply to the built-in support for UTF-8 and UTF-16.
+
+ 4. No Unicode character may be encoded by more than one distinct
+ sequence of bytes.
+*/
+typedef struct {
+ int map[256];
+ void *data;
+ int (XMLCALL *convert)(void *data, const char *s);
+ void (XMLCALL *release)(void *data);
+} XML_Encoding;
+
+/* This is called for an encoding that is unknown to the parser.
+
+ The encodingHandlerData argument is that which was passed as the
+ second argument to XML_SetUnknownEncodingHandler.
+
+ The name argument gives the name of the encoding as specified in
+ the encoding declaration.
+
+ If the callback can provide information about the encoding, it must
+ fill in the XML_Encoding structure, and return XML_STATUS_OK.
+ Otherwise it must return XML_STATUS_ERROR.
+
+ If info does not describe a suitable encoding, then the parser will
+ return an XML_UNKNOWN_ENCODING error.
+*/
+typedef int (XMLCALL *XML_UnknownEncodingHandler) (
+ void *encodingHandlerData,
+ const XML_Char *name,
+ XML_Encoding *info);
+
+XMLPARSEAPI(void)
+XML_SetElementHandler(XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartElementHandler(XML_Parser parser,
+ XML_StartElementHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetEndElementHandler(XML_Parser parser,
+ XML_EndElementHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetCharacterDataHandler(XML_Parser parser,
+ XML_CharacterDataHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+ XML_ProcessingInstructionHandler handler);
+XMLPARSEAPI(void)
+XML_SetCommentHandler(XML_Parser parser,
+ XML_CommentHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetCdataSectionHandler(XML_Parser parser,
+ XML_StartCdataSectionHandler start,
+ XML_EndCdataSectionHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartCdataSectionHandler(XML_Parser parser,
+ XML_StartCdataSectionHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndCdataSectionHandler(XML_Parser parser,
+ XML_EndCdataSectionHandler end);
+
+/* This sets the default handler and also inhibits expansion of
+ internal entities. These entity references will be passed to the
+ default handler, or to the skipped entity handler, if one is set.
+*/
+XMLPARSEAPI(void)
+XML_SetDefaultHandler(XML_Parser parser,
+ XML_DefaultHandler handler);
+
+/* This sets the default handler but does not inhibit expansion of
+ internal entities. The entity reference will not be passed to the
+ default handler.
+*/
+XMLPARSEAPI(void)
+XML_SetDefaultHandlerExpand(XML_Parser parser,
+ XML_DefaultHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetDoctypeDeclHandler(XML_Parser parser,
+ XML_StartDoctypeDeclHandler start,
+ XML_EndDoctypeDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartDoctypeDeclHandler(XML_Parser parser,
+ XML_StartDoctypeDeclHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndDoctypeDeclHandler(XML_Parser parser,
+ XML_EndDoctypeDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+ XML_UnparsedEntityDeclHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetNotationDeclHandler(XML_Parser parser,
+ XML_NotationDeclHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+ XML_StartNamespaceDeclHandler start,
+ XML_EndNamespaceDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartNamespaceDeclHandler(XML_Parser parser,
+ XML_StartNamespaceDeclHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndNamespaceDeclHandler(XML_Parser parser,
+ XML_EndNamespaceDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetNotStandaloneHandler(XML_Parser parser,
+ XML_NotStandaloneHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+ XML_ExternalEntityRefHandler handler);
+
+/* If a non-NULL value for arg is specified here, then it will be
+ passed as the first argument to the external entity ref handler
+ instead of the parser object.
+*/
+XMLPARSEAPI(void)
+XML_SetExternalEntityRefHandlerArg(XML_Parser parser,
+ void *arg);
+
+XMLPARSEAPI(void)
+XML_SetSkippedEntityHandler(XML_Parser parser,
+ XML_SkippedEntityHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+ XML_UnknownEncodingHandler handler,
+ void *encodingHandlerData);
+
+/* This can be called within a handler for a start element, end
+ element, processing instruction or character data. It causes the
+ corresponding markup to be passed to the default handler.
+*/
+XMLPARSEAPI(void)
+XML_DefaultCurrent(XML_Parser parser);
+
+/* If do_nst is non-zero, and namespace processing is in effect, and
+ a name has a prefix (i.e. an explicit namespace qualifier) then
+ that name is returned as a triplet in a single string separated by
+ the separator character specified when the parser was created: URI
+ + sep + local_name + sep + prefix.
+
+ If do_nst is zero, then namespace information is returned in the
+ default manner (URI + sep + local_name) whether or not the name
+ has a prefix.
+
+ Note: Calling XML_SetReturnNSTriplet after XML_Parse or
+ XML_ParseBuffer has no effect.
+*/
+
+XMLPARSEAPI(void)
+XML_SetReturnNSTriplet(XML_Parser parser, int do_nst);
+
+/* This value is passed as the userData argument to callbacks. */
+XMLPARSEAPI(void)
+XML_SetUserData(XML_Parser parser, void *userData);
+
+/* Returns the last value set by XML_SetUserData or NULL. */
+#define XML_GetUserData(parser) (*(void **)(parser))
+
+/* This is equivalent to supplying an encoding argument to
+ XML_ParserCreate. On success XML_SetEncoding returns non-zero,
+ zero otherwise.
+ Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer
+ has no effect and returns XML_STATUS_ERROR.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_SetEncoding(XML_Parser parser, const XML_Char *encoding);
+
+/* If this function is called, then the parser will be passed as the
+ first argument to callbacks instead of userData. The userData will
+ still be accessible using XML_GetUserData.
+*/
+XMLPARSEAPI(void)
+XML_UseParserAsHandlerArg(XML_Parser parser);
+
+/* If useDTD == XML_TRUE is passed to this function, then the parser
+ will assume that there is an external subset, even if none is
+ specified in the document. In such a case the parser will call the
+ externalEntityRefHandler with a value of NULL for the systemId
+ argument (the publicId and context arguments will be NULL as well).
+ Note: For the purpose of checking WFC: Entity Declared, passing
+ useDTD == XML_TRUE will make the parser behave as if the document
+ had a DTD with an external subset.
+ Note: If this function is called, then this must be done before
+ the first call to XML_Parse or XML_ParseBuffer, since it will
+ have no effect after that. Returns
+ XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING.
+ Note: If the document does not have a DOCTYPE declaration at all,
+ then startDoctypeDeclHandler and endDoctypeDeclHandler will not
+ be called, despite an external subset being parsed.
+ Note: If XML_DTD is not defined when Expat is compiled, returns
+ XML_ERROR_FEATURE_REQUIRES_XML_DTD.
+*/
+XMLPARSEAPI(enum XML_Error)
+XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD);
+
+
+/* Sets the base to be used for resolving relative URIs in system
+ identifiers in declarations. Resolving relative identifiers is
+ left to the application: this value will be passed through as the
+ base argument to the XML_ExternalEntityRefHandler,
+ XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base
+ argument will be copied. Returns XML_STATUS_ERROR if out of memory,
+ XML_STATUS_OK otherwise.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_SetBase(XML_Parser parser, const XML_Char *base);
+
+XMLPARSEAPI(const XML_Char *)
+XML_GetBase(XML_Parser parser);
+
+/* Returns the number of the attribute/value pairs passed in last call
+ to the XML_StartElementHandler that were specified in the start-tag
+ rather than defaulted. Each attribute/value pair counts as 2; thus
+ this correspondds to an index into the atts array passed to the
+ XML_StartElementHandler.
+*/
+XMLPARSEAPI(int)
+XML_GetSpecifiedAttributeCount(XML_Parser parser);
+
+/* Returns the index of the ID attribute passed in the last call to
+ XML_StartElementHandler, or -1 if there is no ID attribute. Each
+ attribute/value pair counts as 2; thus this correspondds to an
+ index into the atts array passed to the XML_StartElementHandler.
+*/
+XMLPARSEAPI(int)
+XML_GetIdAttributeIndex(XML_Parser parser);
+
+/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is
+ detected. The last call to XML_Parse must have isFinal true; len
+ may be zero for this call (or any other).
+
+ Though the return values for these functions has always been
+ described as a Boolean value, the implementation, at least for the
+ 1.95.x series, has always returned exactly one of the XML_Status
+ values.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal);
+
+XMLPARSEAPI(void *)
+XML_GetBuffer(XML_Parser parser, int len);
+
+XMLPARSEAPI(enum XML_Status)
+XML_ParseBuffer(XML_Parser parser, int len, int isFinal);
+
+/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return.
+ Must be called from within a call-back handler, except when aborting
+ (resumable = 0) an already suspended parser. Some call-backs may
+ still follow because they would otherwise get lost. Examples:
+ - endElementHandler() for empty elements when stopped in
+ startElementHandler(),
+ - endNameSpaceDeclHandler() when stopped in endElementHandler(),
+ and possibly others.
+
+ Can be called from most handlers, including DTD related call-backs,
+ except when parsing an external parameter entity and resumable != 0.
+ Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise.
+ Possible error codes:
+ - XML_ERROR_SUSPENDED: when suspending an already suspended parser.
+ - XML_ERROR_FINISHED: when the parser has already finished.
+ - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE.
+
+ When resumable != 0 (true) then parsing is suspended, that is,
+ XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED.
+ Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer()
+ return XML_STATUS_ERROR with error code XML_ERROR_ABORTED.
+
+ *Note*:
+ This will be applied to the current parser instance only, that is, if
+ there is a parent parser then it will continue parsing when the
+ externalEntityRefHandler() returns. It is up to the implementation of
+ the externalEntityRefHandler() to call XML_StopParser() on the parent
+ parser (recursively), if one wants to stop parsing altogether.
+
+ When suspended, parsing can be resumed by calling XML_ResumeParser().
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_StopParser(XML_Parser parser, XML_Bool resumable);
+
+/* Resumes parsing after it has been suspended with XML_StopParser().
+ Must not be called from within a handler call-back. Returns same
+ status codes as XML_Parse() or XML_ParseBuffer().
+ Additional error code XML_ERROR_NOT_SUSPENDED possible.
+
+ *Note*:
+ This must be called on the most deeply nested child parser instance
+ first, and on its parent parser only after the child parser has finished,
+ to be applied recursively until the document entity's parser is restarted.
+ That is, the parent parser will not resume by itself and it is up to the
+ application to call XML_ResumeParser() on it at the appropriate moment.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_ResumeParser(XML_Parser parser);
+
+enum XML_Parsing {
+ XML_INITIALIZED,
+ XML_PARSING,
+ XML_FINISHED,
+ XML_SUSPENDED
+};
+
+typedef struct {
+ enum XML_Parsing parsing;
+ XML_Bool finalBuffer;
+} XML_ParsingStatus;
+
+/* Returns status of parser with respect to being initialized, parsing,
+ finished, or suspended and processing the final buffer.
+ XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus,
+ XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED
+*/
+XMLPARSEAPI(void)
+XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status);
+
+/* Creates an XML_Parser object that can parse an external general
+ entity; context is a '\0'-terminated string specifying the parse
+ context; encoding is a '\0'-terminated string giving the name of
+ the externally specified encoding, or NULL if there is no
+ externally specified encoding. The context string consists of a
+ sequence of tokens separated by formfeeds (\f); a token consisting
+ of a name specifies that the general entity of the name is open; a
+ token of the form prefix=uri specifies the namespace for a
+ particular prefix; a token of the form =uri specifies the default
+ namespace. This can be called at any point after the first call to
+ an ExternalEntityRefHandler so longer as the parser has not yet
+ been freed. The new parser is completely independent and may
+ safely be used in a separate thread. The handlers and userData are
+ initialized from the parser argument. Returns NULL if out of memory.
+ Otherwise returns a new XML_Parser object.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ExternalEntityParserCreate(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *encoding);
+
+enum XML_ParamEntityParsing {
+ XML_PARAM_ENTITY_PARSING_NEVER,
+ XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE,
+ XML_PARAM_ENTITY_PARSING_ALWAYS
+};
+
+/* Controls parsing of parameter entities (including the external DTD
+ subset). If parsing of parameter entities is enabled, then
+ references to external parameter entities (including the external
+ DTD subset) will be passed to the handler set with
+ XML_SetExternalEntityRefHandler. The context passed will be 0.
+
+ Unlike external general entities, external parameter entities can
+ only be parsed synchronously. If the external parameter entity is
+ to be parsed, it must be parsed during the call to the external
+ entity ref handler: the complete sequence of
+ XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and
+ XML_ParserFree calls must be made during this call. After
+ XML_ExternalEntityParserCreate has been called to create the parser
+ for the external parameter entity (context must be 0 for this
+ call), it is illegal to make any calls on the old parser until
+ XML_ParserFree has been called on the newly created parser.
+ If the library has been compiled without support for parameter
+ entity parsing (ie without XML_DTD being defined), then
+ XML_SetParamEntityParsing will return 0 if parsing of parameter
+ entities is requested; otherwise it will return non-zero.
+ Note: If XML_SetParamEntityParsing is called after XML_Parse or
+ XML_ParseBuffer, then it has no effect and will always return 0.
+*/
+XMLPARSEAPI(int)
+XML_SetParamEntityParsing(XML_Parser parser,
+ enum XML_ParamEntityParsing parsing);
+
+/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then
+ XML_GetErrorCode returns information about the error.
+*/
+XMLPARSEAPI(enum XML_Error)
+XML_GetErrorCode(XML_Parser parser);
+
+/* These functions return information about the current parse
+ location. They may be called from any callback called to report
+ some parse event; in this case the location is the location of the
+ first of the sequence of characters that generated the event. When
+ called from callbacks generated by declarations in the document
+ prologue, the location identified isn't as neatly defined, but will
+ be within the relevant markup. When called outside of the callback
+ functions, the position indicated will be just past the last parse
+ event (regardless of whether there was an associated callback).
+
+ They may also be called after returning from a call to XML_Parse
+ or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then
+ the location is the location of the character at which the error
+ was detected; otherwise the location is the location of the last
+ parse event, as described above.
+*/
+XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser);
+XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser);
+XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser);
+
+/* Return the number of bytes in the current event.
+ Returns 0 if the event is in an internal entity.
+*/
+XMLPARSEAPI(int)
+XML_GetCurrentByteCount(XML_Parser parser);
+
+/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets
+ the integer pointed to by offset to the offset within this buffer
+ of the current parse position, and sets the integer pointed to by size
+ to the size of this buffer (the number of input bytes). Otherwise
+ returns a NULL pointer. Also returns a NULL pointer if a parse isn't
+ active.
+
+ NOTE: The character pointer returned should not be used outside
+ the handler that makes the call.
+*/
+XMLPARSEAPI(const char *)
+XML_GetInputContext(XML_Parser parser,
+ int *offset,
+ int *size);
+
+/* For backwards compatibility with previous versions. */
+#define XML_GetErrorLineNumber XML_GetCurrentLineNumber
+#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber
+#define XML_GetErrorByteIndex XML_GetCurrentByteIndex
+
+/* Frees the content model passed to the element declaration handler */
+XMLPARSEAPI(void)
+XML_FreeContentModel(XML_Parser parser, XML_Content *model);
+
+/* Exposing the memory handling functions used in Expat */
+XMLPARSEAPI(void *)
+XML_MemMalloc(XML_Parser parser, size_t size);
+
+XMLPARSEAPI(void *)
+XML_MemRealloc(XML_Parser parser, void *ptr, size_t size);
+
+XMLPARSEAPI(void)
+XML_MemFree(XML_Parser parser, void *ptr);
+
+/* Frees memory used by the parser. */
+XMLPARSEAPI(void)
+XML_ParserFree(XML_Parser parser);
+
+/* Returns a string describing the error. */
+XMLPARSEAPI(const XML_LChar *)
+XML_ErrorString(enum XML_Error code);
+
+/* Return a string containing the version number of this expat */
+XMLPARSEAPI(const XML_LChar *)
+XML_ExpatVersion(void);
+
+typedef struct {
+ int major;
+ int minor;
+ int micro;
+} XML_Expat_Version;
+
+/* Return an XML_Expat_Version structure containing numeric version
+ number information for this version of expat.
+*/
+XMLPARSEAPI(XML_Expat_Version)
+XML_ExpatVersionInfo(void);
+
+/* Added in Expat 1.95.5. */
+enum XML_FeatureEnum {
+ XML_FEATURE_END = 0,
+ XML_FEATURE_UNICODE,
+ XML_FEATURE_UNICODE_WCHAR_T,
+ XML_FEATURE_DTD,
+ XML_FEATURE_CONTEXT_BYTES,
+ XML_FEATURE_MIN_SIZE,
+ XML_FEATURE_SIZEOF_XML_CHAR,
+ XML_FEATURE_SIZEOF_XML_LCHAR,
+ XML_FEATURE_NS,
+ XML_FEATURE_LARGE_SIZE
+ /* Additional features must be added to the end of this enum. */
+};
+
+typedef struct {
+ enum XML_FeatureEnum feature;
+ const XML_LChar *name;
+ long int value;
+} XML_Feature;
+
+XMLPARSEAPI(const XML_Feature *)
+XML_GetFeatureList(void);
+
+
+/* Expat follows the GNU/Linux convention of odd number minor version for
+ beta/development releases and even number minor version for stable
+ releases. Micro is bumped with each release, and set to 0 with each
+ change to major or minor version.
+*/
+#define XML_MAJOR_VERSION 2
+#define XML_MINOR_VERSION 0
+#define XML_MICRO_VERSION 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not Expat_INCLUDED */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_config.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_config.h
new file mode 100644
index 0000000..72bdec2
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_config.h
@@ -0,0 +1,104 @@
+// No copyright notice; this file based on autogenerated header
+
+#ifndef THIRD_PARTY_EXPAT_V2_0_1_SOURCE_LIB_EXPAT_CONFIG_H__
+#define THIRD_PARTY_EXPAT_V2_0_1_SOURCE_LIB_EXPAT_CONFIG_H__
+
+/* expat_config.h. Generated by configure. */
+/* expat_config.h.in. Generated from configure.in by autoheader. */
+
+/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */
+#if defined __BIG_ENDIAN__ // OSX and PPC => big endian; predefined gcc macro
+#define BYTEORDER 4321
+#else
+#define BYTEORDER 1234
+#endif
+
+/* Define to 1 if you have the `bcopy' function. */
+#define HAVE_BCOPY 1
+
+/* Define to 1 if you have the <check.h> header file. */
+/* #undef HAVE_CHECK_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have a working `mmap' system call. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "expat-bugs@mail.libexpat.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "expat"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "expat 1.95.8"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "expat"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.95.8"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* whether byteorder is bigendian */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define to specify how much context to retain around the current parse
+ point. */
+#define XML_CONTEXT_BYTES 1024
+
+/* Define to make parameter entity parsing functionality available. */
+#define XML_DTD 1
+
+/* Define to make XML Namespaces functionality available. */
+#define XML_NS 1
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+#endif // THIRD_PARTY_EXPAT_V2_0_1_SOURCE_LIB_EXPAT_CONFIG_H__
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_external.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_external.h
new file mode 100644
index 0000000..2c03284
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_external.h
@@ -0,0 +1,115 @@
+/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+#ifndef Expat_External_INCLUDED
+#define Expat_External_INCLUDED 1
+
+/* External API definitions */
+
+#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__)
+#define XML_USE_MSC_EXTENSIONS 1
+#endif
+
+/* Expat tries very hard to make the API boundary very specifically
+ defined. There are two macros defined to control this boundary;
+ each of these can be defined before including this header to
+ achieve some different behavior, but doing so it not recommended or
+ tested frequently.
+
+ XMLCALL - The calling convention to use for all calls across the
+ "library boundary." This will default to cdecl, and
+ try really hard to tell the compiler that's what we
+ want.
+
+ XMLIMPORT - Whatever magic is needed to note that a function is
+ to be imported from a dynamically loaded library
+ (.dll, .so, or .sl, depending on your platform).
+
+ The XMLCALL macro was added in Expat 1.95.7. The only one which is
+ expected to be directly useful in client code is XMLCALL.
+
+ Note that on at least some Unix versions, the Expat library must be
+ compiled with the cdecl calling convention as the default since
+ system headers may assume the cdecl convention.
+*/
+#ifndef XMLCALL
+#if defined(_MSC_VER)
+#define XMLCALL __cdecl
+#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER)
+#define XMLCALL __attribute__((cdecl))
+#else
+/* For any platform which uses this definition and supports more than
+ one calling convention, we need to extend this definition to
+ declare the convention used on that platform, if it's possible to
+ do so.
+
+ If this is the case for your platform, please file a bug report
+ with information on how to identify your platform via the C
+ pre-processor and how to specify the same calling convention as the
+ platform's malloc() implementation.
+*/
+#define XMLCALL
+#endif
+#endif /* not defined XMLCALL */
+
+
+#if !defined(XML_STATIC) && !defined(XMLIMPORT)
+#ifndef XML_BUILDING_EXPAT
+/* using Expat from an application */
+
+#ifdef XML_USE_MSC_EXTENSIONS
+#define XMLIMPORT __declspec(dllimport)
+#endif
+
+#endif
+#endif /* not defined XML_STATIC */
+
+
+/* If we didn't define it above, define it away: */
+#ifndef XMLIMPORT
+#define XMLIMPORT
+#endif
+
+
+#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef XML_UNICODE_WCHAR_T
+#define XML_UNICODE
+#endif
+
+#ifdef XML_UNICODE /* Information is UTF-16 encoded. */
+#ifdef XML_UNICODE_WCHAR_T
+typedef wchar_t XML_Char;
+typedef wchar_t XML_LChar;
+#else
+typedef unsigned short XML_Char;
+typedef char XML_LChar;
+#endif /* XML_UNICODE_WCHAR_T */
+#else /* Information is UTF-8 encoded. */
+typedef char XML_Char;
+typedef char XML_LChar;
+#endif /* XML_UNICODE */
+
+#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */
+#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400
+typedef __int64 XML_Index;
+typedef unsigned __int64 XML_Size;
+#else
+typedef long long XML_Index;
+typedef unsigned long long XML_Size;
+#endif
+#else
+typedef long XML_Index;
+typedef unsigned long XML_Size;
+#endif /* XML_LARGE_SIZE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not Expat_External_INCLUDED */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_static.dsp b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_static.dsp
new file mode 100644
index 0000000..a67897b
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expat_static.dsp
@@ -0,0 +1,162 @@
+# Microsoft Developer Studio Project File - Name="expat_static" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=expat_static - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "expat_static.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "expat_static.mak" CFG="expat_static - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "expat_static - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "expat_static - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "expat_static - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "expat_static___Win32_Release"
+# PROP BASE Intermediate_Dir "expat_static___Win32_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\win32\bin\Release"
+# PROP Intermediate_Dir "..\win32\tmp\Release_static"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "_WINDOWS" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "COMPILED_FROM_DSP" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x1009 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"..\win32\bin\Release/libexpatMT.lib"
+
+!ELSEIF "$(CFG)" == "expat_static - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "expat_static___Win32_Debug"
+# PROP BASE Intermediate_Dir "expat_static___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\win32\bin\Debug"
+# PROP Intermediate_Dir "..\win32\tmp\Debug_static"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "COMPILED_FROM_DSP" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x1009 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"..\win32\bin\Debug\libexpatMT.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "expat_static - Win32 Release"
+# Name "expat_static - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\xmlparse.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlrole.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_impl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_ns.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ascii.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\asciitab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expat_external.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\iasciitab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\latin1tab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\nametab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8tab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlrole.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_impl.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expatw.dsp b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expatw.dsp
new file mode 100644
index 0000000..46db7d6
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expatw.dsp
@@ -0,0 +1,185 @@
+# Microsoft Developer Studio Project File - Name="expatw" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=expatw - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "expatw.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "expatw.mak" CFG="expatw - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "expatw - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "expatw - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "expatw - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\win32\bin\Release"
+# PROP Intermediate_Dir "..\win32\tmp\Release-w"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXPAT_EXPORTS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "COMPILED_FROM_DSP" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XML_UNICODE_WCHAR_T" /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /machine:I386 /out:"..\win32\bin\Release/libexpatw.dll"
+
+!ELSEIF "$(CFG)" == "expatw - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\win32\bin\Debug"
+# PROP Intermediate_Dir "..\win32\tmp\Debug-w"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXPAT_EXPORTS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "_DEBUG" /D "COMPILED_FROM_DSP" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XML_UNICODE_WCHAR_T" /FR /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /debug /machine:I386 /out:"..\win32\bin\Debug/libexpatw.dll"
+
+!ENDIF
+
+# Begin Target
+
+# Name "expatw - Win32 Release"
+# Name "expatw - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\libexpatw.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlparse.c
+
+!IF "$(CFG)" == "expatw - Win32 Release"
+
+!ELSEIF "$(CFG)" == "expatw - Win32 Debug"
+
+# ADD CPP /GX- /Od
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlrole.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_impl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_ns.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ascii.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\asciitab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expat_external.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\iasciitab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\latin1tab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\nametab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8tab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlrole.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_impl.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expatw_static.dsp b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expatw_static.dsp
new file mode 100644
index 0000000..d28632c
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/expatw_static.dsp
@@ -0,0 +1,162 @@
+# Microsoft Developer Studio Project File - Name="expatw_static" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=expatw_static - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "expatw_static.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "expatw_static.mak" CFG="expatw_static - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "expatw_static - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "expatw_static - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "expatw_static - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "expatw_static___Win32_Release"
+# PROP BASE Intermediate_Dir "expatw_static___Win32_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\win32\bin\Release"
+# PROP Intermediate_Dir "..\win32\tmp\Release-w_static"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "_WINDOWS" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "COMPILED_FROM_DSP" /D "XML_UNICODE_WCHAR_T" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x1009 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"..\win32\bin\Release\libexpatwMT.lib"
+
+!ELSEIF "$(CFG)" == "expatw_static - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "expatw_static___Win32_Debug"
+# PROP BASE Intermediate_Dir "expatw_static___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\win32\bin\Debug"
+# PROP Intermediate_Dir "..\win32\tmp\Debug-w_static"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_LIB" /D "COMPILED_FROM_DSP" /D "XML_UNICODE_WCHAR_T" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x1009 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"..\win32\bin\Debug\libexpatwMT.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "expatw_static - Win32 Release"
+# Name "expatw_static - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\xmlparse.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlrole.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_impl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_ns.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ascii.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\asciitab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expat_external.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\iasciitab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\latin1tab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\nametab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8tab.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmlrole.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\xmltok_impl.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/iasciitab.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/iasciitab.h
new file mode 100644
index 0000000..24a1d5c
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/iasciitab.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/internal.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/internal.h
new file mode 100644
index 0000000..dd54548
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/internal.h
@@ -0,0 +1,73 @@
+/* internal.h
+
+ Internal definitions used by Expat. This is not needed to compile
+ client code.
+
+ The following calling convention macros are defined for frequently
+ called functions:
+
+ FASTCALL - Used for those internal functions that have a simple
+ body and a low number of arguments and local variables.
+
+ PTRCALL - Used for functions called though function pointers.
+
+ PTRFASTCALL - Like PTRCALL, but for low number of arguments.
+
+ inline - Used for selected internal functions for which inlining
+ may improve performance on some platforms.
+
+ Note: Use of these macros is based on judgement, not hard rules,
+ and therefore subject to change.
+*/
+
+#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__)
+/* We'll use this version by default only where we know it helps.
+
+ regparm() generates warnings on Solaris boxes. See SF bug #692878.
+
+ Instability reported with egcs on a RedHat Linux 7.3.
+ Let's comment out:
+ #define FASTCALL __attribute__((stdcall, regparm(3)))
+ and let's try this:
+*/
+#define FASTCALL __attribute__((regparm(3)))
+#define PTRFASTCALL __attribute__((regparm(3)))
+#endif
+
+/* Using __fastcall seems to have an unexpected negative effect under
+ MS VC++, especially for function pointers, so we won't use it for
+ now on that platform. It may be reconsidered for a future release
+ if it can be made more effective.
+ Likely reason: __fastcall on Windows is like stdcall, therefore
+ the compiler cannot perform stack optimizations for call clusters.
+*/
+
+/* Make sure all of these are defined if they aren't already. */
+
+#ifndef FASTCALL
+#define FASTCALL
+#endif
+
+#ifndef PTRCALL
+#define PTRCALL
+#endif
+
+#ifndef PTRFASTCALL
+#define PTRFASTCALL
+#endif
+
+#ifndef XML_MIN_SIZE
+#if !defined(__cplusplus) && !defined(inline)
+#ifdef __GNUC__
+#define inline __inline
+#endif /* __GNUC__ */
+#endif
+#endif /* XML_MIN_SIZE */
+
+#ifdef __cplusplus
+#define inline inline
+#else
+#ifndef inline
+#define inline
+#endif
+#endif
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/latin1tab.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/latin1tab.h
new file mode 100644
index 0000000..53c25d7
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/latin1tab.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
+/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/libexpat.def b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/libexpat.def
new file mode 100644
index 0000000..3920bbc
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/libexpat.def
@@ -0,0 +1,73 @@
+; DEF file for MS VC++
+
+LIBRARY
+EXPORTS
+ XML_DefaultCurrent @1
+ XML_ErrorString @2
+ XML_ExpatVersion @3
+ XML_ExpatVersionInfo @4
+ XML_ExternalEntityParserCreate @5
+ XML_GetBase @6
+ XML_GetBuffer @7
+ XML_GetCurrentByteCount @8
+ XML_GetCurrentByteIndex @9
+ XML_GetCurrentColumnNumber @10
+ XML_GetCurrentLineNumber @11
+ XML_GetErrorCode @12
+ XML_GetIdAttributeIndex @13
+ XML_GetInputContext @14
+ XML_GetSpecifiedAttributeCount @15
+ XML_Parse @16
+ XML_ParseBuffer @17
+ XML_ParserCreate @18
+ XML_ParserCreateNS @19
+ XML_ParserCreate_MM @20
+ XML_ParserFree @21
+ XML_SetAttlistDeclHandler @22
+ XML_SetBase @23
+ XML_SetCdataSectionHandler @24
+ XML_SetCharacterDataHandler @25
+ XML_SetCommentHandler @26
+ XML_SetDefaultHandler @27
+ XML_SetDefaultHandlerExpand @28
+ XML_SetDoctypeDeclHandler @29
+ XML_SetElementDeclHandler @30
+ XML_SetElementHandler @31
+ XML_SetEncoding @32
+ XML_SetEndCdataSectionHandler @33
+ XML_SetEndDoctypeDeclHandler @34
+ XML_SetEndElementHandler @35
+ XML_SetEndNamespaceDeclHandler @36
+ XML_SetEntityDeclHandler @37
+ XML_SetExternalEntityRefHandler @38
+ XML_SetExternalEntityRefHandlerArg @39
+ XML_SetNamespaceDeclHandler @40
+ XML_SetNotStandaloneHandler @41
+ XML_SetNotationDeclHandler @42
+ XML_SetParamEntityParsing @43
+ XML_SetProcessingInstructionHandler @44
+ XML_SetReturnNSTriplet @45
+ XML_SetStartCdataSectionHandler @46
+ XML_SetStartDoctypeDeclHandler @47
+ XML_SetStartElementHandler @48
+ XML_SetStartNamespaceDeclHandler @49
+ XML_SetUnknownEncodingHandler @50
+ XML_SetUnparsedEntityDeclHandler @51
+ XML_SetUserData @52
+ XML_SetXmlDeclHandler @53
+ XML_UseParserAsHandlerArg @54
+; added with version 1.95.3
+ XML_ParserReset @55
+ XML_SetSkippedEntityHandler @56
+; added with version 1.95.5
+ XML_GetFeatureList @57
+ XML_UseForeignDTD @58
+; added with version 1.95.6
+ XML_FreeContentModel @59
+ XML_MemMalloc @60
+ XML_MemRealloc @61
+ XML_MemFree @62
+; added with version 1.95.8
+ XML_StopParser @63
+ XML_ResumeParser @64
+ XML_GetParsingStatus @65
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/libexpatw.def b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/libexpatw.def
new file mode 100644
index 0000000..3920bbc
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/libexpatw.def
@@ -0,0 +1,73 @@
+; DEF file for MS VC++
+
+LIBRARY
+EXPORTS
+ XML_DefaultCurrent @1
+ XML_ErrorString @2
+ XML_ExpatVersion @3
+ XML_ExpatVersionInfo @4
+ XML_ExternalEntityParserCreate @5
+ XML_GetBase @6
+ XML_GetBuffer @7
+ XML_GetCurrentByteCount @8
+ XML_GetCurrentByteIndex @9
+ XML_GetCurrentColumnNumber @10
+ XML_GetCurrentLineNumber @11
+ XML_GetErrorCode @12
+ XML_GetIdAttributeIndex @13
+ XML_GetInputContext @14
+ XML_GetSpecifiedAttributeCount @15
+ XML_Parse @16
+ XML_ParseBuffer @17
+ XML_ParserCreate @18
+ XML_ParserCreateNS @19
+ XML_ParserCreate_MM @20
+ XML_ParserFree @21
+ XML_SetAttlistDeclHandler @22
+ XML_SetBase @23
+ XML_SetCdataSectionHandler @24
+ XML_SetCharacterDataHandler @25
+ XML_SetCommentHandler @26
+ XML_SetDefaultHandler @27
+ XML_SetDefaultHandlerExpand @28
+ XML_SetDoctypeDeclHandler @29
+ XML_SetElementDeclHandler @30
+ XML_SetElementHandler @31
+ XML_SetEncoding @32
+ XML_SetEndCdataSectionHandler @33
+ XML_SetEndDoctypeDeclHandler @34
+ XML_SetEndElementHandler @35
+ XML_SetEndNamespaceDeclHandler @36
+ XML_SetEntityDeclHandler @37
+ XML_SetExternalEntityRefHandler @38
+ XML_SetExternalEntityRefHandlerArg @39
+ XML_SetNamespaceDeclHandler @40
+ XML_SetNotStandaloneHandler @41
+ XML_SetNotationDeclHandler @42
+ XML_SetParamEntityParsing @43
+ XML_SetProcessingInstructionHandler @44
+ XML_SetReturnNSTriplet @45
+ XML_SetStartCdataSectionHandler @46
+ XML_SetStartDoctypeDeclHandler @47
+ XML_SetStartElementHandler @48
+ XML_SetStartNamespaceDeclHandler @49
+ XML_SetUnknownEncodingHandler @50
+ XML_SetUnparsedEntityDeclHandler @51
+ XML_SetUserData @52
+ XML_SetXmlDeclHandler @53
+ XML_UseParserAsHandlerArg @54
+; added with version 1.95.3
+ XML_ParserReset @55
+ XML_SetSkippedEntityHandler @56
+; added with version 1.95.5
+ XML_GetFeatureList @57
+ XML_UseForeignDTD @58
+; added with version 1.95.6
+ XML_FreeContentModel @59
+ XML_MemMalloc @60
+ XML_MemRealloc @61
+ XML_MemFree @62
+; added with version 1.95.8
+ XML_StopParser @63
+ XML_ResumeParser @64
+ XML_GetParsingStatus @65
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/macconfig.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/macconfig.h
new file mode 100644
index 0000000..2725caa
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/macconfig.h
@@ -0,0 +1,53 @@
+/*================================================================
+** Copyright 2000, Clark Cooper
+** All rights reserved.
+**
+** This is free software. You are permitted to copy, distribute, or modify
+** it under the terms of the MIT/X license (contained in the COPYING file
+** with this distribution.)
+**
+*/
+
+#ifndef MACCONFIG_H
+#define MACCONFIG_H
+
+
+/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */
+#define BYTEORDER 4321
+
+/* Define to 1 if you have the `bcopy' function. */
+#undef HAVE_BCOPY
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE
+
+/* Define to 1 if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* whether byteorder is bigendian */
+#define WORDS_BIGENDIAN
+
+/* Define to specify how much context to retain around the current parse
+ point. */
+#undef XML_CONTEXT_BYTES
+
+/* Define to make parameter entity parsing functionality available. */
+#define XML_DTD
+
+/* Define to make XML Namespaces functionality available. */
+#define XML_NS
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `long' if <sys/types.h> does not define. */
+#define off_t long
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#undef size_t
+
+
+#endif /* ifndef MACCONFIG_H */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/nametab.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/nametab.h
new file mode 100644
index 0000000..b05e62c
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/nametab.h
@@ -0,0 +1,150 @@
+static const unsigned namingBitmap[] = {
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF,
+0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000,
+0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
+0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003,
+0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003,
+0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
+0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001,
+0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003,
+0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000,
+0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003,
+0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003,
+0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000,
+0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000,
+0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF,
+0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB,
+0x40000000, 0xF580C900, 0x00000007, 0x02010800,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF,
+0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
+0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF,
+0x00000000, 0x00004C40, 0x00000000, 0x00000000,
+0x00000007, 0x00000000, 0x00000000, 0x00000000,
+0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF,
+0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000,
+0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003,
+0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF,
+0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF,
+0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
+0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF,
+0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0,
+0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1,
+0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3,
+0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80,
+0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000,
+0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000,
+0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF,
+0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x1FFF0000, 0x00000002,
+0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF,
+};
+static const unsigned char nmstrtPages[] = {
+0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,
+0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+static const unsigned char namePages[] = {
+0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00,
+0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/utf8tab.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/utf8tab.h
new file mode 100644
index 0000000..7bb3e77
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/utf8tab.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+
+/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
+/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/watcomconfig.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/watcomconfig.h
new file mode 100644
index 0000000..2f05e3f
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/watcomconfig.h
@@ -0,0 +1,47 @@
+/* expat_config.h for use with Open Watcom 1.5 and above. */
+
+#ifndef WATCOMCONFIG_H
+#define WATCOMCONFIG_H
+
+#ifdef __NT__
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */
+#define BYTEORDER 1234
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "expat-bugs@mail.libexpat.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "expat"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "expat 2.0.0"
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.0.0"
+
+/* Define to specify how much context to retain around the current parse
+ point. */
+#define XML_CONTEXT_BYTES 1024
+
+/* Define to make parameter entity parsing functionality available. */
+#define XML_DTD 1
+
+/* Define to make XML Namespaces functionality available. */
+#define XML_NS 1
+
+#endif
+
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/winconfig.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/winconfig.h
new file mode 100644
index 0000000..0f16ba5
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/winconfig.h
@@ -0,0 +1,32 @@
+/*================================================================
+** Copyright 2000, Clark Cooper
+** All rights reserved.
+**
+** This is free software. You are permitted to copy, distribute, or modify
+** it under the terms of the MIT/X license (contained in the COPYING file
+** with this distribution.)
+*/
+
+#ifndef WINCONFIG_H
+#define WINCONFIG_H
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+
+#include <memory.h>
+#include <string.h>
+
+#define XML_NS 1
+#define XML_DTD 1
+#define XML_CONTEXT_BYTES 1024
+
+/* we will assume all Windows platforms are little endian */
+#define BYTEORDER 1234
+
+/* Windows has memmove() available. */
+#define HAVE_MEMMOVE
+
+#endif /* ndef WINCONFIG_H */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlparse.c b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlparse.c
new file mode 100644
index 0000000..94e31de
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlparse.c
@@ -0,0 +1,6287 @@
+/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+#include <stddef.h>
+#include <string.h> /* memset(), memcpy() */
+#include <assert.h>
+
+#define XML_BUILDING_EXPAT 1
+
+#ifdef COMPILED_FROM_DSP
+#include "winconfig.h"
+#elif defined(MACOS_CLASSIC)
+#include "macconfig.h"
+#elif defined(__amigaos4__)
+#include "amigaconfig.h"
+#elif defined(__WATCOMC__)
+#include "watcomconfig.h"
+#elif defined(HAVE_EXPAT_CONFIG_H)
+#include <expat_config.h>
+#endif /* ndef COMPILED_FROM_DSP */
+
+#include "ascii.h"
+#include "expat.h"
+
+#ifdef XML_UNICODE
+#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
+#define XmlConvert XmlUtf16Convert
+#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS
+#define XmlEncode XmlUtf16Encode
+/* Using pointer subtraction to convert to integer type. */
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1))
+typedef unsigned short ICHAR;
+#else
+#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX
+#define XmlConvert XmlUtf8Convert
+#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS
+#define XmlEncode XmlUtf8Encode
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf8)
+typedef char ICHAR;
+#endif
+
+
+#ifndef XML_NS
+
+#define XmlInitEncodingNS XmlInitEncoding
+#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding
+#undef XmlGetInternalEncodingNS
+#define XmlGetInternalEncodingNS XmlGetInternalEncoding
+#define XmlParseXmlDeclNS XmlParseXmlDecl
+
+#endif
+
+#ifdef XML_UNICODE
+
+#ifdef XML_UNICODE_WCHAR_T
+#define XML_T(x) (const wchar_t)x
+#define XML_L(x) L ## x
+#else
+#define XML_T(x) (const unsigned short)x
+#define XML_L(x) x
+#endif
+
+#else
+
+#define XML_T(x) x
+#define XML_L(x) x
+
+#endif
+
+/* Round up n to be a multiple of sz, where sz is a power of 2. */
+#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
+
+/* Handle the case where memmove() doesn't exist. */
+#ifndef HAVE_MEMMOVE
+#ifdef HAVE_BCOPY
+#define memmove(d,s,l) bcopy((s),(d),(l))
+#else
+#error memmove does not exist on this platform, nor is a substitute available
+#endif /* HAVE_BCOPY */
+#endif /* HAVE_MEMMOVE */
+
+#include "internal.h"
+#include "xmltok.h"
+#include "xmlrole.h"
+
+typedef const XML_Char *KEY;
+
+typedef struct {
+ KEY name;
+} NAMED;
+
+typedef struct {
+ NAMED **v;
+ unsigned char power;
+ size_t size;
+ size_t used;
+ const XML_Memory_Handling_Suite *mem;
+} HASH_TABLE;
+
+/* Basic character hash algorithm, taken from Python's string hash:
+ h = h * 1000003 ^ character, the constant being a prime number.
+
+*/
+#ifdef XML_UNICODE
+#define CHAR_HASH(h, c) \
+ (((h) * 0xF4243) ^ (unsigned short)(c))
+#else
+#define CHAR_HASH(h, c) \
+ (((h) * 0xF4243) ^ (unsigned char)(c))
+#endif
+
+/* For probing (after a collision) we need a step size relative prime
+ to the hash table size, which is a power of 2. We use double-hashing,
+ since we can calculate a second hash value cheaply by taking those bits
+ of the first hash value that were discarded (masked out) when the table
+ index was calculated: index = hash & mask, where mask = table->size - 1.
+ We limit the maximum step size to table->size / 4 (mask >> 2) and make
+ it odd, since odd numbers are always relative prime to a power of 2.
+*/
+#define SECOND_HASH(hash, mask, power) \
+ ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2))
+#define PROBE_STEP(hash, mask, power) \
+ ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1))
+
+typedef struct {
+ NAMED **p;
+ NAMED **end;
+} HASH_TABLE_ITER;
+
+#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */
+#define INIT_DATA_BUF_SIZE 1024
+#define INIT_ATTS_SIZE 16
+#define INIT_ATTS_VERSION 0xFFFFFFFF
+#define INIT_BLOCK_SIZE 1024
+#define INIT_BUFFER_SIZE 1024
+
+#define EXPAND_SPARE 24
+
+typedef struct binding {
+ struct prefix *prefix;
+ struct binding *nextTagBinding;
+ struct binding *prevPrefixBinding;
+ const struct attribute_id *attId;
+ XML_Char *uri;
+ int uriLen;
+ int uriAlloc;
+} BINDING;
+
+typedef struct prefix {
+ const XML_Char *name;
+ BINDING *binding;
+} PREFIX;
+
+typedef struct {
+ const XML_Char *str;
+ const XML_Char *localPart;
+ const XML_Char *prefix;
+ int strLen;
+ int uriLen;
+ int prefixLen;
+} TAG_NAME;
+
+/* TAG represents an open element.
+ The name of the element is stored in both the document and API
+ encodings. The memory buffer 'buf' is a separately-allocated
+ memory area which stores the name. During the XML_Parse()/
+ XMLParseBuffer() when the element is open, the memory for the 'raw'
+ version of the name (in the document encoding) is shared with the
+ document buffer. If the element is open across calls to
+ XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to
+ contain the 'raw' name as well.
+
+ A parser re-uses these structures, maintaining a list of allocated
+ TAG objects in a free list.
+*/
+typedef struct tag {
+ struct tag *parent; /* parent of this element */
+ const char *rawName; /* tagName in the original encoding */
+ int rawNameLength;
+ TAG_NAME name; /* tagName in the API encoding */
+ char *buf; /* buffer for name components */
+ char *bufEnd; /* end of the buffer */
+ BINDING *bindings;
+} TAG;
+
+typedef struct {
+ const XML_Char *name;
+ const XML_Char *textPtr;
+ int textLen; /* length in XML_Chars */
+ int processed; /* # of processed bytes - when suspended */
+ const XML_Char *systemId;
+ const XML_Char *base;
+ const XML_Char *publicId;
+ const XML_Char *notation;
+ XML_Bool open;
+ XML_Bool is_param;
+ XML_Bool is_internal; /* true if declared in internal subset outside PE */
+} ENTITY;
+
+typedef struct {
+ enum XML_Content_Type type;
+ enum XML_Content_Quant quant;
+ const XML_Char * name;
+ int firstchild;
+ int lastchild;
+ int childcnt;
+ int nextsib;
+} CONTENT_SCAFFOLD;
+
+#define INIT_SCAFFOLD_ELEMENTS 32
+
+typedef struct block {
+ struct block *next;
+ int size;
+ XML_Char s[1];
+} BLOCK;
+
+typedef struct {
+ BLOCK *blocks;
+ BLOCK *freeBlocks;
+ const XML_Char *end;
+ XML_Char *ptr;
+ XML_Char *start;
+ const XML_Memory_Handling_Suite *mem;
+} STRING_POOL;
+
+/* The XML_Char before the name is used to determine whether
+ an attribute has been specified. */
+typedef struct attribute_id {
+ XML_Char *name;
+ PREFIX *prefix;
+ XML_Bool maybeTokenized;
+ XML_Bool xmlns;
+} ATTRIBUTE_ID;
+
+typedef struct {
+ const ATTRIBUTE_ID *id;
+ XML_Bool isCdata;
+ const XML_Char *value;
+} DEFAULT_ATTRIBUTE;
+
+typedef struct {
+ unsigned long version;
+ unsigned long hash;
+ const XML_Char *uriName;
+} NS_ATT;
+
+typedef struct {
+ const XML_Char *name;
+ PREFIX *prefix;
+ const ATTRIBUTE_ID *idAtt;
+ int nDefaultAtts;
+ int allocDefaultAtts;
+ DEFAULT_ATTRIBUTE *defaultAtts;
+} ELEMENT_TYPE;
+
+typedef struct {
+ HASH_TABLE generalEntities;
+ HASH_TABLE elementTypes;
+ HASH_TABLE attributeIds;
+ HASH_TABLE prefixes;
+ STRING_POOL pool;
+ STRING_POOL entityValuePool;
+ /* false once a parameter entity reference has been skipped */
+ XML_Bool keepProcessing;
+ /* true once an internal or external PE reference has been encountered;
+ this includes the reference to an external subset */
+ XML_Bool hasParamEntityRefs;
+ XML_Bool standalone;
+#ifdef XML_DTD
+ /* indicates if external PE has been read */
+ XML_Bool paramEntityRead;
+ HASH_TABLE paramEntities;
+#endif /* XML_DTD */
+ PREFIX defaultPrefix;
+ /* === scaffolding for building content model === */
+ XML_Bool in_eldecl;
+ CONTENT_SCAFFOLD *scaffold;
+ unsigned contentStringLen;
+ unsigned scaffSize;
+ unsigned scaffCount;
+ int scaffLevel;
+ int *scaffIndex;
+} DTD;
+
+typedef struct open_internal_entity {
+ const char *internalEventPtr;
+ const char *internalEventEndPtr;
+ struct open_internal_entity *next;
+ ENTITY *entity;
+ int startTagLevel;
+ XML_Bool betweenDecl; /* WFC: PE Between Declarations */
+} OPEN_INTERNAL_ENTITY;
+
+typedef enum XML_Error PTRCALL Processor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr);
+
+static Processor prologProcessor;
+static Processor prologInitProcessor;
+static Processor contentProcessor;
+static Processor cdataSectionProcessor;
+#ifdef XML_DTD
+static Processor ignoreSectionProcessor;
+static Processor externalParEntProcessor;
+static Processor externalParEntInitProcessor;
+static Processor entityValueProcessor;
+static Processor entityValueInitProcessor;
+#endif /* XML_DTD */
+static Processor epilogProcessor;
+static Processor errorProcessor;
+static Processor externalEntityInitProcessor;
+static Processor externalEntityInitProcessor2;
+static Processor externalEntityInitProcessor3;
+static Processor externalEntityContentProcessor;
+static Processor internalEntityProcessor;
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName);
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
+ const char *s, const char *next);
+static enum XML_Error
+initializeEncoding(XML_Parser parser);
+static enum XML_Error
+doProlog(XML_Parser parser, const ENCODING *enc, const char *s,
+ const char *end, int tok, const char *next, const char **nextPtr,
+ XML_Bool haveMore);
+static enum XML_Error
+processInternalEntity(XML_Parser parser, ENTITY *entity,
+ XML_Bool betweenDecl);
+static enum XML_Error
+doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+ const char *start, const char *end, const char **endPtr,
+ XML_Bool haveMore);
+static enum XML_Error
+doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr,
+ const char *end, const char **nextPtr, XML_Bool haveMore);
+#ifdef XML_DTD
+static enum XML_Error
+doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr,
+ const char *end, const char **nextPtr, XML_Bool haveMore);
+#endif /* XML_DTD */
+
+static enum XML_Error
+storeAtts(XML_Parser parser, const ENCODING *, const char *s,
+ TAG_NAME *tagNamePtr, BINDING **bindingsPtr);
+static enum XML_Error
+addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
+ const XML_Char *uri, BINDING **bindingsPtr);
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata,
+ XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser);
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata,
+ const char *, const char *, STRING_POOL *);
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata,
+ const char *, const char *, STRING_POOL *);
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start,
+ const char *end);
+static int
+setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
+static enum XML_Error
+storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start,
+ const char *end);
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end);
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start,
+ const char *end);
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *start,
+ const char *end);
+
+static const XML_Char * getContext(XML_Parser parser);
+static XML_Bool
+setContext(XML_Parser parser, const XML_Char *context);
+
+static void FASTCALL normalizePublicId(XML_Char *s);
+
+static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms);
+/* do not call if parentParser != NULL */
+static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
+static void
+dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms);
+static int
+dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms);
+static int
+copyEntityTable(HASH_TABLE *, STRING_POOL *, const HASH_TABLE *);
+
+static NAMED *
+lookup(HASH_TABLE *table, KEY name, size_t createSize);
+static void FASTCALL
+hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms);
+static void FASTCALL hashTableClear(HASH_TABLE *);
+static void FASTCALL hashTableDestroy(HASH_TABLE *);
+static void FASTCALL
+hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
+static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *);
+
+static void FASTCALL
+poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms);
+static void FASTCALL poolClear(STRING_POOL *);
+static void FASTCALL poolDestroy(STRING_POOL *);
+static XML_Char *
+poolAppend(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end);
+static XML_Char *
+poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end);
+static XML_Bool FASTCALL poolGrow(STRING_POOL *pool);
+static const XML_Char * FASTCALL
+poolCopyString(STRING_POOL *pool, const XML_Char *s);
+static const XML_Char *
+poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n);
+static const XML_Char * FASTCALL
+poolAppendString(STRING_POOL *pool, const XML_Char *s);
+
+static int FASTCALL nextScaffoldPart(XML_Parser parser);
+static XML_Content * build_model(XML_Parser parser);
+static ELEMENT_TYPE *
+getElementType(XML_Parser parser, const ENCODING *enc,
+ const char *ptr, const char *end);
+
+static XML_Parser
+parserCreate(const XML_Char *encodingName,
+ const XML_Memory_Handling_Suite *memsuite,
+ const XML_Char *nameSep,
+ DTD *dtd);
+static void
+parserInit(XML_Parser parser, const XML_Char *encodingName);
+
+#define poolStart(pool) ((pool)->start)
+#define poolEnd(pool) ((pool)->ptr)
+#define poolLength(pool) ((pool)->ptr - (pool)->start)
+#define poolChop(pool) ((void)--(pool->ptr))
+#define poolLastChar(pool) (((pool)->ptr)[-1])
+#define poolDiscard(pool) ((pool)->ptr = (pool)->start)
+#define poolFinish(pool) ((pool)->start = (pool)->ptr)
+#define poolAppendChar(pool, c) \
+ (((pool)->ptr == (pool)->end && !poolGrow(pool)) \
+ ? 0 \
+ : ((*((pool)->ptr)++ = c), 1))
+
+struct XML_ParserStruct {
+ /* The first member must be userData so that the XML_GetUserData
+ macro works. */
+ void *m_userData;
+ void *m_handlerArg;
+ char *m_buffer;
+ const XML_Memory_Handling_Suite m_mem;
+ /* first character to be parsed */
+ const char *m_bufferPtr;
+ /* past last character to be parsed */
+ char *m_bufferEnd;
+ /* allocated end of buffer */
+ const char *m_bufferLim;
+ XML_Index m_parseEndByteIndex;
+ const char *m_parseEndPtr;
+ XML_Char *m_dataBuf;
+ XML_Char *m_dataBufEnd;
+ XML_StartElementHandler m_startElementHandler;
+ XML_EndElementHandler m_endElementHandler;
+ XML_CharacterDataHandler m_characterDataHandler;
+ XML_ProcessingInstructionHandler m_processingInstructionHandler;
+ XML_CommentHandler m_commentHandler;
+ XML_StartCdataSectionHandler m_startCdataSectionHandler;
+ XML_EndCdataSectionHandler m_endCdataSectionHandler;
+ XML_DefaultHandler m_defaultHandler;
+ XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler;
+ XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler;
+ XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler;
+ XML_NotationDeclHandler m_notationDeclHandler;
+ XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler;
+ XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler;
+ XML_NotStandaloneHandler m_notStandaloneHandler;
+ XML_ExternalEntityRefHandler m_externalEntityRefHandler;
+ XML_Parser m_externalEntityRefHandlerArg;
+ XML_SkippedEntityHandler m_skippedEntityHandler;
+ XML_UnknownEncodingHandler m_unknownEncodingHandler;
+ XML_ElementDeclHandler m_elementDeclHandler;
+ XML_AttlistDeclHandler m_attlistDeclHandler;
+ XML_EntityDeclHandler m_entityDeclHandler;
+ XML_XmlDeclHandler m_xmlDeclHandler;
+ const ENCODING *m_encoding;
+ INIT_ENCODING m_initEncoding;
+ const ENCODING *m_internalEncoding;
+ const XML_Char *m_protocolEncodingName;
+ XML_Bool m_ns;
+ XML_Bool m_ns_triplets;
+ void *m_unknownEncodingMem;
+ void *m_unknownEncodingData;
+ void *m_unknownEncodingHandlerData;
+ void (XMLCALL *m_unknownEncodingRelease)(void *);
+ PROLOG_STATE m_prologState;
+ Processor *m_processor;
+ enum XML_Error m_errorCode;
+ const char *m_eventPtr;
+ const char *m_eventEndPtr;
+ const char *m_positionPtr;
+ OPEN_INTERNAL_ENTITY *m_openInternalEntities;
+ OPEN_INTERNAL_ENTITY *m_freeInternalEntities;
+ XML_Bool m_defaultExpandInternalEntities;
+ int m_tagLevel;
+ ENTITY *m_declEntity;
+ const XML_Char *m_doctypeName;
+ const XML_Char *m_doctypeSysid;
+ const XML_Char *m_doctypePubid;
+ const XML_Char *m_declAttributeType;
+ const XML_Char *m_declNotationName;
+ const XML_Char *m_declNotationPublicId;
+ ELEMENT_TYPE *m_declElementType;
+ ATTRIBUTE_ID *m_declAttributeId;
+ XML_Bool m_declAttributeIsCdata;
+ XML_Bool m_declAttributeIsId;
+ DTD *m_dtd;
+ const XML_Char *m_curBase;
+ TAG *m_tagStack;
+ TAG *m_freeTagList;
+ BINDING *m_inheritedBindings;
+ BINDING *m_freeBindingList;
+ int m_attsSize;
+ int m_nSpecifiedAtts;
+ int m_idAttIndex;
+ ATTRIBUTE *m_atts;
+ NS_ATT *m_nsAtts;
+ unsigned long m_nsAttsVersion;
+ unsigned char m_nsAttsPower;
+ POSITION m_position;
+ STRING_POOL m_tempPool;
+ STRING_POOL m_temp2Pool;
+ char *m_groupConnector;
+ unsigned int m_groupSize;
+ XML_Char m_namespaceSeparator;
+ XML_Parser m_parentParser;
+ XML_ParsingStatus m_parsingStatus;
+#ifdef XML_DTD
+ XML_Bool m_isParamEntity;
+ XML_Bool m_useForeignDTD;
+ enum XML_ParamEntityParsing m_paramEntityParsing;
+#endif
+};
+
+#define MALLOC(s) (parser->m_mem.malloc_fcn((s)))
+#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s)))
+#define FREE(p) (parser->m_mem.free_fcn((p)))
+
+#define userData (parser->m_userData)
+#define handlerArg (parser->m_handlerArg)
+#define startElementHandler (parser->m_startElementHandler)
+#define endElementHandler (parser->m_endElementHandler)
+#define characterDataHandler (parser->m_characterDataHandler)
+#define processingInstructionHandler \
+ (parser->m_processingInstructionHandler)
+#define commentHandler (parser->m_commentHandler)
+#define startCdataSectionHandler \
+ (parser->m_startCdataSectionHandler)
+#define endCdataSectionHandler (parser->m_endCdataSectionHandler)
+#define defaultHandler (parser->m_defaultHandler)
+#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler)
+#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler)
+#define unparsedEntityDeclHandler \
+ (parser->m_unparsedEntityDeclHandler)
+#define notationDeclHandler (parser->m_notationDeclHandler)
+#define startNamespaceDeclHandler \
+ (parser->m_startNamespaceDeclHandler)
+#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler)
+#define notStandaloneHandler (parser->m_notStandaloneHandler)
+#define externalEntityRefHandler \
+ (parser->m_externalEntityRefHandler)
+#define externalEntityRefHandlerArg \
+ (parser->m_externalEntityRefHandlerArg)
+#define internalEntityRefHandler \
+ (parser->m_internalEntityRefHandler)
+#define skippedEntityHandler (parser->m_skippedEntityHandler)
+#define unknownEncodingHandler (parser->m_unknownEncodingHandler)
+#define elementDeclHandler (parser->m_elementDeclHandler)
+#define attlistDeclHandler (parser->m_attlistDeclHandler)
+#define entityDeclHandler (parser->m_entityDeclHandler)
+#define xmlDeclHandler (parser->m_xmlDeclHandler)
+#define encoding (parser->m_encoding)
+#define initEncoding (parser->m_initEncoding)
+#define internalEncoding (parser->m_internalEncoding)
+#define unknownEncodingMem (parser->m_unknownEncodingMem)
+#define unknownEncodingData (parser->m_unknownEncodingData)
+#define unknownEncodingHandlerData \
+ (parser->m_unknownEncodingHandlerData)
+#define unknownEncodingRelease (parser->m_unknownEncodingRelease)
+#define protocolEncodingName (parser->m_protocolEncodingName)
+#define ns (parser->m_ns)
+#define ns_triplets (parser->m_ns_triplets)
+#define prologState (parser->m_prologState)
+#define processor (parser->m_processor)
+#define errorCode (parser->m_errorCode)
+#define eventPtr (parser->m_eventPtr)
+#define eventEndPtr (parser->m_eventEndPtr)
+#define positionPtr (parser->m_positionPtr)
+#define position (parser->m_position)
+#define openInternalEntities (parser->m_openInternalEntities)
+#define freeInternalEntities (parser->m_freeInternalEntities)
+#define defaultExpandInternalEntities \
+ (parser->m_defaultExpandInternalEntities)
+#define tagLevel (parser->m_tagLevel)
+#define buffer (parser->m_buffer)
+#define bufferPtr (parser->m_bufferPtr)
+#define bufferEnd (parser->m_bufferEnd)
+#define parseEndByteIndex (parser->m_parseEndByteIndex)
+#define parseEndPtr (parser->m_parseEndPtr)
+#define bufferLim (parser->m_bufferLim)
+#define dataBuf (parser->m_dataBuf)
+#define dataBufEnd (parser->m_dataBufEnd)
+#define _dtd (parser->m_dtd)
+#define curBase (parser->m_curBase)
+#define declEntity (parser->m_declEntity)
+#define doctypeName (parser->m_doctypeName)
+#define doctypeSysid (parser->m_doctypeSysid)
+#define doctypePubid (parser->m_doctypePubid)
+#define declAttributeType (parser->m_declAttributeType)
+#define declNotationName (parser->m_declNotationName)
+#define declNotationPublicId (parser->m_declNotationPublicId)
+#define declElementType (parser->m_declElementType)
+#define declAttributeId (parser->m_declAttributeId)
+#define declAttributeIsCdata (parser->m_declAttributeIsCdata)
+#define declAttributeIsId (parser->m_declAttributeIsId)
+#define freeTagList (parser->m_freeTagList)
+#define freeBindingList (parser->m_freeBindingList)
+#define inheritedBindings (parser->m_inheritedBindings)
+#define tagStack (parser->m_tagStack)
+#define atts (parser->m_atts)
+#define attsSize (parser->m_attsSize)
+#define nSpecifiedAtts (parser->m_nSpecifiedAtts)
+#define idAttIndex (parser->m_idAttIndex)
+#define nsAtts (parser->m_nsAtts)
+#define nsAttsVersion (parser->m_nsAttsVersion)
+#define nsAttsPower (parser->m_nsAttsPower)
+#define tempPool (parser->m_tempPool)
+#define temp2Pool (parser->m_temp2Pool)
+#define groupConnector (parser->m_groupConnector)
+#define groupSize (parser->m_groupSize)
+#define namespaceSeparator (parser->m_namespaceSeparator)
+#define parentParser (parser->m_parentParser)
+#define ps_parsing (parser->m_parsingStatus.parsing)
+#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer)
+#ifdef XML_DTD
+#define isParamEntity (parser->m_isParamEntity)
+#define useForeignDTD (parser->m_useForeignDTD)
+#define paramEntityParsing (parser->m_paramEntityParsing)
+#endif /* XML_DTD */
+
+XML_Parser XMLCALL
+XML_ParserCreate(const XML_Char *encodingName)
+{
+ return XML_ParserCreate_MM(encodingName, NULL, NULL);
+}
+
+XML_Parser XMLCALL
+XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep)
+{
+ XML_Char tmp[2];
+ *tmp = nsSep;
+ return XML_ParserCreate_MM(encodingName, NULL, tmp);
+}
+
+static const XML_Char implicitContext[] = {
+ ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p,
+ ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w,
+ ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g,
+ ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9,
+ ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e,
+ ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0'
+};
+
+XML_Parser XMLCALL
+XML_ParserCreate_MM(const XML_Char *encodingName,
+ const XML_Memory_Handling_Suite *memsuite,
+ const XML_Char *nameSep)
+{
+ XML_Parser parser = parserCreate(encodingName, memsuite, nameSep, NULL);
+ if (parser != NULL && ns) {
+ /* implicit context only set for root parser, since child
+ parsers (i.e. external entity parsers) will inherit it
+ */
+ if (!setContext(parser, implicitContext)) {
+ XML_ParserFree(parser);
+ return NULL;
+ }
+ }
+ return parser;
+}
+
+static XML_Parser
+parserCreate(const XML_Char *encodingName,
+ const XML_Memory_Handling_Suite *memsuite,
+ const XML_Char *nameSep,
+ DTD *dtd)
+{
+ XML_Parser parser;
+
+ if (memsuite) {
+ XML_Memory_Handling_Suite *mtemp;
+ parser = (XML_Parser)
+ memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
+ if (parser != NULL) {
+ mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
+ mtemp->malloc_fcn = memsuite->malloc_fcn;
+ mtemp->realloc_fcn = memsuite->realloc_fcn;
+ mtemp->free_fcn = memsuite->free_fcn;
+ }
+ }
+ else {
+ XML_Memory_Handling_Suite *mtemp;
+ parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
+ if (parser != NULL) {
+ mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
+ mtemp->malloc_fcn = malloc;
+ mtemp->realloc_fcn = realloc;
+ mtemp->free_fcn = free;
+ }
+ }
+
+ if (!parser)
+ return parser;
+
+ buffer = NULL;
+ bufferLim = NULL;
+
+ attsSize = INIT_ATTS_SIZE;
+ atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE));
+ if (atts == NULL) {
+ FREE(parser);
+ return NULL;
+ }
+ dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char));
+ if (dataBuf == NULL) {
+ FREE(atts);
+ FREE(parser);
+ return NULL;
+ }
+ dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE;
+
+ if (dtd)
+ _dtd = dtd;
+ else {
+ _dtd = dtdCreate(&parser->m_mem);
+ if (_dtd == NULL) {
+ FREE(dataBuf);
+ FREE(atts);
+ FREE(parser);
+ return NULL;
+ }
+ }
+
+ freeBindingList = NULL;
+ freeTagList = NULL;
+ freeInternalEntities = NULL;
+
+ groupSize = 0;
+ groupConnector = NULL;
+
+ unknownEncodingHandler = NULL;
+ unknownEncodingHandlerData = NULL;
+
+ namespaceSeparator = ASCII_EXCL;
+ ns = XML_FALSE;
+ ns_triplets = XML_FALSE;
+
+ nsAtts = NULL;
+ nsAttsVersion = 0;
+ nsAttsPower = 0;
+
+ poolInit(&tempPool, &(parser->m_mem));
+ poolInit(&temp2Pool, &(parser->m_mem));
+ parserInit(parser, encodingName);
+
+ if (encodingName && !protocolEncodingName) {
+ XML_ParserFree(parser);
+ return NULL;
+ }
+
+ if (nameSep) {
+ ns = XML_TRUE;
+ internalEncoding = XmlGetInternalEncodingNS();
+ namespaceSeparator = *nameSep;
+ }
+ else {
+ internalEncoding = XmlGetInternalEncoding();
+ }
+
+ return parser;
+}
+
+static void
+parserInit(XML_Parser parser, const XML_Char *encodingName)
+{
+ processor = prologInitProcessor;
+ XmlPrologStateInit(&prologState);
+ protocolEncodingName = (encodingName != NULL
+ ? poolCopyString(&tempPool, encodingName)
+ : NULL);
+ curBase = NULL;
+ XmlInitEncoding(&initEncoding, &encoding, 0);
+ userData = NULL;
+ handlerArg = NULL;
+ startElementHandler = NULL;
+ endElementHandler = NULL;
+ characterDataHandler = NULL;
+ processingInstructionHandler = NULL;
+ commentHandler = NULL;
+ startCdataSectionHandler = NULL;
+ endCdataSectionHandler = NULL;
+ defaultHandler = NULL;
+ startDoctypeDeclHandler = NULL;
+ endDoctypeDeclHandler = NULL;
+ unparsedEntityDeclHandler = NULL;
+ notationDeclHandler = NULL;
+ startNamespaceDeclHandler = NULL;
+ endNamespaceDeclHandler = NULL;
+ notStandaloneHandler = NULL;
+ externalEntityRefHandler = NULL;
+ externalEntityRefHandlerArg = parser;
+ skippedEntityHandler = NULL;
+ elementDeclHandler = NULL;
+ attlistDeclHandler = NULL;
+ entityDeclHandler = NULL;
+ xmlDeclHandler = NULL;
+ bufferPtr = buffer;
+ bufferEnd = buffer;
+ parseEndByteIndex = 0;
+ parseEndPtr = NULL;
+ declElementType = NULL;
+ declAttributeId = NULL;
+ declEntity = NULL;
+ doctypeName = NULL;
+ doctypeSysid = NULL;
+ doctypePubid = NULL;
+ declAttributeType = NULL;
+ declNotationName = NULL;
+ declNotationPublicId = NULL;
+ declAttributeIsCdata = XML_FALSE;
+ declAttributeIsId = XML_FALSE;
+ memset(&position, 0, sizeof(POSITION));
+ errorCode = XML_ERROR_NONE;
+ eventPtr = NULL;
+ eventEndPtr = NULL;
+ positionPtr = NULL;
+ openInternalEntities = NULL;
+ defaultExpandInternalEntities = XML_TRUE;
+ tagLevel = 0;
+ tagStack = NULL;
+ inheritedBindings = NULL;
+ nSpecifiedAtts = 0;
+ unknownEncodingMem = NULL;
+ unknownEncodingRelease = NULL;
+ unknownEncodingData = NULL;
+ parentParser = NULL;
+ ps_parsing = XML_INITIALIZED;
+#ifdef XML_DTD
+ isParamEntity = XML_FALSE;
+ useForeignDTD = XML_FALSE;
+ paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+#endif
+}
+
+/* moves list of bindings to freeBindingList */
+static void FASTCALL
+moveToFreeBindingList(XML_Parser parser, BINDING *bindings)
+{
+ while (bindings) {
+ BINDING *b = bindings;
+ bindings = bindings->nextTagBinding;
+ b->nextTagBinding = freeBindingList;
+ freeBindingList = b;
+ }
+}
+
+XML_Bool XMLCALL
+XML_ParserReset(XML_Parser parser, const XML_Char *encodingName)
+{
+ TAG *tStk;
+ OPEN_INTERNAL_ENTITY *openEntityList;
+ if (parentParser)
+ return XML_FALSE;
+ /* move tagStack to freeTagList */
+ tStk = tagStack;
+ while (tStk) {
+ TAG *tag = tStk;
+ tStk = tStk->parent;
+ tag->parent = freeTagList;
+ moveToFreeBindingList(parser, tag->bindings);
+ tag->bindings = NULL;
+ freeTagList = tag;
+ }
+ /* move openInternalEntities to freeInternalEntities */
+ openEntityList = openInternalEntities;
+ while (openEntityList) {
+ OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
+ openEntityList = openEntity->next;
+ openEntity->next = freeInternalEntities;
+ freeInternalEntities = openEntity;
+ }
+ moveToFreeBindingList(parser, inheritedBindings);
+ FREE(unknownEncodingMem);
+ if (unknownEncodingRelease)
+ unknownEncodingRelease(unknownEncodingData);
+ poolClear(&tempPool);
+ poolClear(&temp2Pool);
+ parserInit(parser, encodingName);
+ dtdReset(_dtd, &parser->m_mem);
+ return setContext(parser, implicitContext);
+}
+
+enum XML_Status XMLCALL
+XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName)
+{
+ /* Block after XML_Parse()/XML_ParseBuffer() has been called.
+ XXX There's no way for the caller to determine which of the
+ XXX possible error cases caused the XML_STATUS_ERROR return.
+ */
+ if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ return XML_STATUS_ERROR;
+ if (encodingName == NULL)
+ protocolEncodingName = NULL;
+ else {
+ protocolEncodingName = poolCopyString(&tempPool, encodingName);
+ if (!protocolEncodingName)
+ return XML_STATUS_ERROR;
+ }
+ return XML_STATUS_OK;
+}
+
+XML_Parser XMLCALL
+XML_ExternalEntityParserCreate(XML_Parser oldParser,
+ const XML_Char *context,
+ const XML_Char *encodingName)
+{
+ XML_Parser parser = oldParser;
+ DTD *newDtd = NULL;
+ DTD *oldDtd = _dtd;
+ XML_StartElementHandler oldStartElementHandler = startElementHandler;
+ XML_EndElementHandler oldEndElementHandler = endElementHandler;
+ XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler;
+ XML_ProcessingInstructionHandler oldProcessingInstructionHandler
+ = processingInstructionHandler;
+ XML_CommentHandler oldCommentHandler = commentHandler;
+ XML_StartCdataSectionHandler oldStartCdataSectionHandler
+ = startCdataSectionHandler;
+ XML_EndCdataSectionHandler oldEndCdataSectionHandler
+ = endCdataSectionHandler;
+ XML_DefaultHandler oldDefaultHandler = defaultHandler;
+ XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler
+ = unparsedEntityDeclHandler;
+ XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler;
+ XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler
+ = startNamespaceDeclHandler;
+ XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler
+ = endNamespaceDeclHandler;
+ XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler;
+ XML_ExternalEntityRefHandler oldExternalEntityRefHandler
+ = externalEntityRefHandler;
+ XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler;
+ XML_UnknownEncodingHandler oldUnknownEncodingHandler
+ = unknownEncodingHandler;
+ XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler;
+ XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler;
+ XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler;
+ XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler;
+ ELEMENT_TYPE * oldDeclElementType = declElementType;
+
+ void *oldUserData = userData;
+ void *oldHandlerArg = handlerArg;
+ XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities;
+ XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg;
+#ifdef XML_DTD
+ enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing;
+ int oldInEntityValue = prologState.inEntityValue;
+#endif
+ XML_Bool oldns_triplets = ns_triplets;
+
+#ifdef XML_DTD
+ if (!context)
+ newDtd = oldDtd;
+#endif /* XML_DTD */
+
+ /* Note that the magical uses of the pre-processor to make field
+ access look more like C++ require that `parser' be overwritten
+ here. This makes this function more painful to follow than it
+ would be otherwise.
+ */
+ if (ns) {
+ XML_Char tmp[2];
+ *tmp = namespaceSeparator;
+ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
+ }
+ else {
+ parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
+ }
+
+ if (!parser)
+ return NULL;
+
+ startElementHandler = oldStartElementHandler;
+ endElementHandler = oldEndElementHandler;
+ characterDataHandler = oldCharacterDataHandler;
+ processingInstructionHandler = oldProcessingInstructionHandler;
+ commentHandler = oldCommentHandler;
+ startCdataSectionHandler = oldStartCdataSectionHandler;
+ endCdataSectionHandler = oldEndCdataSectionHandler;
+ defaultHandler = oldDefaultHandler;
+ unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler;
+ notationDeclHandler = oldNotationDeclHandler;
+ startNamespaceDeclHandler = oldStartNamespaceDeclHandler;
+ endNamespaceDeclHandler = oldEndNamespaceDeclHandler;
+ notStandaloneHandler = oldNotStandaloneHandler;
+ externalEntityRefHandler = oldExternalEntityRefHandler;
+ skippedEntityHandler = oldSkippedEntityHandler;
+ unknownEncodingHandler = oldUnknownEncodingHandler;
+ elementDeclHandler = oldElementDeclHandler;
+ attlistDeclHandler = oldAttlistDeclHandler;
+ entityDeclHandler = oldEntityDeclHandler;
+ xmlDeclHandler = oldXmlDeclHandler;
+ declElementType = oldDeclElementType;
+ userData = oldUserData;
+ if (oldUserData == oldHandlerArg)
+ handlerArg = userData;
+ else
+ handlerArg = parser;
+ if (oldExternalEntityRefHandlerArg != oldParser)
+ externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg;
+ defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
+ ns_triplets = oldns_triplets;
+ parentParser = oldParser;
+#ifdef XML_DTD
+ paramEntityParsing = oldParamEntityParsing;
+ prologState.inEntityValue = oldInEntityValue;
+ if (context) {
+#endif /* XML_DTD */
+ if (!dtdCopy(_dtd, oldDtd, &parser->m_mem)
+ || !setContext(parser, context)) {
+ XML_ParserFree(parser);
+ return NULL;
+ }
+ processor = externalEntityInitProcessor;
+#ifdef XML_DTD
+ }
+ else {
+ /* The DTD instance referenced by _dtd is shared between the document's
+ root parser and external PE parsers, therefore one does not need to
+ call setContext. In addition, one also *must* not call setContext,
+ because this would overwrite existing prefix->binding pointers in
+ _dtd with ones that get destroyed with the external PE parser.
+ This would leave those prefixes with dangling pointers.
+ */
+ isParamEntity = XML_TRUE;
+ XmlPrologStateInitExternalEntity(&prologState);
+ processor = externalParEntInitProcessor;
+ }
+#endif /* XML_DTD */
+ return parser;
+}
+
+static void FASTCALL
+destroyBindings(BINDING *bindings, XML_Parser parser)
+{
+ for (;;) {
+ BINDING *b = bindings;
+ if (!b)
+ break;
+ bindings = b->nextTagBinding;
+ FREE(b->uri);
+ FREE(b);
+ }
+}
+
+void XMLCALL
+XML_ParserFree(XML_Parser parser)
+{
+ TAG *tagList;
+ OPEN_INTERNAL_ENTITY *entityList;
+ if (parser == NULL)
+ return;
+ /* free tagStack and freeTagList */
+ tagList = tagStack;
+ for (;;) {
+ TAG *p;
+ if (tagList == NULL) {
+ if (freeTagList == NULL)
+ break;
+ tagList = freeTagList;
+ freeTagList = NULL;
+ }
+ p = tagList;
+ tagList = tagList->parent;
+ FREE(p->buf);
+ destroyBindings(p->bindings, parser);
+ FREE(p);
+ }
+ /* free openInternalEntities and freeInternalEntities */
+ entityList = openInternalEntities;
+ for (;;) {
+ OPEN_INTERNAL_ENTITY *openEntity;
+ if (entityList == NULL) {
+ if (freeInternalEntities == NULL)
+ break;
+ entityList = freeInternalEntities;
+ freeInternalEntities = NULL;
+ }
+ openEntity = entityList;
+ entityList = entityList->next;
+ FREE(openEntity);
+ }
+
+ destroyBindings(freeBindingList, parser);
+ destroyBindings(inheritedBindings, parser);
+ poolDestroy(&tempPool);
+ poolDestroy(&temp2Pool);
+#ifdef XML_DTD
+ /* external parameter entity parsers share the DTD structure
+ parser->m_dtd with the root parser, so we must not destroy it
+ */
+ if (!isParamEntity && _dtd)
+#else
+ if (_dtd)
+#endif /* XML_DTD */
+ dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem);
+ FREE((void *)atts);
+ FREE(groupConnector);
+ FREE(buffer);
+ FREE(dataBuf);
+ FREE(nsAtts);
+ FREE(unknownEncodingMem);
+ if (unknownEncodingRelease)
+ unknownEncodingRelease(unknownEncodingData);
+ FREE(parser);
+}
+
+void XMLCALL
+XML_UseParserAsHandlerArg(XML_Parser parser)
+{
+ handlerArg = parser;
+}
+
+enum XML_Error XMLCALL
+XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD)
+{
+#ifdef XML_DTD
+ /* block after XML_Parse()/XML_ParseBuffer() has been called */
+ if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING;
+ useForeignDTD = useDTD;
+ return XML_ERROR_NONE;
+#else
+ return XML_ERROR_FEATURE_REQUIRES_XML_DTD;
+#endif
+}
+
+void XMLCALL
+XML_SetReturnNSTriplet(XML_Parser parser, int do_nst)
+{
+ /* block after XML_Parse()/XML_ParseBuffer() has been called */
+ if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ return;
+ ns_triplets = do_nst ? XML_TRUE : XML_FALSE;
+}
+
+void XMLCALL
+XML_SetUserData(XML_Parser parser, void *p)
+{
+ if (handlerArg == userData)
+ handlerArg = userData = p;
+ else
+ userData = p;
+}
+
+enum XML_Status XMLCALL
+XML_SetBase(XML_Parser parser, const XML_Char *p)
+{
+ if (p) {
+ p = poolCopyString(&_dtd->pool, p);
+ if (!p)
+ return XML_STATUS_ERROR;
+ curBase = p;
+ }
+ else
+ curBase = NULL;
+ return XML_STATUS_OK;
+}
+
+const XML_Char * XMLCALL
+XML_GetBase(XML_Parser parser)
+{
+ return curBase;
+}
+
+int XMLCALL
+XML_GetSpecifiedAttributeCount(XML_Parser parser)
+{
+ return nSpecifiedAtts;
+}
+
+int XMLCALL
+XML_GetIdAttributeIndex(XML_Parser parser)
+{
+ return idAttIndex;
+}
+
+void XMLCALL
+XML_SetElementHandler(XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end)
+{
+ startElementHandler = start;
+ endElementHandler = end;
+}
+
+void XMLCALL
+XML_SetStartElementHandler(XML_Parser parser,
+ XML_StartElementHandler start) {
+ startElementHandler = start;
+}
+
+void XMLCALL
+XML_SetEndElementHandler(XML_Parser parser,
+ XML_EndElementHandler end) {
+ endElementHandler = end;
+}
+
+void XMLCALL
+XML_SetCharacterDataHandler(XML_Parser parser,
+ XML_CharacterDataHandler handler)
+{
+ characterDataHandler = handler;
+}
+
+void XMLCALL
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+ XML_ProcessingInstructionHandler handler)
+{
+ processingInstructionHandler = handler;
+}
+
+void XMLCALL
+XML_SetCommentHandler(XML_Parser parser,
+ XML_CommentHandler handler)
+{
+ commentHandler = handler;
+}
+
+void XMLCALL
+XML_SetCdataSectionHandler(XML_Parser parser,
+ XML_StartCdataSectionHandler start,
+ XML_EndCdataSectionHandler end)
+{
+ startCdataSectionHandler = start;
+ endCdataSectionHandler = end;
+}
+
+void XMLCALL
+XML_SetStartCdataSectionHandler(XML_Parser parser,
+ XML_StartCdataSectionHandler start) {
+ startCdataSectionHandler = start;
+}
+
+void XMLCALL
+XML_SetEndCdataSectionHandler(XML_Parser parser,
+ XML_EndCdataSectionHandler end) {
+ endCdataSectionHandler = end;
+}
+
+void XMLCALL
+XML_SetDefaultHandler(XML_Parser parser,
+ XML_DefaultHandler handler)
+{
+ defaultHandler = handler;
+ defaultExpandInternalEntities = XML_FALSE;
+}
+
+void XMLCALL
+XML_SetDefaultHandlerExpand(XML_Parser parser,
+ XML_DefaultHandler handler)
+{
+ defaultHandler = handler;
+ defaultExpandInternalEntities = XML_TRUE;
+}
+
+void XMLCALL
+XML_SetDoctypeDeclHandler(XML_Parser parser,
+ XML_StartDoctypeDeclHandler start,
+ XML_EndDoctypeDeclHandler end)
+{
+ startDoctypeDeclHandler = start;
+ endDoctypeDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetStartDoctypeDeclHandler(XML_Parser parser,
+ XML_StartDoctypeDeclHandler start) {
+ startDoctypeDeclHandler = start;
+}
+
+void XMLCALL
+XML_SetEndDoctypeDeclHandler(XML_Parser parser,
+ XML_EndDoctypeDeclHandler end) {
+ endDoctypeDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+ XML_UnparsedEntityDeclHandler handler)
+{
+ unparsedEntityDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetNotationDeclHandler(XML_Parser parser,
+ XML_NotationDeclHandler handler)
+{
+ notationDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+ XML_StartNamespaceDeclHandler start,
+ XML_EndNamespaceDeclHandler end)
+{
+ startNamespaceDeclHandler = start;
+ endNamespaceDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetStartNamespaceDeclHandler(XML_Parser parser,
+ XML_StartNamespaceDeclHandler start) {
+ startNamespaceDeclHandler = start;
+}
+
+void XMLCALL
+XML_SetEndNamespaceDeclHandler(XML_Parser parser,
+ XML_EndNamespaceDeclHandler end) {
+ endNamespaceDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetNotStandaloneHandler(XML_Parser parser,
+ XML_NotStandaloneHandler handler)
+{
+ notStandaloneHandler = handler;
+}
+
+void XMLCALL
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+ XML_ExternalEntityRefHandler handler)
+{
+ externalEntityRefHandler = handler;
+}
+
+void XMLCALL
+XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg)
+{
+ if (arg)
+ externalEntityRefHandlerArg = (XML_Parser)arg;
+ else
+ externalEntityRefHandlerArg = parser;
+}
+
+void XMLCALL
+XML_SetSkippedEntityHandler(XML_Parser parser,
+ XML_SkippedEntityHandler handler)
+{
+ skippedEntityHandler = handler;
+}
+
+void XMLCALL
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+ XML_UnknownEncodingHandler handler,
+ void *data)
+{
+ unknownEncodingHandler = handler;
+ unknownEncodingHandlerData = data;
+}
+
+void XMLCALL
+XML_SetElementDeclHandler(XML_Parser parser,
+ XML_ElementDeclHandler eldecl)
+{
+ elementDeclHandler = eldecl;
+}
+
+void XMLCALL
+XML_SetAttlistDeclHandler(XML_Parser parser,
+ XML_AttlistDeclHandler attdecl)
+{
+ attlistDeclHandler = attdecl;
+}
+
+void XMLCALL
+XML_SetEntityDeclHandler(XML_Parser parser,
+ XML_EntityDeclHandler handler)
+{
+ entityDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetXmlDeclHandler(XML_Parser parser,
+ XML_XmlDeclHandler handler) {
+ xmlDeclHandler = handler;
+}
+
+int XMLCALL
+XML_SetParamEntityParsing(XML_Parser parser,
+ enum XML_ParamEntityParsing peParsing)
+{
+ /* block after XML_Parse()/XML_ParseBuffer() has been called */
+ if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ return 0;
+#ifdef XML_DTD
+ paramEntityParsing = peParsing;
+ return 1;
+#else
+ return peParsing == XML_PARAM_ENTITY_PARSING_NEVER;
+#endif
+}
+
+enum XML_Status XMLCALL
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
+{
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ errorCode = XML_ERROR_SUSPENDED;
+ return XML_STATUS_ERROR;
+ case XML_FINISHED:
+ errorCode = XML_ERROR_FINISHED;
+ return XML_STATUS_ERROR;
+ default:
+ ps_parsing = XML_PARSING;
+ }
+
+ if (len == 0) {
+ ps_finalBuffer = (XML_Bool)isFinal;
+ if (!isFinal)
+ return XML_STATUS_OK;
+ positionPtr = bufferPtr;
+ parseEndPtr = bufferEnd;
+
+ /* If data are left over from last buffer, and we now know that these
+ data are the final chunk of input, then we have to check them again
+ to detect errors based on that fact.
+ */
+ errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr);
+
+ if (errorCode == XML_ERROR_NONE) {
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+ positionPtr = bufferPtr;
+ return XML_STATUS_SUSPENDED;
+ case XML_INITIALIZED:
+ case XML_PARSING:
+ ps_parsing = XML_FINISHED;
+ /* fall through */
+ default:
+ return XML_STATUS_OK;
+ }
+ }
+ eventEndPtr = eventPtr;
+ processor = errorProcessor;
+ return XML_STATUS_ERROR;
+ }
+#ifndef XML_CONTEXT_BYTES
+ else if (bufferPtr == bufferEnd) {
+ const char *end;
+ int nLeftOver;
+ enum XML_Error result;
+ parseEndByteIndex += len;
+ positionPtr = s;
+ ps_finalBuffer = (XML_Bool)isFinal;
+
+ errorCode = processor(parser, s, parseEndPtr = s + len, &end);
+
+ if (errorCode != XML_ERROR_NONE) {
+ eventEndPtr = eventPtr;
+ processor = errorProcessor;
+ return XML_STATUS_ERROR;
+ }
+ else {
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ result = XML_STATUS_SUSPENDED;
+ break;
+ case XML_INITIALIZED:
+ case XML_PARSING:
+ result = XML_STATUS_OK;
+ if (isFinal) {
+ ps_parsing = XML_FINISHED;
+ return result;
+ }
+ }
+ }
+
+ XmlUpdatePosition(encoding, positionPtr, end, &position);
+ nLeftOver = s + len - end;
+ if (nLeftOver) {
+ if (buffer == NULL || nLeftOver > bufferLim - buffer) {
+ /* FIXME avoid integer overflow */
+ char *temp;
+ temp = (buffer == NULL
+ ? (char *)MALLOC(len * 2)
+ : (char *)REALLOC(buffer, len * 2));
+ if (temp == NULL) {
+ errorCode = XML_ERROR_NO_MEMORY;
+ return XML_STATUS_ERROR;
+ }
+ buffer = temp;
+ if (!buffer) {
+ errorCode = XML_ERROR_NO_MEMORY;
+ eventPtr = eventEndPtr = NULL;
+ processor = errorProcessor;
+ return XML_STATUS_ERROR;
+ }
+ bufferLim = buffer + len * 2;
+ }
+ memcpy(buffer, end, nLeftOver);
+ }
+ bufferPtr = buffer;
+ bufferEnd = buffer + nLeftOver;
+ positionPtr = bufferPtr;
+ parseEndPtr = bufferEnd;
+ eventPtr = bufferPtr;
+ eventEndPtr = bufferPtr;
+ return result;
+ }
+#endif /* not defined XML_CONTEXT_BYTES */
+ else {
+ void *buff = XML_GetBuffer(parser, len);
+ if (buff == NULL)
+ return XML_STATUS_ERROR;
+ else {
+ memcpy(buff, s, len);
+ return XML_ParseBuffer(parser, len, isFinal);
+ }
+ }
+}
+
+enum XML_Status XMLCALL
+XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
+{
+ const char *start;
+ enum XML_Status result = XML_STATUS_OK;
+
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ errorCode = XML_ERROR_SUSPENDED;
+ return XML_STATUS_ERROR;
+ case XML_FINISHED:
+ errorCode = XML_ERROR_FINISHED;
+ return XML_STATUS_ERROR;
+ default:
+ ps_parsing = XML_PARSING;
+ }
+
+ start = bufferPtr;
+ positionPtr = start;
+ bufferEnd += len;
+ parseEndPtr = bufferEnd;
+ parseEndByteIndex += len;
+ ps_finalBuffer = (XML_Bool)isFinal;
+
+ errorCode = processor(parser, start, parseEndPtr, &bufferPtr);
+
+ if (errorCode != XML_ERROR_NONE) {
+ eventEndPtr = eventPtr;
+ processor = errorProcessor;
+ return XML_STATUS_ERROR;
+ }
+ else {
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ result = XML_STATUS_SUSPENDED;
+ break;
+ case XML_INITIALIZED:
+ case XML_PARSING:
+ if (isFinal) {
+ ps_parsing = XML_FINISHED;
+ return result;
+ }
+ default: ; /* should not happen */
+ }
+ }
+
+ XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+ positionPtr = bufferPtr;
+ return result;
+}
+
+void * XMLCALL
+XML_GetBuffer(XML_Parser parser, int len)
+{
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ errorCode = XML_ERROR_SUSPENDED;
+ return NULL;
+ case XML_FINISHED:
+ errorCode = XML_ERROR_FINISHED;
+ return NULL;
+ default: ;
+ }
+
+ if (len > bufferLim - bufferEnd) {
+ /* FIXME avoid integer overflow */
+ int neededSize = len + (int)(bufferEnd - bufferPtr);
+#ifdef XML_CONTEXT_BYTES
+ int keep = (int)(bufferPtr - buffer);
+
+ if (keep > XML_CONTEXT_BYTES)
+ keep = XML_CONTEXT_BYTES;
+ neededSize += keep;
+#endif /* defined XML_CONTEXT_BYTES */
+ if (neededSize <= bufferLim - buffer) {
+#ifdef XML_CONTEXT_BYTES
+ if (keep < bufferPtr - buffer) {
+ int offset = (int)(bufferPtr - buffer) - keep;
+ memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep);
+ bufferEnd -= offset;
+ bufferPtr -= offset;
+ }
+#else
+ memmove(buffer, bufferPtr, bufferEnd - bufferPtr);
+ bufferEnd = buffer + (bufferEnd - bufferPtr);
+ bufferPtr = buffer;
+#endif /* not defined XML_CONTEXT_BYTES */
+ }
+ else {
+ char *newBuf;
+ int bufferSize = (int)(bufferLim - bufferPtr);
+ if (bufferSize == 0)
+ bufferSize = INIT_BUFFER_SIZE;
+ do {
+ bufferSize *= 2;
+ } while (bufferSize < neededSize);
+ newBuf = (char *)MALLOC(bufferSize);
+ if (newBuf == 0) {
+ errorCode = XML_ERROR_NO_MEMORY;
+ return NULL;
+ }
+ bufferLim = newBuf + bufferSize;
+#ifdef XML_CONTEXT_BYTES
+ if (bufferPtr) {
+ int keep = (int)(bufferPtr - buffer);
+ if (keep > XML_CONTEXT_BYTES)
+ keep = XML_CONTEXT_BYTES;
+ memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep);
+ FREE(buffer);
+ buffer = newBuf;
+ bufferEnd = buffer + (bufferEnd - bufferPtr) + keep;
+ bufferPtr = buffer + keep;
+ }
+ else {
+ bufferEnd = newBuf + (bufferEnd - bufferPtr);
+ bufferPtr = buffer = newBuf;
+ }
+#else
+ if (bufferPtr) {
+ memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr);
+ FREE(buffer);
+ }
+ bufferEnd = newBuf + (bufferEnd - bufferPtr);
+ bufferPtr = buffer = newBuf;
+#endif /* not defined XML_CONTEXT_BYTES */
+ }
+ }
+ return bufferEnd;
+}
+
+enum XML_Status XMLCALL
+XML_StopParser(XML_Parser parser, XML_Bool resumable)
+{
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ if (resumable) {
+ errorCode = XML_ERROR_SUSPENDED;
+ return XML_STATUS_ERROR;
+ }
+ ps_parsing = XML_FINISHED;
+ break;
+ case XML_FINISHED:
+ errorCode = XML_ERROR_FINISHED;
+ return XML_STATUS_ERROR;
+ default:
+ if (resumable) {
+#ifdef XML_DTD
+ if (isParamEntity) {
+ errorCode = XML_ERROR_SUSPEND_PE;
+ return XML_STATUS_ERROR;
+ }
+#endif
+ ps_parsing = XML_SUSPENDED;
+ }
+ else
+ ps_parsing = XML_FINISHED;
+ }
+ return XML_STATUS_OK;
+}
+
+enum XML_Status XMLCALL
+XML_ResumeParser(XML_Parser parser)
+{
+ enum XML_Status result = XML_STATUS_OK;
+
+ if (ps_parsing != XML_SUSPENDED) {
+ errorCode = XML_ERROR_NOT_SUSPENDED;
+ return XML_STATUS_ERROR;
+ }
+ ps_parsing = XML_PARSING;
+
+ errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr);
+
+ if (errorCode != XML_ERROR_NONE) {
+ eventEndPtr = eventPtr;
+ processor = errorProcessor;
+ return XML_STATUS_ERROR;
+ }
+ else {
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ result = XML_STATUS_SUSPENDED;
+ break;
+ case XML_INITIALIZED:
+ case XML_PARSING:
+ if (ps_finalBuffer) {
+ ps_parsing = XML_FINISHED;
+ return result;
+ }
+ default: ;
+ }
+ }
+
+ XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+ positionPtr = bufferPtr;
+ return result;
+}
+
+void XMLCALL
+XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status)
+{
+ assert(status != NULL);
+ *status = parser->m_parsingStatus;
+}
+
+enum XML_Error XMLCALL
+XML_GetErrorCode(XML_Parser parser)
+{
+ return errorCode;
+}
+
+XML_Index XMLCALL
+XML_GetCurrentByteIndex(XML_Parser parser)
+{
+ if (eventPtr)
+ return parseEndByteIndex - (parseEndPtr - eventPtr);
+ return -1;
+}
+
+int XMLCALL
+XML_GetCurrentByteCount(XML_Parser parser)
+{
+ if (eventEndPtr && eventPtr)
+ return (int)(eventEndPtr - eventPtr);
+ return 0;
+}
+
+const char * XMLCALL
+XML_GetInputContext(XML_Parser parser, int *offset, int *size)
+{
+#ifdef XML_CONTEXT_BYTES
+ if (eventPtr && buffer) {
+ *offset = (int)(eventPtr - buffer);
+ *size = (int)(bufferEnd - buffer);
+ return buffer;
+ }
+#endif /* defined XML_CONTEXT_BYTES */
+ return (char *) 0;
+}
+
+XML_Size XMLCALL
+XML_GetCurrentLineNumber(XML_Parser parser)
+{
+ if (eventPtr && eventPtr >= positionPtr) {
+ XmlUpdatePosition(encoding, positionPtr, eventPtr, &position);
+ positionPtr = eventPtr;
+ }
+ return position.lineNumber + 1;
+}
+
+XML_Size XMLCALL
+XML_GetCurrentColumnNumber(XML_Parser parser)
+{
+ if (eventPtr && eventPtr >= positionPtr) {
+ XmlUpdatePosition(encoding, positionPtr, eventPtr, &position);
+ positionPtr = eventPtr;
+ }
+ return position.columnNumber;
+}
+
+void XMLCALL
+XML_FreeContentModel(XML_Parser parser, XML_Content *model)
+{
+ FREE(model);
+}
+
+void * XMLCALL
+XML_MemMalloc(XML_Parser parser, size_t size)
+{
+ return MALLOC(size);
+}
+
+void * XMLCALL
+XML_MemRealloc(XML_Parser parser, void *ptr, size_t size)
+{
+ return REALLOC(ptr, size);
+}
+
+void XMLCALL
+XML_MemFree(XML_Parser parser, void *ptr)
+{
+ FREE(ptr);
+}
+
+void XMLCALL
+XML_DefaultCurrent(XML_Parser parser)
+{
+ if (defaultHandler) {
+ if (openInternalEntities)
+ reportDefault(parser,
+ internalEncoding,
+ openInternalEntities->internalEventPtr,
+ openInternalEntities->internalEventEndPtr);
+ else
+ reportDefault(parser, encoding, eventPtr, eventEndPtr);
+ }
+}
+
+const XML_LChar * XMLCALL
+XML_ErrorString(enum XML_Error code)
+{
+ static const XML_LChar* const message[] = {
+ 0,
+ XML_L("out of memory"),
+ XML_L("syntax error"),
+ XML_L("no element found"),
+ XML_L("not well-formed (invalid token)"),
+ XML_L("unclosed token"),
+ XML_L("partial character"),
+ XML_L("mismatched tag"),
+ XML_L("duplicate attribute"),
+ XML_L("junk after document element"),
+ XML_L("illegal parameter entity reference"),
+ XML_L("undefined entity"),
+ XML_L("recursive entity reference"),
+ XML_L("asynchronous entity"),
+ XML_L("reference to invalid character number"),
+ XML_L("reference to binary entity"),
+ XML_L("reference to external entity in attribute"),
+ XML_L("XML or text declaration not at start of entity"),
+ XML_L("unknown encoding"),
+ XML_L("encoding specified in XML declaration is incorrect"),
+ XML_L("unclosed CDATA section"),
+ XML_L("error in processing external entity reference"),
+ XML_L("document is not standalone"),
+ XML_L("unexpected parser state - please send a bug report"),
+ XML_L("entity declared in parameter entity"),
+ XML_L("requested feature requires XML_DTD support in Expat"),
+ XML_L("cannot change setting once parsing has begun"),
+ XML_L("unbound prefix"),
+ XML_L("must not undeclare prefix"),
+ XML_L("incomplete markup in parameter entity"),
+ XML_L("XML declaration not well-formed"),
+ XML_L("text declaration not well-formed"),
+ XML_L("illegal character(s) in public id"),
+ XML_L("parser suspended"),
+ XML_L("parser not suspended"),
+ XML_L("parsing aborted"),
+ XML_L("parsing finished"),
+ XML_L("cannot suspend in external parameter entity"),
+ XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"),
+ XML_L("reserved prefix (xmlns) must not be declared or undeclared"),
+ XML_L("prefix must not be bound to one of the reserved namespace names")
+ };
+ if (code > 0 && code < sizeof(message)/sizeof(message[0]))
+ return message[code];
+ return NULL;
+}
+
+const XML_LChar * XMLCALL
+XML_ExpatVersion(void) {
+
+ /* V1 is used to string-ize the version number. However, it would
+ string-ize the actual version macro *names* unless we get them
+ substituted before being passed to V1. CPP is defined to expand
+ a macro, then rescan for more expansions. Thus, we use V2 to expand
+ the version macros, then CPP will expand the resulting V1() macro
+ with the correct numerals. */
+ /* ### I'm assuming cpp is portable in this respect... */
+
+#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c)
+#define V2(a,b,c) XML_L("expat_")V1(a,b,c)
+
+ return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION);
+
+#undef V1
+#undef V2
+}
+
+XML_Expat_Version XMLCALL
+XML_ExpatVersionInfo(void)
+{
+ XML_Expat_Version version;
+
+ version.major = XML_MAJOR_VERSION;
+ version.minor = XML_MINOR_VERSION;
+ version.micro = XML_MICRO_VERSION;
+
+ return version;
+}
+
+const XML_Feature * XMLCALL
+XML_GetFeatureList(void)
+{
+ static const XML_Feature features[] = {
+ {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
+ sizeof(XML_Char)},
+ {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
+ sizeof(XML_LChar)},
+#ifdef XML_UNICODE
+ {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
+#endif
+#ifdef XML_UNICODE_WCHAR_T
+ {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
+#endif
+#ifdef XML_DTD
+ {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
+#endif
+#ifdef XML_CONTEXT_BYTES
+ {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
+ XML_CONTEXT_BYTES},
+#endif
+#ifdef XML_MIN_SIZE
+ {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
+#endif
+#ifdef XML_NS
+ {XML_FEATURE_NS, XML_L("XML_NS"), 0},
+#endif
+#ifdef XML_LARGE_SIZE
+ {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
+#endif
+ {XML_FEATURE_END, NULL, 0}
+ };
+
+ return features;
+}
+
+/* Initially tag->rawName always points into the parse buffer;
+ for those TAG instances opened while the current parse buffer was
+ processed, and not yet closed, we need to store tag->rawName in a more
+ permanent location, since the parse buffer is about to be discarded.
+*/
+static XML_Bool
+storeRawNames(XML_Parser parser)
+{
+ TAG *tag = tagStack;
+ while (tag) {
+ int bufSize;
+ int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
+ char *rawNameBuf = tag->buf + nameLen;
+ /* Stop if already stored. Since tagStack is a stack, we can stop
+ at the first entry that has already been copied; everything
+ below it in the stack is already been accounted for in a
+ previous call to this function.
+ */
+ if (tag->rawName == rawNameBuf)
+ break;
+ /* For re-use purposes we need to ensure that the
+ size of tag->buf is a multiple of sizeof(XML_Char).
+ */
+ bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+ if (bufSize > tag->bufEnd - tag->buf) {
+ char *temp = (char *)REALLOC(tag->buf, bufSize);
+ if (temp == NULL)
+ return XML_FALSE;
+ /* if tag->name.str points to tag->buf (only when namespace
+ processing is off) then we have to update it
+ */
+ if (tag->name.str == (XML_Char *)tag->buf)
+ tag->name.str = (XML_Char *)temp;
+ /* if tag->name.localPart is set (when namespace processing is on)
+ then update it as well, since it will always point into tag->buf
+ */
+ if (tag->name.localPart)
+ tag->name.localPart = (XML_Char *)temp + (tag->name.localPart -
+ (XML_Char *)tag->buf);
+ tag->buf = temp;
+ tag->bufEnd = temp + bufSize;
+ rawNameBuf = temp + nameLen;
+ }
+ memcpy(rawNameBuf, tag->rawName, tag->rawNameLength);
+ tag->rawName = rawNameBuf;
+ tag = tag->parent;
+ }
+ return XML_TRUE;
+}
+
+static enum XML_Error PTRCALL
+contentProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = doContent(parser, 0, encoding, start, end,
+ endPtr, (XML_Bool)!ps_finalBuffer);
+ if (result == XML_ERROR_NONE) {
+ if (!storeRawNames(parser))
+ return XML_ERROR_NO_MEMORY;
+ }
+ return result;
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = initializeEncoding(parser);
+ if (result != XML_ERROR_NONE)
+ return result;
+ processor = externalEntityInitProcessor2;
+ return externalEntityInitProcessor2(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor2(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ const char *next = start; /* XmlContentTok doesn't always set the last arg */
+ int tok = XmlContentTok(encoding, start, end, &next);
+ switch (tok) {
+ case XML_TOK_BOM:
+ /* If we are at the end of the buffer, this would cause the next stage,
+ i.e. externalEntityInitProcessor3, to pass control directly to
+ doContent (by detecting XML_TOK_NONE) without processing any xml text
+ declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent.
+ */
+ if (next == end && !ps_finalBuffer) {
+ *endPtr = next;
+ return XML_ERROR_NONE;
+ }
+ start = next;
+ break;
+ case XML_TOK_PARTIAL:
+ if (!ps_finalBuffer) {
+ *endPtr = start;
+ return XML_ERROR_NONE;
+ }
+ eventPtr = start;
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (!ps_finalBuffer) {
+ *endPtr = start;
+ return XML_ERROR_NONE;
+ }
+ eventPtr = start;
+ return XML_ERROR_PARTIAL_CHAR;
+ }
+ processor = externalEntityInitProcessor3;
+ return externalEntityInitProcessor3(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor3(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ int tok;
+ const char *next = start; /* XmlContentTok doesn't always set the last arg */
+ eventPtr = start;
+ tok = XmlContentTok(encoding, start, end, &next);
+ eventEndPtr = next;
+
+ switch (tok) {
+ case XML_TOK_XML_DECL:
+ {
+ enum XML_Error result;
+ result = processXmlDecl(parser, 1, start, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ *endPtr = next;
+ return XML_ERROR_NONE;
+ case XML_FINISHED:
+ return XML_ERROR_ABORTED;
+ default:
+ start = next;
+ }
+ }
+ break;
+ case XML_TOK_PARTIAL:
+ if (!ps_finalBuffer) {
+ *endPtr = start;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (!ps_finalBuffer) {
+ *endPtr = start;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ }
+ processor = externalEntityContentProcessor;
+ tagLevel = 1;
+ return externalEntityContentProcessor(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityContentProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = doContent(parser, 1, encoding, start, end,
+ endPtr, (XML_Bool)!ps_finalBuffer);
+ if (result == XML_ERROR_NONE) {
+ if (!storeRawNames(parser))
+ return XML_ERROR_NO_MEMORY;
+ }
+ return result;
+}
+
+static enum XML_Error
+doContent(XML_Parser parser,
+ int startTagLevel,
+ const ENCODING *enc,
+ const char *s,
+ const char *end,
+ const char **nextPtr,
+ XML_Bool haveMore)
+{
+ /* save one level of indirection */
+ DTD * const dtd = _dtd;
+
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ *eventPP = s;
+
+ for (;;) {
+ const char *next = s; /* XmlContentTok doesn't always set the last arg */
+ int tok = XmlContentTok(enc, s, end, &next);
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_TRAILING_CR:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ *eventEndPP = end;
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, end);
+ /* We are at the end of the final buffer, should we check for
+ XML_SUSPENDED, XML_FINISHED?
+ */
+ if (startTagLevel == 0)
+ return XML_ERROR_NO_ELEMENTS;
+ if (tagLevel != startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ *nextPtr = end;
+ return XML_ERROR_NONE;
+ case XML_TOK_NONE:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ if (startTagLevel > 0) {
+ if (tagLevel != startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_NO_ELEMENTS;
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_ENTITY_REF:
+ {
+ const XML_Char *name;
+ ENTITY *entity;
+ XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (ch) {
+ if (characterDataHandler)
+ characterDataHandler(handlerArg, &ch, 1);
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ name = poolStoreString(&dtd->pool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0);
+ poolDiscard(&dtd->pool);
+ /* First, determine if a check for an existing declaration is needed;
+ if yes, check that the entity exists, and that it is internal,
+ otherwise call the skipped entity or default handler.
+ */
+ if (!dtd->hasParamEntityRefs || dtd->standalone) {
+ if (!entity)
+ return XML_ERROR_UNDEFINED_ENTITY;
+ else if (!entity->is_internal)
+ return XML_ERROR_ENTITY_DECLARED_IN_PE;
+ }
+ else if (!entity) {
+ if (skippedEntityHandler)
+ skippedEntityHandler(handlerArg, name, 0);
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ if (entity->open)
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ if (entity->notation)
+ return XML_ERROR_BINARY_ENTITY_REF;
+ if (entity->textPtr) {
+ enum XML_Error result;
+ if (!defaultExpandInternalEntities) {
+ if (skippedEntityHandler)
+ skippedEntityHandler(handlerArg, entity->name, 0);
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ result = processInternalEntity(parser, entity, XML_FALSE);
+ if (result != XML_ERROR_NONE)
+ return result;
+ }
+ else if (externalEntityRefHandler) {
+ const XML_Char *context;
+ entity->open = XML_TRUE;
+ context = getContext(parser);
+ entity->open = XML_FALSE;
+ if (!context)
+ return XML_ERROR_NO_MEMORY;
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ context,
+ entity->base,
+ entity->systemId,
+ entity->publicId))
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ poolDiscard(&tempPool);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ case XML_TOK_START_TAG_NO_ATTS:
+ /* fall through */
+ case XML_TOK_START_TAG_WITH_ATTS:
+ {
+ TAG *tag;
+ enum XML_Error result;
+ XML_Char *toPtr;
+ if (freeTagList) {
+ tag = freeTagList;
+ freeTagList = freeTagList->parent;
+ }
+ else {
+ tag = (TAG *)MALLOC(sizeof(TAG));
+ if (!tag)
+ return XML_ERROR_NO_MEMORY;
+ tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE);
+ if (!tag->buf) {
+ FREE(tag);
+ return XML_ERROR_NO_MEMORY;
+ }
+ tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE;
+ }
+ tag->bindings = NULL;
+ tag->parent = tagStack;
+ tagStack = tag;
+ tag->name.localPart = NULL;
+ tag->name.prefix = NULL;
+ tag->rawName = s + enc->minBytesPerChar;
+ tag->rawNameLength = XmlNameLength(enc, tag->rawName);
+ ++tagLevel;
+ {
+ const char *rawNameEnd = tag->rawName + tag->rawNameLength;
+ const char *fromPtr = tag->rawName;
+ toPtr = (XML_Char *)tag->buf;
+ for (;;) {
+ int bufSize;
+ int convLen;
+ XmlConvert(enc,
+ &fromPtr, rawNameEnd,
+ (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1);
+ convLen = (int)(toPtr - (XML_Char *)tag->buf);
+ if (fromPtr == rawNameEnd) {
+ tag->name.strLen = convLen;
+ break;
+ }
+ bufSize = (int)(tag->bufEnd - tag->buf) << 1;
+ {
+ char *temp = (char *)REALLOC(tag->buf, bufSize);
+ if (temp == NULL)
+ return XML_ERROR_NO_MEMORY;
+ tag->buf = temp;
+ tag->bufEnd = temp + bufSize;
+ toPtr = (XML_Char *)temp + convLen;
+ }
+ }
+ }
+ tag->name.str = (XML_Char *)tag->buf;
+ *toPtr = XML_T('\0');
+ result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
+ if (result)
+ return result;
+ if (startElementHandler)
+ startElementHandler(handlerArg, tag->name.str,
+ (const XML_Char **)atts);
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ poolClear(&tempPool);
+ break;
+ }
+ case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
+ /* fall through */
+ case XML_TOK_EMPTY_ELEMENT_WITH_ATTS:
+ {
+ const char *rawName = s + enc->minBytesPerChar;
+ enum XML_Error result;
+ BINDING *bindings = NULL;
+ XML_Bool noElmHandlers = XML_TRUE;
+ TAG_NAME name;
+ name.str = poolStoreString(&tempPool, enc, rawName,
+ rawName + XmlNameLength(enc, rawName));
+ if (!name.str)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ result = storeAtts(parser, enc, s, &name, &bindings);
+ if (result)
+ return result;
+ poolFinish(&tempPool);
+ if (startElementHandler) {
+ startElementHandler(handlerArg, name.str, (const XML_Char **)atts);
+ noElmHandlers = XML_FALSE;
+ }
+ if (endElementHandler) {
+ if (startElementHandler)
+ *eventPP = *eventEndPP;
+ endElementHandler(handlerArg, name.str);
+ noElmHandlers = XML_FALSE;
+ }
+ if (noElmHandlers && defaultHandler)
+ reportDefault(parser, enc, s, next);
+ poolClear(&tempPool);
+ while (bindings) {
+ BINDING *b = bindings;
+ if (endNamespaceDeclHandler)
+ endNamespaceDeclHandler(handlerArg, b->prefix->name);
+ bindings = bindings->nextTagBinding;
+ b->nextTagBinding = freeBindingList;
+ freeBindingList = b;
+ b->prefix->binding = b->prevPrefixBinding;
+ }
+ }
+ if (tagLevel == 0)
+ return epilogProcessor(parser, next, end, nextPtr);
+ break;
+ case XML_TOK_END_TAG:
+ if (tagLevel == startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ else {
+ int len;
+ const char *rawName;
+ TAG *tag = tagStack;
+ tagStack = tag->parent;
+ tag->parent = freeTagList;
+ freeTagList = tag;
+ rawName = s + enc->minBytesPerChar*2;
+ len = XmlNameLength(enc, rawName);
+ if (len != tag->rawNameLength
+ || memcmp(tag->rawName, rawName, len) != 0) {
+ *eventPP = rawName;
+ return XML_ERROR_TAG_MISMATCH;
+ }
+ --tagLevel;
+ if (endElementHandler) {
+ const XML_Char *localPart;
+ const XML_Char *prefix;
+ XML_Char *uri;
+ localPart = tag->name.localPart;
+ if (ns && localPart) {
+ /* localPart and prefix may have been overwritten in
+ tag->name.str, since this points to the binding->uri
+ buffer which gets re-used; so we have to add them again
+ */
+ uri = (XML_Char *)tag->name.str + tag->name.uriLen;
+ /* don't need to check for space - already done in storeAtts() */
+ while (*localPart) *uri++ = *localPart++;
+ prefix = (XML_Char *)tag->name.prefix;
+ if (ns_triplets && prefix) {
+ *uri++ = namespaceSeparator;
+ while (*prefix) *uri++ = *prefix++;
+ }
+ *uri = XML_T('\0');
+ }
+ endElementHandler(handlerArg, tag->name.str);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ while (tag->bindings) {
+ BINDING *b = tag->bindings;
+ if (endNamespaceDeclHandler)
+ endNamespaceDeclHandler(handlerArg, b->prefix->name);
+ tag->bindings = tag->bindings->nextTagBinding;
+ b->nextTagBinding = freeBindingList;
+ freeBindingList = b;
+ b->prefix->binding = b->prevPrefixBinding;
+ }
+ if (tagLevel == 0)
+ return epilogProcessor(parser, next, end, nextPtr);
+ }
+ break;
+ case XML_TOK_CHAR_REF:
+ {
+ int n = XmlCharRefNumber(enc, s);
+ if (n < 0)
+ return XML_ERROR_BAD_CHAR_REF;
+ if (characterDataHandler) {
+ XML_Char buf[XML_ENCODE_MAX];
+ characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf));
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ case XML_TOK_XML_DECL:
+ return XML_ERROR_MISPLACED_XML_PI;
+ case XML_TOK_DATA_NEWLINE:
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_CDATA_SECT_OPEN:
+ {
+ enum XML_Error result;
+ if (startCdataSectionHandler)
+ startCdataSectionHandler(handlerArg);
+#if 0
+ /* Suppose you doing a transformation on a document that involves
+ changing only the character data. You set up a defaultHandler
+ and a characterDataHandler. The defaultHandler simply copies
+ characters through. The characterDataHandler does the
+ transformation and writes the characters out escaping them as
+ necessary. This case will fail to work if we leave out the
+ following two lines (because & and < inside CDATA sections will
+ be incorrectly escaped).
+
+ However, now we have a start/endCdataSectionHandler, so it seems
+ easier to let the user deal with this.
+ */
+ else if (characterDataHandler)
+ characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore);
+ if (result != XML_ERROR_NONE)
+ return result;
+ else if (!next) {
+ processor = cdataSectionProcessor;
+ return result;
+ }
+ }
+ break;
+ case XML_TOK_TRAILING_RSQB:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ if (characterDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+ characterDataHandler(handlerArg, dataBuf,
+ (int)(dataPtr - (ICHAR *)dataBuf));
+ }
+ else
+ characterDataHandler(handlerArg,
+ (XML_Char *)s,
+ (int)((XML_Char *)end - (XML_Char *)s));
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, end);
+ /* We are at the end of the final buffer, should we check for
+ XML_SUSPENDED, XML_FINISHED?
+ */
+ if (startTagLevel == 0) {
+ *eventPP = end;
+ return XML_ERROR_NO_ELEMENTS;
+ }
+ if (tagLevel != startTagLevel) {
+ *eventPP = end;
+ return XML_ERROR_ASYNC_ENTITY;
+ }
+ *nextPtr = end;
+ return XML_ERROR_NONE;
+ case XML_TOK_DATA_CHARS:
+ {
+ XML_CharacterDataHandler charDataHandler = characterDataHandler;
+ if (charDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ for (;;) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = s;
+ charDataHandler(handlerArg, dataBuf,
+ (int)(dataPtr - (ICHAR *)dataBuf));
+ if (s == next)
+ break;
+ *eventPP = s;
+ }
+ }
+ else
+ charDataHandler(handlerArg,
+ (XML_Char *)s,
+ (int)((XML_Char *)next - (XML_Char *)s));
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ case XML_TOK_PI:
+ if (!reportProcessingInstruction(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_COMMENT:
+ if (!reportComment(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ default:
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ *eventPP = s = next;
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ *nextPtr = next;
+ return XML_ERROR_NONE;
+ case XML_FINISHED:
+ return XML_ERROR_ABORTED;
+ default: ;
+ }
+ }
+ /* not reached */
+}
+
+/* Precondition: all arguments must be non-NULL;
+ Purpose:
+ - normalize attributes
+ - check attributes for well-formedness
+ - generate namespace aware attribute names (URI, prefix)
+ - build list of attributes for startElementHandler
+ - default attributes
+ - process namespace declarations (check and report them)
+ - generate namespace aware element name (URI, prefix)
+*/
+static enum XML_Error
+storeAtts(XML_Parser parser, const ENCODING *enc,
+ const char *attStr, TAG_NAME *tagNamePtr,
+ BINDING **bindingsPtr)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ ELEMENT_TYPE *elementType;
+ int nDefaultAtts;
+ const XML_Char **appAtts; /* the attribute list for the application */
+ int attIndex = 0;
+ int prefixLen;
+ int i;
+ int n;
+ XML_Char *uri;
+ int nPrefixes = 0;
+ BINDING *binding;
+ const XML_Char *localPart;
+
+ /* lookup the element type name */
+ elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, tagNamePtr->str,0);
+ if (!elementType) {
+ const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, name,
+ sizeof(ELEMENT_TYPE));
+ if (!elementType)
+ return XML_ERROR_NO_MEMORY;
+ if (ns && !setElementTypePrefix(parser, elementType))
+ return XML_ERROR_NO_MEMORY;
+ }
+ nDefaultAtts = elementType->nDefaultAtts;
+
+ /* get the attributes from the tokenizer */
+ n = XmlGetAttributes(enc, attStr, attsSize, atts);
+ if (n + nDefaultAtts > attsSize) {
+ int oldAttsSize = attsSize;
+ ATTRIBUTE *temp;
+ attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+ temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE));
+ if (temp == NULL)
+ return XML_ERROR_NO_MEMORY;
+ atts = temp;
+ if (n > oldAttsSize)
+ XmlGetAttributes(enc, attStr, n, atts);
+ }
+
+ appAtts = (const XML_Char **)atts;
+ for (i = 0; i < n; i++) {
+ /* add the name and value to the attribute list */
+ ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name,
+ atts[i].name
+ + XmlNameLength(enc, atts[i].name));
+ if (!attId)
+ return XML_ERROR_NO_MEMORY;
+ /* Detect duplicate attributes by their QNames. This does not work when
+ namespace processing is turned on and different prefixes for the same
+ namespace are used. For this case we have a check further down.
+ */
+ if ((attId->name)[-1]) {
+ if (enc == encoding)
+ eventPtr = atts[i].name;
+ return XML_ERROR_DUPLICATE_ATTRIBUTE;
+ }
+ (attId->name)[-1] = 1;
+ appAtts[attIndex++] = attId->name;
+ if (!atts[i].normalized) {
+ enum XML_Error result;
+ XML_Bool isCdata = XML_TRUE;
+
+ /* figure out whether declared as other than CDATA */
+ if (attId->maybeTokenized) {
+ int j;
+ for (j = 0; j < nDefaultAtts; j++) {
+ if (attId == elementType->defaultAtts[j].id) {
+ isCdata = elementType->defaultAtts[j].isCdata;
+ break;
+ }
+ }
+ }
+
+ /* normalize the attribute value */
+ result = storeAttributeValue(parser, enc, isCdata,
+ atts[i].valuePtr, atts[i].valueEnd,
+ &tempPool);
+ if (result)
+ return result;
+ appAtts[attIndex] = poolStart(&tempPool);
+ poolFinish(&tempPool);
+ }
+ else {
+ /* the value did not need normalizing */
+ appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr,
+ atts[i].valueEnd);
+ if (appAtts[attIndex] == 0)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ }
+ /* handle prefixed attribute names */
+ if (attId->prefix) {
+ if (attId->xmlns) {
+ /* deal with namespace declarations here */
+ enum XML_Error result = addBinding(parser, attId->prefix, attId,
+ appAtts[attIndex], bindingsPtr);
+ if (result)
+ return result;
+ --attIndex;
+ }
+ else {
+ /* deal with other prefixed names later */
+ attIndex++;
+ nPrefixes++;
+ (attId->name)[-1] = 2;
+ }
+ }
+ else
+ attIndex++;
+ }
+
+ /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */
+ nSpecifiedAtts = attIndex;
+ if (elementType->idAtt && (elementType->idAtt->name)[-1]) {
+ for (i = 0; i < attIndex; i += 2)
+ if (appAtts[i] == elementType->idAtt->name) {
+ idAttIndex = i;
+ break;
+ }
+ }
+ else
+ idAttIndex = -1;
+
+ /* do attribute defaulting */
+ for (i = 0; i < nDefaultAtts; i++) {
+ const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i;
+ if (!(da->id->name)[-1] && da->value) {
+ if (da->id->prefix) {
+ if (da->id->xmlns) {
+ enum XML_Error result = addBinding(parser, da->id->prefix, da->id,
+ da->value, bindingsPtr);
+ if (result)
+ return result;
+ }
+ else {
+ (da->id->name)[-1] = 2;
+ nPrefixes++;
+ appAtts[attIndex++] = da->id->name;
+ appAtts[attIndex++] = da->value;
+ }
+ }
+ else {
+ (da->id->name)[-1] = 1;
+ appAtts[attIndex++] = da->id->name;
+ appAtts[attIndex++] = da->value;
+ }
+ }
+ }
+ appAtts[attIndex] = 0;
+
+ /* expand prefixed attribute names, check for duplicates,
+ and clear flags that say whether attributes were specified */
+ i = 0;
+ if (nPrefixes) {
+ int j; /* hash table index */
+ unsigned long version = nsAttsVersion;
+ int nsAttsSize = (int)1 << nsAttsPower;
+ /* size of hash table must be at least 2 * (# of prefixed attributes) */
+ if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */
+ NS_ATT *temp;
+ /* hash table size must also be a power of 2 and >= 8 */
+ while (nPrefixes >> nsAttsPower++);
+ if (nsAttsPower < 3)
+ nsAttsPower = 3;
+ nsAttsSize = (int)1 << nsAttsPower;
+ temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT));
+ if (!temp)
+ return XML_ERROR_NO_MEMORY;
+ nsAtts = temp;
+ version = 0; /* force re-initialization of nsAtts hash table */
+ }
+ /* using a version flag saves us from initializing nsAtts every time */
+ if (!version) { /* initialize version flags when version wraps around */
+ version = INIT_ATTS_VERSION;
+ for (j = nsAttsSize; j != 0; )
+ nsAtts[--j].version = version;
+ }
+ nsAttsVersion = --version;
+
+ /* expand prefixed names and check for duplicates */
+ for (; i < attIndex; i += 2) {
+ const XML_Char *s = appAtts[i];
+ if (s[-1] == 2) { /* prefixed */
+ ATTRIBUTE_ID *id;
+ const BINDING *b;
+ unsigned long uriHash = 0;
+ ((XML_Char *)s)[-1] = 0; /* clear flag */
+ id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, s, 0);
+ b = id->prefix->binding;
+ if (!b)
+ return XML_ERROR_UNBOUND_PREFIX;
+
+ /* as we expand the name we also calculate its hash value */
+ for (j = 0; j < b->uriLen; j++) {
+ const XML_Char c = b->uri[j];
+ if (!poolAppendChar(&tempPool, c))
+ return XML_ERROR_NO_MEMORY;
+ uriHash = CHAR_HASH(uriHash, c);
+ }
+ while (*s++ != XML_T(ASCII_COLON))
+ ;
+ do { /* copies null terminator */
+ const XML_Char c = *s;
+ if (!poolAppendChar(&tempPool, *s))
+ return XML_ERROR_NO_MEMORY;
+ uriHash = CHAR_HASH(uriHash, c);
+ } while (*s++);
+
+ { /* Check hash table for duplicate of expanded name (uriName).
+ Derived from code in lookup(HASH_TABLE *table, ...).
+ */
+ unsigned char step = 0;
+ unsigned long mask = nsAttsSize - 1;
+ j = uriHash & mask; /* index into hash table */
+ while (nsAtts[j].version == version) {
+ /* for speed we compare stored hash values first */
+ if (uriHash == nsAtts[j].hash) {
+ const XML_Char *s1 = poolStart(&tempPool);
+ const XML_Char *s2 = nsAtts[j].uriName;
+ /* s1 is null terminated, but not s2 */
+ for (; *s1 == *s2 && *s1 != 0; s1++, s2++);
+ if (*s1 == 0)
+ return XML_ERROR_DUPLICATE_ATTRIBUTE;
+ }
+ if (!step)
+ step = PROBE_STEP(uriHash, mask, nsAttsPower);
+ j < step ? (j += nsAttsSize - step) : (j -= step);
+ }
+ }
+
+ if (ns_triplets) { /* append namespace separator and prefix */
+ tempPool.ptr[-1] = namespaceSeparator;
+ s = b->prefix->name;
+ do {
+ if (!poolAppendChar(&tempPool, *s))
+ return XML_ERROR_NO_MEMORY;
+ } while (*s++);
+ }
+
+ /* store expanded name in attribute list */
+ s = poolStart(&tempPool);
+ poolFinish(&tempPool);
+ appAtts[i] = s;
+
+ /* fill empty slot with new version, uriName and hash value */
+ nsAtts[j].version = version;
+ nsAtts[j].hash = uriHash;
+ nsAtts[j].uriName = s;
+
+ if (!--nPrefixes) {
+ i += 2;
+ break;
+ }
+ }
+ else /* not prefixed */
+ ((XML_Char *)s)[-1] = 0; /* clear flag */
+ }
+ }
+ /* clear flags for the remaining attributes */
+ for (; i < attIndex; i += 2)
+ ((XML_Char *)(appAtts[i]))[-1] = 0;
+ for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding)
+ binding->attId->name[-1] = 0;
+
+ if (!ns)
+ return XML_ERROR_NONE;
+
+ /* expand the element type name */
+ if (elementType->prefix) {
+ binding = elementType->prefix->binding;
+ if (!binding)
+ return XML_ERROR_UNBOUND_PREFIX;
+ localPart = tagNamePtr->str;
+ while (*localPart++ != XML_T(ASCII_COLON))
+ ;
+ }
+ else if (dtd->defaultPrefix.binding) {
+ binding = dtd->defaultPrefix.binding;
+ localPart = tagNamePtr->str;
+ }
+ else
+ return XML_ERROR_NONE;
+ prefixLen = 0;
+ if (ns_triplets && binding->prefix->name) {
+ for (; binding->prefix->name[prefixLen++];)
+ ; /* prefixLen includes null terminator */
+ }
+ tagNamePtr->localPart = localPart;
+ tagNamePtr->uriLen = binding->uriLen;
+ tagNamePtr->prefix = binding->prefix->name;
+ tagNamePtr->prefixLen = prefixLen;
+ for (i = 0; localPart[i++];)
+ ; /* i includes null terminator */
+ n = i + binding->uriLen + prefixLen;
+ if (n > binding->uriAlloc) {
+ TAG *p;
+ uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char));
+ if (!uri)
+ return XML_ERROR_NO_MEMORY;
+ binding->uriAlloc = n + EXPAND_SPARE;
+ memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char));
+ for (p = tagStack; p; p = p->parent)
+ if (p->name.str == binding->uri)
+ p->name.str = uri;
+ FREE(binding->uri);
+ binding->uri = uri;
+ }
+ /* if namespaceSeparator != '\0' then uri includes it already */
+ uri = binding->uri + binding->uriLen;
+ memcpy(uri, localPart, i * sizeof(XML_Char));
+ /* we always have a namespace separator between localPart and prefix */
+ if (prefixLen) {
+ uri += i - 1;
+ *uri = namespaceSeparator; /* replace null terminator */
+ memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char));
+ }
+ tagNamePtr->str = binding->uri;
+ return XML_ERROR_NONE;
+}
+
+/* addBinding() overwrites the value of prefix->binding without checking.
+ Therefore one must keep track of the old value outside of addBinding().
+*/
+static enum XML_Error
+addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
+ const XML_Char *uri, BINDING **bindingsPtr)
+{
+ static const XML_Char xmlNamespace[] = {
+ ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH,
+ ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD,
+ ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L,
+ ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH,
+ ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c,
+ ASCII_e, '\0'
+ };
+ static const int xmlLen =
+ (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1;
+ static const XML_Char xmlnsNamespace[] = {
+ ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH,
+ ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD,
+ ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0,
+ ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s,
+ ASCII_SLASH, '\0'
+ };
+ static const int xmlnsLen =
+ (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1;
+
+ XML_Bool mustBeXML = XML_FALSE;
+ XML_Bool isXML = XML_TRUE;
+ XML_Bool isXMLNS = XML_TRUE;
+
+ BINDING *b;
+ int len;
+
+ /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */
+ if (*uri == XML_T('\0') && prefix->name)
+ return XML_ERROR_UNDECLARING_PREFIX;
+
+ if (prefix->name
+ && prefix->name[0] == XML_T(ASCII_x)
+ && prefix->name[1] == XML_T(ASCII_m)
+ && prefix->name[2] == XML_T(ASCII_l)) {
+
+ /* Not allowed to bind xmlns */
+ if (prefix->name[3] == XML_T(ASCII_n)
+ && prefix->name[4] == XML_T(ASCII_s)
+ && prefix->name[5] == XML_T('\0'))
+ return XML_ERROR_RESERVED_PREFIX_XMLNS;
+
+ if (prefix->name[3] == XML_T('\0'))
+ mustBeXML = XML_TRUE;
+ }
+
+ for (len = 0; uri[len]; len++) {
+ if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len]))
+ isXML = XML_FALSE;
+
+ if (!mustBeXML && isXMLNS
+ && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
+ isXMLNS = XML_FALSE;
+ }
+ isXML = isXML && len == xmlLen;
+ isXMLNS = isXMLNS && len == xmlnsLen;
+
+ if (mustBeXML != isXML)
+ return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML
+ : XML_ERROR_RESERVED_NAMESPACE_URI;
+
+ if (isXMLNS)
+ return XML_ERROR_RESERVED_NAMESPACE_URI;
+
+ if (namespaceSeparator)
+ len++;
+ if (freeBindingList) {
+ b = freeBindingList;
+ if (len > b->uriAlloc) {
+ XML_Char *temp = (XML_Char *)REALLOC(b->uri,
+ sizeof(XML_Char) * (len + EXPAND_SPARE));
+ if (temp == NULL)
+ return XML_ERROR_NO_MEMORY;
+ b->uri = temp;
+ b->uriAlloc = len + EXPAND_SPARE;
+ }
+ freeBindingList = b->nextTagBinding;
+ }
+ else {
+ b = (BINDING *)MALLOC(sizeof(BINDING));
+ if (!b)
+ return XML_ERROR_NO_MEMORY;
+ b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE));
+ if (!b->uri) {
+ FREE(b);
+ return XML_ERROR_NO_MEMORY;
+ }
+ b->uriAlloc = len + EXPAND_SPARE;
+ }
+ b->uriLen = len;
+ memcpy(b->uri, uri, len * sizeof(XML_Char));
+ if (namespaceSeparator)
+ b->uri[len - 1] = namespaceSeparator;
+ b->prefix = prefix;
+ b->attId = attId;
+ b->prevPrefixBinding = prefix->binding;
+ /* NULL binding when default namespace undeclared */
+ if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix)
+ prefix->binding = NULL;
+ else
+ prefix->binding = b;
+ b->nextTagBinding = *bindingsPtr;
+ *bindingsPtr = b;
+ /* if attId == NULL then we are not starting a namespace scope */
+ if (attId && startNamespaceDeclHandler)
+ startNamespaceDeclHandler(handlerArg, prefix->name,
+ prefix->binding ? uri : 0);
+ return XML_ERROR_NONE;
+}
+
+/* The idea here is to avoid using stack for each CDATA section when
+ the whole file is parsed with one call.
+*/
+static enum XML_Error PTRCALL
+cdataSectionProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = doCdataSection(parser, encoding, &start, end,
+ endPtr, (XML_Bool)!ps_finalBuffer);
+ if (result != XML_ERROR_NONE)
+ return result;
+ if (start) {
+ if (parentParser) { /* we are parsing an external entity */
+ processor = externalEntityContentProcessor;
+ return externalEntityContentProcessor(parser, start, end, endPtr);
+ }
+ else {
+ processor = contentProcessor;
+ return contentProcessor(parser, start, end, endPtr);
+ }
+ }
+ return result;
+}
+
+/* startPtr gets set to non-null if the section is closed, and to null if
+ the section is not yet closed.
+*/
+static enum XML_Error
+doCdataSection(XML_Parser parser,
+ const ENCODING *enc,
+ const char **startPtr,
+ const char *end,
+ const char **nextPtr,
+ XML_Bool haveMore)
+{
+ const char *s = *startPtr;
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ *eventPP = s;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ *eventPP = s;
+ *startPtr = NULL;
+
+ for (;;) {
+ const char *next;
+ int tok = XmlCdataSectionTok(enc, s, end, &next);
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_CDATA_SECT_CLOSE:
+ if (endCdataSectionHandler)
+ endCdataSectionHandler(handlerArg);
+#if 0
+ /* see comment under XML_TOK_CDATA_SECT_OPEN */
+ else if (characterDataHandler)
+ characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ *startPtr = next;
+ *nextPtr = next;
+ if (ps_parsing == XML_FINISHED)
+ return XML_ERROR_ABORTED;
+ else
+ return XML_ERROR_NONE;
+ case XML_TOK_DATA_NEWLINE:
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_DATA_CHARS:
+ {
+ XML_CharacterDataHandler charDataHandler = characterDataHandler;
+ if (charDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ for (;;) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = next;
+ charDataHandler(handlerArg, dataBuf,
+ (int)(dataPtr - (ICHAR *)dataBuf));
+ if (s == next)
+ break;
+ *eventPP = s;
+ }
+ }
+ else
+ charDataHandler(handlerArg,
+ (XML_Char *)s,
+ (int)((XML_Char *)next - (XML_Char *)s));
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_PARTIAL:
+ case XML_TOK_NONE:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_CDATA_SECTION;
+ default:
+ *eventPP = next;
+ return XML_ERROR_UNEXPECTED_STATE;
+ }
+
+ *eventPP = s = next;
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ *nextPtr = next;
+ return XML_ERROR_NONE;
+ case XML_FINISHED:
+ return XML_ERROR_ABORTED;
+ default: ;
+ }
+ }
+ /* not reached */
+}
+
+#ifdef XML_DTD
+
+/* The idea here is to avoid using stack for each IGNORE section when
+ the whole file is parsed with one call.
+*/
+static enum XML_Error PTRCALL
+ignoreSectionProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = doIgnoreSection(parser, encoding, &start, end,
+ endPtr, (XML_Bool)!ps_finalBuffer);
+ if (result != XML_ERROR_NONE)
+ return result;
+ if (start) {
+ processor = prologProcessor;
+ return prologProcessor(parser, start, end, endPtr);
+ }
+ return result;
+}
+
+/* startPtr gets set to non-null is the section is closed, and to null
+ if the section is not yet closed.
+*/
+static enum XML_Error
+doIgnoreSection(XML_Parser parser,
+ const ENCODING *enc,
+ const char **startPtr,
+ const char *end,
+ const char **nextPtr,
+ XML_Bool haveMore)
+{
+ const char *next;
+ int tok;
+ const char *s = *startPtr;
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ *eventPP = s;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ *eventPP = s;
+ *startPtr = NULL;
+ tok = XmlIgnoreSectionTok(enc, s, end, &next);
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_IGNORE_SECT:
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ *startPtr = next;
+ *nextPtr = next;
+ if (ps_parsing == XML_FINISHED)
+ return XML_ERROR_ABORTED;
+ else
+ return XML_ERROR_NONE;
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_PARTIAL:
+ case XML_TOK_NONE:
+ if (haveMore) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */
+ default:
+ *eventPP = next;
+ return XML_ERROR_UNEXPECTED_STATE;
+ }
+ /* not reached */
+}
+
+#endif /* XML_DTD */
+
+static enum XML_Error
+initializeEncoding(XML_Parser parser)
+{
+ const char *s;
+#ifdef XML_UNICODE
+ char encodingBuf[128];
+ if (!protocolEncodingName)
+ s = NULL;
+ else {
+ int i;
+ for (i = 0; protocolEncodingName[i]; i++) {
+ if (i == sizeof(encodingBuf) - 1
+ || (protocolEncodingName[i] & ~0x7f) != 0) {
+ encodingBuf[0] = '\0';
+ break;
+ }
+ encodingBuf[i] = (char)protocolEncodingName[i];
+ }
+ encodingBuf[i] = '\0';
+ s = encodingBuf;
+ }
+#else
+ s = protocolEncodingName;
+#endif
+ if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s))
+ return XML_ERROR_NONE;
+ return handleUnknownEncoding(parser, protocolEncodingName);
+}
+
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
+ const char *s, const char *next)
+{
+ const char *encodingName = NULL;
+ const XML_Char *storedEncName = NULL;
+ const ENCODING *newEncoding = NULL;
+ const char *version = NULL;
+ const char *versionend;
+ const XML_Char *storedversion = NULL;
+ int standalone = -1;
+ if (!(ns
+ ? XmlParseXmlDeclNS
+ : XmlParseXmlDecl)(isGeneralTextEntity,
+ encoding,
+ s,
+ next,
+ &eventPtr,
+ &version,
+ &versionend,
+ &encodingName,
+ &newEncoding,
+ &standalone)) {
+ if (isGeneralTextEntity)
+ return XML_ERROR_TEXT_DECL;
+ else
+ return XML_ERROR_XML_DECL;
+ }
+ if (!isGeneralTextEntity && standalone == 1) {
+ _dtd->standalone = XML_TRUE;
+#ifdef XML_DTD
+ if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
+ paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+#endif /* XML_DTD */
+ }
+ if (xmlDeclHandler) {
+ if (encodingName != NULL) {
+ storedEncName = poolStoreString(&temp2Pool,
+ encoding,
+ encodingName,
+ encodingName
+ + XmlNameLength(encoding, encodingName));
+ if (!storedEncName)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&temp2Pool);
+ }
+ if (version) {
+ storedversion = poolStoreString(&temp2Pool,
+ encoding,
+ version,
+ versionend - encoding->minBytesPerChar);
+ if (!storedversion)
+ return XML_ERROR_NO_MEMORY;
+ }
+ xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, encoding, s, next);
+ if (protocolEncodingName == NULL) {
+ if (newEncoding) {
+ if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) {
+ eventPtr = encodingName;
+ return XML_ERROR_INCORRECT_ENCODING;
+ }
+ encoding = newEncoding;
+ }
+ else if (encodingName) {
+ enum XML_Error result;
+ if (!storedEncName) {
+ storedEncName = poolStoreString(
+ &temp2Pool, encoding, encodingName,
+ encodingName + XmlNameLength(encoding, encodingName));
+ if (!storedEncName)
+ return XML_ERROR_NO_MEMORY;
+ }
+ result = handleUnknownEncoding(parser, storedEncName);
+ poolClear(&temp2Pool);
+ if (result == XML_ERROR_UNKNOWN_ENCODING)
+ eventPtr = encodingName;
+ return result;
+ }
+ }
+
+ if (storedEncName || storedversion)
+ poolClear(&temp2Pool);
+
+ return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName)
+{
+ if (unknownEncodingHandler) {
+ XML_Encoding info;
+ int i;
+ for (i = 0; i < 256; i++)
+ info.map[i] = -1;
+ info.convert = NULL;
+ info.data = NULL;
+ info.release = NULL;
+ if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName,
+ &info)) {
+ ENCODING *enc;
+ unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding());
+ if (!unknownEncodingMem) {
+ if (info.release)
+ info.release(info.data);
+ return XML_ERROR_NO_MEMORY;
+ }
+ enc = (ns
+ ? XmlInitUnknownEncodingNS
+ : XmlInitUnknownEncoding)(unknownEncodingMem,
+ info.map,
+ info.convert,
+ info.data);
+ if (enc) {
+ unknownEncodingData = info.data;
+ unknownEncodingRelease = info.release;
+ encoding = enc;
+ return XML_ERROR_NONE;
+ }
+ }
+ if (info.release != NULL)
+ info.release(info.data);
+ }
+ return XML_ERROR_UNKNOWN_ENCODING;
+}
+
+static enum XML_Error PTRCALL
+prologInitProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ enum XML_Error result = initializeEncoding(parser);
+ if (result != XML_ERROR_NONE)
+ return result;
+ processor = prologProcessor;
+ return prologProcessor(parser, s, end, nextPtr);
+}
+
+#ifdef XML_DTD
+
+static enum XML_Error PTRCALL
+externalParEntInitProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ enum XML_Error result = initializeEncoding(parser);
+ if (result != XML_ERROR_NONE)
+ return result;
+
+ /* we know now that XML_Parse(Buffer) has been called,
+ so we consider the external parameter entity read */
+ _dtd->paramEntityRead = XML_TRUE;
+
+ if (prologState.inEntityValue) {
+ processor = entityValueInitProcessor;
+ return entityValueInitProcessor(parser, s, end, nextPtr);
+ }
+ else {
+ processor = externalParEntProcessor;
+ return externalParEntProcessor(parser, s, end, nextPtr);
+ }
+}
+
+static enum XML_Error PTRCALL
+entityValueInitProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ int tok;
+ const char *start = s;
+ const char *next = start;
+ eventPtr = start;
+
+ for (;;) {
+ tok = XmlPrologTok(encoding, start, end, &next);
+ eventEndPtr = next;
+ if (tok <= 0) {
+ if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ switch (tok) {
+ case XML_TOK_INVALID:
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_NONE: /* start == end */
+ default:
+ break;
+ }
+ /* found end of entity value - can store it now */
+ return storeEntityValue(parser, encoding, s, end);
+ }
+ else if (tok == XML_TOK_XML_DECL) {
+ enum XML_Error result;
+ result = processXmlDecl(parser, 0, start, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ *nextPtr = next;
+ return XML_ERROR_NONE;
+ case XML_FINISHED:
+ return XML_ERROR_ABORTED;
+ default:
+ *nextPtr = next;
+ }
+ /* stop scanning for text declaration - we found one */
+ processor = entityValueProcessor;
+ return entityValueProcessor(parser, next, end, nextPtr);
+ }
+ /* If we are at the end of the buffer, this would cause XmlPrologTok to
+ return XML_TOK_NONE on the next call, which would then cause the
+ function to exit with *nextPtr set to s - that is what we want for other
+ tokens, but not for the BOM - we would rather like to skip it;
+ then, when this routine is entered the next time, XmlPrologTok will
+ return XML_TOK_INVALID, since the BOM is still in the buffer
+ */
+ else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) {
+ *nextPtr = next;
+ return XML_ERROR_NONE;
+ }
+ start = next;
+ eventPtr = start;
+ }
+}
+
+static enum XML_Error PTRCALL
+externalParEntProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ const char *next = s;
+ int tok;
+
+ tok = XmlPrologTok(encoding, s, end, &next);
+ if (tok <= 0) {
+ if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ switch (tok) {
+ case XML_TOK_INVALID:
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_NONE: /* start == end */
+ default:
+ break;
+ }
+ }
+ /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM.
+ However, when parsing an external subset, doProlog will not accept a BOM
+ as valid, and report a syntax error, so we have to skip the BOM
+ */
+ else if (tok == XML_TOK_BOM) {
+ s = next;
+ tok = XmlPrologTok(encoding, s, end, &next);
+ }
+
+ processor = prologProcessor;
+ return doProlog(parser, encoding, s, end, tok, next,
+ nextPtr, (XML_Bool)!ps_finalBuffer);
+}
+
+static enum XML_Error PTRCALL
+entityValueProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ const char *start = s;
+ const char *next = s;
+ const ENCODING *enc = encoding;
+ int tok;
+
+ for (;;) {
+ tok = XmlPrologTok(enc, start, end, &next);
+ if (tok <= 0) {
+ if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ switch (tok) {
+ case XML_TOK_INVALID:
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_NONE: /* start == end */
+ default:
+ break;
+ }
+ /* found end of entity value - can store it now */
+ return storeEntityValue(parser, enc, s, end);
+ }
+ start = next;
+ }
+}
+
+#endif /* XML_DTD */
+
+static enum XML_Error PTRCALL
+prologProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ const char *next = s;
+ int tok = XmlPrologTok(encoding, s, end, &next);
+ return doProlog(parser, encoding, s, end, tok, next,
+ nextPtr, (XML_Bool)!ps_finalBuffer);
+}
+
+static enum XML_Error
+doProlog(XML_Parser parser,
+ const ENCODING *enc,
+ const char *s,
+ const char *end,
+ int tok,
+ const char *next,
+ const char **nextPtr,
+ XML_Bool haveMore)
+{
+#ifdef XML_DTD
+ static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' };
+#endif /* XML_DTD */
+ static const XML_Char atypeCDATA[] =
+ { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+ static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' };
+ static const XML_Char atypeIDREF[] =
+ { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' };
+ static const XML_Char atypeIDREFS[] =
+ { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' };
+ static const XML_Char atypeENTITY[] =
+ { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' };
+ static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N,
+ ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' };
+ static const XML_Char atypeNMTOKEN[] = {
+ ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' };
+ static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T,
+ ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' };
+ static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T,
+ ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' };
+ static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' };
+ static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' };
+
+ /* save one level of indirection */
+ DTD * const dtd = _dtd;
+
+ const char **eventPP;
+ const char **eventEndPP;
+ enum XML_Content_Quant quant;
+
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+
+ for (;;) {
+ int role;
+ XML_Bool handleDefault = XML_TRUE;
+ *eventPP = s;
+ *eventEndPP = next;
+ if (tok <= 0) {
+ if (haveMore && tok != XML_TOK_INVALID) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ switch (tok) {
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_NONE:
+#ifdef XML_DTD
+ /* for internal PE NOT referenced between declarations */
+ if (enc != encoding && !openInternalEntities->betweenDecl) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ /* WFC: PE Between Declarations - must check that PE contains
+ complete markup, not only for external PEs, but also for
+ internal PEs if the reference occurs between declarations.
+ */
+ if (isParamEntity || enc != encoding) {
+ if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc)
+ == XML_ROLE_ERROR)
+ return XML_ERROR_INCOMPLETE_PE;
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+#endif /* XML_DTD */
+ return XML_ERROR_NO_ELEMENTS;
+ default:
+ tok = -tok;
+ next = end;
+ break;
+ }
+ }
+ role = XmlTokenRole(&prologState, tok, s, next, enc);
+ switch (role) {
+ case XML_ROLE_XML_DECL:
+ {
+ enum XML_Error result = processXmlDecl(parser, 0, s, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ enc = encoding;
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_DOCTYPE_NAME:
+ if (startDoctypeDeclHandler) {
+ doctypeName = poolStoreString(&tempPool, enc, s, next);
+ if (!doctypeName)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ doctypePubid = NULL;
+ handleDefault = XML_FALSE;
+ }
+ doctypeSysid = NULL; /* always initialize to NULL */
+ break;
+ case XML_ROLE_DOCTYPE_INTERNAL_SUBSET:
+ if (startDoctypeDeclHandler) {
+ startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid,
+ doctypePubid, 1);
+ doctypeName = NULL;
+ poolClear(&tempPool);
+ handleDefault = XML_FALSE;
+ }
+ break;
+#ifdef XML_DTD
+ case XML_ROLE_TEXT_DECL:
+ {
+ enum XML_Error result = processXmlDecl(parser, 1, s, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ enc = encoding;
+ handleDefault = XML_FALSE;
+ }
+ break;
+#endif /* XML_DTD */
+ case XML_ROLE_DOCTYPE_PUBLIC_ID:
+#ifdef XML_DTD
+ useForeignDTD = XML_FALSE;
+ declEntity = (ENTITY *)lookup(&dtd->paramEntities,
+ externalSubsetName,
+ sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+#endif /* XML_DTD */
+ dtd->hasParamEntityRefs = XML_TRUE;
+ if (startDoctypeDeclHandler) {
+ if (!XmlIsPublicId(enc, s, next, eventPP))
+ return XML_ERROR_PUBLICID;
+ doctypePubid = poolStoreString(&tempPool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!doctypePubid)
+ return XML_ERROR_NO_MEMORY;
+ normalizePublicId((XML_Char *)doctypePubid);
+ poolFinish(&tempPool);
+ handleDefault = XML_FALSE;
+ goto alreadyChecked;
+ }
+ /* fall through */
+ case XML_ROLE_ENTITY_PUBLIC_ID:
+ if (!XmlIsPublicId(enc, s, next, eventPP))
+ return XML_ERROR_PUBLICID;
+ alreadyChecked:
+ if (dtd->keepProcessing && declEntity) {
+ XML_Char *tem = poolStoreString(&dtd->pool,
+ enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!tem)
+ return XML_ERROR_NO_MEMORY;
+ normalizePublicId(tem);
+ declEntity->publicId = tem;
+ poolFinish(&dtd->pool);
+ if (entityDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_DOCTYPE_CLOSE:
+ if (doctypeName) {
+ startDoctypeDeclHandler(handlerArg, doctypeName,
+ doctypeSysid, doctypePubid, 0);
+ poolClear(&tempPool);
+ handleDefault = XML_FALSE;
+ }
+ /* doctypeSysid will be non-NULL in the case of a previous
+ XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler
+ was not set, indicating an external subset
+ */
+#ifdef XML_DTD
+ if (doctypeSysid || useForeignDTD) {
+ XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
+ dtd->hasParamEntityRefs = XML_TRUE;
+ if (paramEntityParsing && externalEntityRefHandler) {
+ ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities,
+ externalSubsetName,
+ sizeof(ENTITY));
+ if (!entity)
+ return XML_ERROR_NO_MEMORY;
+ if (useForeignDTD)
+ entity->base = curBase;
+ dtd->paramEntityRead = XML_FALSE;
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ 0,
+ entity->base,
+ entity->systemId,
+ entity->publicId))
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ if (dtd->paramEntityRead) {
+ if (!dtd->standalone &&
+ notStandaloneHandler &&
+ !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+ }
+ /* if we didn't read the foreign DTD then this means that there
+ is no external subset and we must reset dtd->hasParamEntityRefs
+ */
+ else if (!doctypeSysid)
+ dtd->hasParamEntityRefs = hadParamEntityRefs;
+ /* end of DTD - no need to update dtd->keepProcessing */
+ }
+ useForeignDTD = XML_FALSE;
+ }
+#endif /* XML_DTD */
+ if (endDoctypeDeclHandler) {
+ endDoctypeDeclHandler(handlerArg);
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_INSTANCE_START:
+#ifdef XML_DTD
+ /* if there is no DOCTYPE declaration then now is the
+ last chance to read the foreign DTD
+ */
+ if (useForeignDTD) {
+ XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
+ dtd->hasParamEntityRefs = XML_TRUE;
+ if (paramEntityParsing && externalEntityRefHandler) {
+ ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities,
+ externalSubsetName,
+ sizeof(ENTITY));
+ if (!entity)
+ return XML_ERROR_NO_MEMORY;
+ entity->base = curBase;
+ dtd->paramEntityRead = XML_FALSE;
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ 0,
+ entity->base,
+ entity->systemId,
+ entity->publicId))
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ if (dtd->paramEntityRead) {
+ if (!dtd->standalone &&
+ notStandaloneHandler &&
+ !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+ }
+ /* if we didn't read the foreign DTD then this means that there
+ is no external subset and we must reset dtd->hasParamEntityRefs
+ */
+ else
+ dtd->hasParamEntityRefs = hadParamEntityRefs;
+ /* end of DTD - no need to update dtd->keepProcessing */
+ }
+ }
+#endif /* XML_DTD */
+ processor = contentProcessor;
+ return contentProcessor(parser, s, end, nextPtr);
+ case XML_ROLE_ATTLIST_ELEMENT_NAME:
+ declElementType = getElementType(parser, enc, s, next);
+ if (!declElementType)
+ return XML_ERROR_NO_MEMORY;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_NAME:
+ declAttributeId = getAttributeId(parser, enc, s, next);
+ if (!declAttributeId)
+ return XML_ERROR_NO_MEMORY;
+ declAttributeIsCdata = XML_FALSE;
+ declAttributeType = NULL;
+ declAttributeIsId = XML_FALSE;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_TYPE_CDATA:
+ declAttributeIsCdata = XML_TRUE;
+ declAttributeType = atypeCDATA;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_TYPE_ID:
+ declAttributeIsId = XML_TRUE;
+ declAttributeType = atypeID;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_TYPE_IDREF:
+ declAttributeType = atypeIDREF;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_TYPE_IDREFS:
+ declAttributeType = atypeIDREFS;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_TYPE_ENTITY:
+ declAttributeType = atypeENTITY;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES:
+ declAttributeType = atypeENTITIES;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN:
+ declAttributeType = atypeNMTOKEN;
+ goto checkAttListDeclHandler;
+ case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS:
+ declAttributeType = atypeNMTOKENS;
+ checkAttListDeclHandler:
+ if (dtd->keepProcessing && attlistDeclHandler)
+ handleDefault = XML_FALSE;
+ break;
+ case XML_ROLE_ATTRIBUTE_ENUM_VALUE:
+ case XML_ROLE_ATTRIBUTE_NOTATION_VALUE:
+ if (dtd->keepProcessing && attlistDeclHandler) {
+ const XML_Char *prefix;
+ if (declAttributeType) {
+ prefix = enumValueSep;
+ }
+ else {
+ prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE
+ ? notationPrefix
+ : enumValueStart);
+ }
+ if (!poolAppendString(&tempPool, prefix))
+ return XML_ERROR_NO_MEMORY;
+ if (!poolAppend(&tempPool, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ declAttributeType = tempPool.start;
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE:
+ case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE:
+ if (dtd->keepProcessing) {
+ if (!defineAttribute(declElementType, declAttributeId,
+ declAttributeIsCdata, declAttributeIsId,
+ 0, parser))
+ return XML_ERROR_NO_MEMORY;
+ if (attlistDeclHandler && declAttributeType) {
+ if (*declAttributeType == XML_T(ASCII_LPAREN)
+ || (*declAttributeType == XML_T(ASCII_N)
+ && declAttributeType[1] == XML_T(ASCII_O))) {
+ /* Enumerated or Notation type */
+ if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN))
+ || !poolAppendChar(&tempPool, XML_T('\0')))
+ return XML_ERROR_NO_MEMORY;
+ declAttributeType = tempPool.start;
+ poolFinish(&tempPool);
+ }
+ *eventEndPP = s;
+ attlistDeclHandler(handlerArg, declElementType->name,
+ declAttributeId->name, declAttributeType,
+ 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE);
+ poolClear(&tempPool);
+ handleDefault = XML_FALSE;
+ }
+ }
+ break;
+ case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE:
+ case XML_ROLE_FIXED_ATTRIBUTE_VALUE:
+ if (dtd->keepProcessing) {
+ const XML_Char *attVal;
+ enum XML_Error result =
+ storeAttributeValue(parser, enc, declAttributeIsCdata,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar,
+ &dtd->pool);
+ if (result)
+ return result;
+ attVal = poolStart(&dtd->pool);
+ poolFinish(&dtd->pool);
+ /* ID attributes aren't allowed to have a default */
+ if (!defineAttribute(declElementType, declAttributeId,
+ declAttributeIsCdata, XML_FALSE, attVal, parser))
+ return XML_ERROR_NO_MEMORY;
+ if (attlistDeclHandler && declAttributeType) {
+ if (*declAttributeType == XML_T(ASCII_LPAREN)
+ || (*declAttributeType == XML_T(ASCII_N)
+ && declAttributeType[1] == XML_T(ASCII_O))) {
+ /* Enumerated or Notation type */
+ if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN))
+ || !poolAppendChar(&tempPool, XML_T('\0')))
+ return XML_ERROR_NO_MEMORY;
+ declAttributeType = tempPool.start;
+ poolFinish(&tempPool);
+ }
+ *eventEndPP = s;
+ attlistDeclHandler(handlerArg, declElementType->name,
+ declAttributeId->name, declAttributeType,
+ attVal,
+ role == XML_ROLE_FIXED_ATTRIBUTE_VALUE);
+ poolClear(&tempPool);
+ handleDefault = XML_FALSE;
+ }
+ }
+ break;
+ case XML_ROLE_ENTITY_VALUE:
+ if (dtd->keepProcessing) {
+ enum XML_Error result = storeEntityValue(parser, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (declEntity) {
+ declEntity->textPtr = poolStart(&dtd->entityValuePool);
+ declEntity->textLen = (int)(poolLength(&dtd->entityValuePool));
+ poolFinish(&dtd->entityValuePool);
+ if (entityDeclHandler) {
+ *eventEndPP = s;
+ entityDeclHandler(handlerArg,
+ declEntity->name,
+ declEntity->is_param,
+ declEntity->textPtr,
+ declEntity->textLen,
+ curBase, 0, 0, 0);
+ handleDefault = XML_FALSE;
+ }
+ }
+ else
+ poolDiscard(&dtd->entityValuePool);
+ if (result != XML_ERROR_NONE)
+ return result;
+ }
+ break;
+ case XML_ROLE_DOCTYPE_SYSTEM_ID:
+#ifdef XML_DTD
+ useForeignDTD = XML_FALSE;
+#endif /* XML_DTD */
+ dtd->hasParamEntityRefs = XML_TRUE;
+ if (startDoctypeDeclHandler) {
+ doctypeSysid = poolStoreString(&tempPool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (doctypeSysid == NULL)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ handleDefault = XML_FALSE;
+ }
+#ifdef XML_DTD
+ else
+ /* use externalSubsetName to make doctypeSysid non-NULL
+ for the case where no startDoctypeDeclHandler is set */
+ doctypeSysid = externalSubsetName;
+#endif /* XML_DTD */
+ if (!dtd->standalone
+#ifdef XML_DTD
+ && !paramEntityParsing
+#endif /* XML_DTD */
+ && notStandaloneHandler
+ && !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+#ifndef XML_DTD
+ break;
+#else /* XML_DTD */
+ if (!declEntity) {
+ declEntity = (ENTITY *)lookup(&dtd->paramEntities,
+ externalSubsetName,
+ sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+ declEntity->publicId = NULL;
+ }
+ /* fall through */
+#endif /* XML_DTD */
+ case XML_ROLE_ENTITY_SYSTEM_ID:
+ if (dtd->keepProcessing && declEntity) {
+ declEntity->systemId = poolStoreString(&dtd->pool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!declEntity->systemId)
+ return XML_ERROR_NO_MEMORY;
+ declEntity->base = curBase;
+ poolFinish(&dtd->pool);
+ if (entityDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_ENTITY_COMPLETE:
+ if (dtd->keepProcessing && declEntity && entityDeclHandler) {
+ *eventEndPP = s;
+ entityDeclHandler(handlerArg,
+ declEntity->name,
+ declEntity->is_param,
+ 0,0,
+ declEntity->base,
+ declEntity->systemId,
+ declEntity->publicId,
+ 0);
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_ENTITY_NOTATION_NAME:
+ if (dtd->keepProcessing && declEntity) {
+ declEntity->notation = poolStoreString(&dtd->pool, enc, s, next);
+ if (!declEntity->notation)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&dtd->pool);
+ if (unparsedEntityDeclHandler) {
+ *eventEndPP = s;
+ unparsedEntityDeclHandler(handlerArg,
+ declEntity->name,
+ declEntity->base,
+ declEntity->systemId,
+ declEntity->publicId,
+ declEntity->notation);
+ handleDefault = XML_FALSE;
+ }
+ else if (entityDeclHandler) {
+ *eventEndPP = s;
+ entityDeclHandler(handlerArg,
+ declEntity->name,
+ 0,0,0,
+ declEntity->base,
+ declEntity->systemId,
+ declEntity->publicId,
+ declEntity->notation);
+ handleDefault = XML_FALSE;
+ }
+ }
+ break;
+ case XML_ROLE_GENERAL_ENTITY_NAME:
+ {
+ if (XmlPredefinedEntityName(enc, s, next)) {
+ declEntity = NULL;
+ break;
+ }
+ if (dtd->keepProcessing) {
+ const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ declEntity = (ENTITY *)lookup(&dtd->generalEntities, name,
+ sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+ if (declEntity->name != name) {
+ poolDiscard(&dtd->pool);
+ declEntity = NULL;
+ }
+ else {
+ poolFinish(&dtd->pool);
+ declEntity->publicId = NULL;
+ declEntity->is_param = XML_FALSE;
+ /* if we have a parent parser or are reading an internal parameter
+ entity, then the entity declaration is not considered "internal"
+ */
+ declEntity->is_internal = !(parentParser || openInternalEntities);
+ if (entityDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+ }
+ else {
+ poolDiscard(&dtd->pool);
+ declEntity = NULL;
+ }
+ }
+ break;
+ case XML_ROLE_PARAM_ENTITY_NAME:
+#ifdef XML_DTD
+ if (dtd->keepProcessing) {
+ const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ declEntity = (ENTITY *)lookup(&dtd->paramEntities,
+ name, sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+ if (declEntity->name != name) {
+ poolDiscard(&dtd->pool);
+ declEntity = NULL;
+ }
+ else {
+ poolFinish(&dtd->pool);
+ declEntity->publicId = NULL;
+ declEntity->is_param = XML_TRUE;
+ /* if we have a parent parser or are reading an internal parameter
+ entity, then the entity declaration is not considered "internal"
+ */
+ declEntity->is_internal = !(parentParser || openInternalEntities);
+ if (entityDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+ }
+ else {
+ poolDiscard(&dtd->pool);
+ declEntity = NULL;
+ }
+#else /* not XML_DTD */
+ declEntity = NULL;
+#endif /* XML_DTD */
+ break;
+ case XML_ROLE_NOTATION_NAME:
+ declNotationPublicId = NULL;
+ declNotationName = NULL;
+ if (notationDeclHandler) {
+ declNotationName = poolStoreString(&tempPool, enc, s, next);
+ if (!declNotationName)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_NOTATION_PUBLIC_ID:
+ if (!XmlIsPublicId(enc, s, next, eventPP))
+ return XML_ERROR_PUBLICID;
+ if (declNotationName) { /* means notationDeclHandler != NULL */
+ XML_Char *tem = poolStoreString(&tempPool,
+ enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!tem)
+ return XML_ERROR_NO_MEMORY;
+ normalizePublicId(tem);
+ declNotationPublicId = tem;
+ poolFinish(&tempPool);
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_NOTATION_SYSTEM_ID:
+ if (declNotationName && notationDeclHandler) {
+ const XML_Char *systemId
+ = poolStoreString(&tempPool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!systemId)
+ return XML_ERROR_NO_MEMORY;
+ *eventEndPP = s;
+ notationDeclHandler(handlerArg,
+ declNotationName,
+ curBase,
+ systemId,
+ declNotationPublicId);
+ handleDefault = XML_FALSE;
+ }
+ poolClear(&tempPool);
+ break;
+ case XML_ROLE_NOTATION_NO_SYSTEM_ID:
+ if (declNotationPublicId && notationDeclHandler) {
+ *eventEndPP = s;
+ notationDeclHandler(handlerArg,
+ declNotationName,
+ curBase,
+ 0,
+ declNotationPublicId);
+ handleDefault = XML_FALSE;
+ }
+ poolClear(&tempPool);
+ break;
+ case XML_ROLE_ERROR:
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+ /* PE references in internal subset are
+ not allowed within declarations. */
+ return XML_ERROR_PARAM_ENTITY_REF;
+ case XML_TOK_XML_DECL:
+ return XML_ERROR_MISPLACED_XML_PI;
+ default:
+ return XML_ERROR_SYNTAX;
+ }
+#ifdef XML_DTD
+ case XML_ROLE_IGNORE_SECT:
+ {
+ enum XML_Error result;
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ handleDefault = XML_FALSE;
+ result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore);
+ if (result != XML_ERROR_NONE)
+ return result;
+ else if (!next) {
+ processor = ignoreSectionProcessor;
+ return result;
+ }
+ }
+ break;
+#endif /* XML_DTD */
+ case XML_ROLE_GROUP_OPEN:
+ if (prologState.level >= groupSize) {
+ if (groupSize) {
+ char *temp = (char *)REALLOC(groupConnector, groupSize *= 2);
+ if (temp == NULL)
+ return XML_ERROR_NO_MEMORY;
+ groupConnector = temp;
+ if (dtd->scaffIndex) {
+ int *temp = (int *)REALLOC(dtd->scaffIndex,
+ groupSize * sizeof(int));
+ if (temp == NULL)
+ return XML_ERROR_NO_MEMORY;
+ dtd->scaffIndex = temp;
+ }
+ }
+ else {
+ groupConnector = (char *)MALLOC(groupSize = 32);
+ if (!groupConnector)
+ return XML_ERROR_NO_MEMORY;
+ }
+ }
+ groupConnector[prologState.level] = 0;
+ if (dtd->in_eldecl) {
+ int myindex = nextScaffoldPart(parser);
+ if (myindex < 0)
+ return XML_ERROR_NO_MEMORY;
+ dtd->scaffIndex[dtd->scaffLevel] = myindex;
+ dtd->scaffLevel++;
+ dtd->scaffold[myindex].type = XML_CTYPE_SEQ;
+ if (elementDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+ break;
+ case XML_ROLE_GROUP_SEQUENCE:
+ if (groupConnector[prologState.level] == ASCII_PIPE)
+ return XML_ERROR_SYNTAX;
+ groupConnector[prologState.level] = ASCII_COMMA;
+ if (dtd->in_eldecl && elementDeclHandler)
+ handleDefault = XML_FALSE;
+ break;
+ case XML_ROLE_GROUP_CHOICE:
+ if (groupConnector[prologState.level] == ASCII_COMMA)
+ return XML_ERROR_SYNTAX;
+ if (dtd->in_eldecl
+ && !groupConnector[prologState.level]
+ && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+ != XML_CTYPE_MIXED)
+ ) {
+ dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+ = XML_CTYPE_CHOICE;
+ if (elementDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+ groupConnector[prologState.level] = ASCII_PIPE;
+ break;
+ case XML_ROLE_PARAM_ENTITY_REF:
+#ifdef XML_DTD
+ case XML_ROLE_INNER_PARAM_ENTITY_REF:
+ dtd->hasParamEntityRefs = XML_TRUE;
+ if (!paramEntityParsing)
+ dtd->keepProcessing = dtd->standalone;
+ else {
+ const XML_Char *name;
+ ENTITY *entity;
+ name = poolStoreString(&dtd->pool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0);
+ poolDiscard(&dtd->pool);
+ /* first, determine if a check for an existing declaration is needed;
+ if yes, check that the entity exists, and that it is internal,
+ otherwise call the skipped entity handler
+ */
+ if (prologState.documentEntity &&
+ (dtd->standalone
+ ? !openInternalEntities
+ : !dtd->hasParamEntityRefs)) {
+ if (!entity)
+ return XML_ERROR_UNDEFINED_ENTITY;
+ else if (!entity->is_internal)
+ return XML_ERROR_ENTITY_DECLARED_IN_PE;
+ }
+ else if (!entity) {
+ dtd->keepProcessing = dtd->standalone;
+ /* cannot report skipped entities in declarations */
+ if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) {
+ skippedEntityHandler(handlerArg, name, 1);
+ handleDefault = XML_FALSE;
+ }
+ break;
+ }
+ if (entity->open)
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ if (entity->textPtr) {
+ enum XML_Error result;
+ XML_Bool betweenDecl =
+ (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE);
+ result = processInternalEntity(parser, entity, betweenDecl);
+ if (result != XML_ERROR_NONE)
+ return result;
+ handleDefault = XML_FALSE;
+ break;
+ }
+ if (externalEntityRefHandler) {
+ dtd->paramEntityRead = XML_FALSE;
+ entity->open = XML_TRUE;
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ 0,
+ entity->base,
+ entity->systemId,
+ entity->publicId)) {
+ entity->open = XML_FALSE;
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ }
+ entity->open = XML_FALSE;
+ handleDefault = XML_FALSE;
+ if (!dtd->paramEntityRead) {
+ dtd->keepProcessing = dtd->standalone;
+ break;
+ }
+ }
+ else {
+ dtd->keepProcessing = dtd->standalone;
+ break;
+ }
+ }
+#endif /* XML_DTD */
+ if (!dtd->standalone &&
+ notStandaloneHandler &&
+ !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+ break;
+
+ /* Element declaration stuff */
+
+ case XML_ROLE_ELEMENT_NAME:
+ if (elementDeclHandler) {
+ declElementType = getElementType(parser, enc, s, next);
+ if (!declElementType)
+ return XML_ERROR_NO_MEMORY;
+ dtd->scaffLevel = 0;
+ dtd->scaffCount = 0;
+ dtd->in_eldecl = XML_TRUE;
+ handleDefault = XML_FALSE;
+ }
+ break;
+
+ case XML_ROLE_CONTENT_ANY:
+ case XML_ROLE_CONTENT_EMPTY:
+ if (dtd->in_eldecl) {
+ if (elementDeclHandler) {
+ XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content));
+ if (!content)
+ return XML_ERROR_NO_MEMORY;
+ content->quant = XML_CQUANT_NONE;
+ content->name = NULL;
+ content->numchildren = 0;
+ content->children = NULL;
+ content->type = ((role == XML_ROLE_CONTENT_ANY) ?
+ XML_CTYPE_ANY :
+ XML_CTYPE_EMPTY);
+ *eventEndPP = s;
+ elementDeclHandler(handlerArg, declElementType->name, content);
+ handleDefault = XML_FALSE;
+ }
+ dtd->in_eldecl = XML_FALSE;
+ }
+ break;
+
+ case XML_ROLE_CONTENT_PCDATA:
+ if (dtd->in_eldecl) {
+ dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+ = XML_CTYPE_MIXED;
+ if (elementDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+ break;
+
+ case XML_ROLE_CONTENT_ELEMENT:
+ quant = XML_CQUANT_NONE;
+ goto elementContent;
+ case XML_ROLE_CONTENT_ELEMENT_OPT:
+ quant = XML_CQUANT_OPT;
+ goto elementContent;
+ case XML_ROLE_CONTENT_ELEMENT_REP:
+ quant = XML_CQUANT_REP;
+ goto elementContent;
+ case XML_ROLE_CONTENT_ELEMENT_PLUS:
+ quant = XML_CQUANT_PLUS;
+ elementContent:
+ if (dtd->in_eldecl) {
+ ELEMENT_TYPE *el;
+ const XML_Char *name;
+ int nameLen;
+ const char *nxt = (quant == XML_CQUANT_NONE
+ ? next
+ : next - enc->minBytesPerChar);
+ int myindex = nextScaffoldPart(parser);
+ if (myindex < 0)
+ return XML_ERROR_NO_MEMORY;
+ dtd->scaffold[myindex].type = XML_CTYPE_NAME;
+ dtd->scaffold[myindex].quant = quant;
+ el = getElementType(parser, enc, s, nxt);
+ if (!el)
+ return XML_ERROR_NO_MEMORY;
+ name = el->name;
+ dtd->scaffold[myindex].name = name;
+ nameLen = 0;
+ for (; name[nameLen++]; );
+ dtd->contentStringLen += nameLen;
+ if (elementDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+ break;
+
+ case XML_ROLE_GROUP_CLOSE:
+ quant = XML_CQUANT_NONE;
+ goto closeGroup;
+ case XML_ROLE_GROUP_CLOSE_OPT:
+ quant = XML_CQUANT_OPT;
+ goto closeGroup;
+ case XML_ROLE_GROUP_CLOSE_REP:
+ quant = XML_CQUANT_REP;
+ goto closeGroup;
+ case XML_ROLE_GROUP_CLOSE_PLUS:
+ quant = XML_CQUANT_PLUS;
+ closeGroup:
+ if (dtd->in_eldecl) {
+ if (elementDeclHandler)
+ handleDefault = XML_FALSE;
+ dtd->scaffLevel--;
+ dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant;
+ if (dtd->scaffLevel == 0) {
+ if (!handleDefault) {
+ XML_Content *model = build_model(parser);
+ if (!model)
+ return XML_ERROR_NO_MEMORY;
+ *eventEndPP = s;
+ elementDeclHandler(handlerArg, declElementType->name, model);
+ }
+ dtd->in_eldecl = XML_FALSE;
+ dtd->contentStringLen = 0;
+ }
+ }
+ break;
+ /* End element declaration stuff */
+
+ case XML_ROLE_PI:
+ if (!reportProcessingInstruction(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ handleDefault = XML_FALSE;
+ break;
+ case XML_ROLE_COMMENT:
+ if (!reportComment(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ handleDefault = XML_FALSE;
+ break;
+ case XML_ROLE_NONE:
+ switch (tok) {
+ case XML_TOK_BOM:
+ handleDefault = XML_FALSE;
+ break;
+ }
+ break;
+ case XML_ROLE_DOCTYPE_NONE:
+ if (startDoctypeDeclHandler)
+ handleDefault = XML_FALSE;
+ break;
+ case XML_ROLE_ENTITY_NONE:
+ if (dtd->keepProcessing && entityDeclHandler)
+ handleDefault = XML_FALSE;
+ break;
+ case XML_ROLE_NOTATION_NONE:
+ if (notationDeclHandler)
+ handleDefault = XML_FALSE;
+ break;
+ case XML_ROLE_ATTLIST_NONE:
+ if (dtd->keepProcessing && attlistDeclHandler)
+ handleDefault = XML_FALSE;
+ break;
+ case XML_ROLE_ELEMENT_NONE:
+ if (elementDeclHandler)
+ handleDefault = XML_FALSE;
+ break;
+ } /* end of big switch */
+
+ if (handleDefault && defaultHandler)
+ reportDefault(parser, enc, s, next);
+
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ *nextPtr = next;
+ return XML_ERROR_NONE;
+ case XML_FINISHED:
+ return XML_ERROR_ABORTED;
+ default:
+ s = next;
+ tok = XmlPrologTok(enc, s, end, &next);
+ }
+ }
+ /* not reached */
+}
+
+static enum XML_Error PTRCALL
+epilogProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ processor = epilogProcessor;
+ eventPtr = s;
+ for (;;) {
+ const char *next = NULL;
+ int tok = XmlPrologTok(encoding, s, end, &next);
+ eventEndPtr = next;
+ switch (tok) {
+ /* report partial linebreak - it might be the last token */
+ case -XML_TOK_PROLOG_S:
+ if (defaultHandler) {
+ reportDefault(parser, encoding, s, next);
+ if (ps_parsing == XML_FINISHED)
+ return XML_ERROR_ABORTED;
+ }
+ *nextPtr = next;
+ return XML_ERROR_NONE;
+ case XML_TOK_NONE:
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ case XML_TOK_PROLOG_S:
+ if (defaultHandler)
+ reportDefault(parser, encoding, s, next);
+ break;
+ case XML_TOK_PI:
+ if (!reportProcessingInstruction(parser, encoding, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_COMMENT:
+ if (!reportComment(parser, encoding, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_INVALID:
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (!ps_finalBuffer) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (!ps_finalBuffer) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ default:
+ return XML_ERROR_JUNK_AFTER_DOC_ELEMENT;
+ }
+ eventPtr = s = next;
+ switch (ps_parsing) {
+ case XML_SUSPENDED:
+ *nextPtr = next;
+ return XML_ERROR_NONE;
+ case XML_FINISHED:
+ return XML_ERROR_ABORTED;
+ default: ;
+ }
+ }
+}
+
+static enum XML_Error
+processInternalEntity(XML_Parser parser, ENTITY *entity,
+ XML_Bool betweenDecl)
+{
+ const char *textStart, *textEnd;
+ const char *next;
+ enum XML_Error result;
+ OPEN_INTERNAL_ENTITY *openEntity;
+
+ if (freeInternalEntities) {
+ openEntity = freeInternalEntities;
+ freeInternalEntities = openEntity->next;
+ }
+ else {
+ openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY));
+ if (!openEntity)
+ return XML_ERROR_NO_MEMORY;
+ }
+ entity->open = XML_TRUE;
+ entity->processed = 0;
+ openEntity->next = openInternalEntities;
+ openInternalEntities = openEntity;
+ openEntity->entity = entity;
+ openEntity->startTagLevel = tagLevel;
+ openEntity->betweenDecl = betweenDecl;
+ openEntity->internalEventPtr = NULL;
+ openEntity->internalEventEndPtr = NULL;
+ textStart = (char *)entity->textPtr;
+ textEnd = (char *)(entity->textPtr + entity->textLen);
+
+#ifdef XML_DTD
+ if (entity->is_param) {
+ int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next);
+ result = doProlog(parser, internalEncoding, textStart, textEnd, tok,
+ next, &next, XML_FALSE);
+ }
+ else
+#endif /* XML_DTD */
+ result = doContent(parser, tagLevel, internalEncoding, textStart,
+ textEnd, &next, XML_FALSE);
+
+ if (result == XML_ERROR_NONE) {
+ if (textEnd != next && ps_parsing == XML_SUSPENDED) {
+ entity->processed = (int)(next - textStart);
+ processor = internalEntityProcessor;
+ }
+ else {
+ entity->open = XML_FALSE;
+ openInternalEntities = openEntity->next;
+ /* put openEntity back in list of free instances */
+ openEntity->next = freeInternalEntities;
+ freeInternalEntities = openEntity;
+ }
+ }
+ return result;
+}
+
+static enum XML_Error PTRCALL
+internalEntityProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ ENTITY *entity;
+ const char *textStart, *textEnd;
+ const char *next;
+ enum XML_Error result;
+ OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities;
+ if (!openEntity)
+ return XML_ERROR_UNEXPECTED_STATE;
+
+ entity = openEntity->entity;
+ textStart = ((char *)entity->textPtr) + entity->processed;
+ textEnd = (char *)(entity->textPtr + entity->textLen);
+
+#ifdef XML_DTD
+ if (entity->is_param) {
+ int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next);
+ result = doProlog(parser, internalEncoding, textStart, textEnd, tok,
+ next, &next, XML_FALSE);
+ }
+ else
+#endif /* XML_DTD */
+ result = doContent(parser, openEntity->startTagLevel, internalEncoding,
+ textStart, textEnd, &next, XML_FALSE);
+
+ if (result != XML_ERROR_NONE)
+ return result;
+ else if (textEnd != next && ps_parsing == XML_SUSPENDED) {
+ entity->processed = (int)(next - (char *)entity->textPtr);
+ return result;
+ }
+ else {
+ entity->open = XML_FALSE;
+ openInternalEntities = openEntity->next;
+ /* put openEntity back in list of free instances */
+ openEntity->next = freeInternalEntities;
+ freeInternalEntities = openEntity;
+ }
+
+#ifdef XML_DTD
+ if (entity->is_param) {
+ int tok;
+ processor = prologProcessor;
+ tok = XmlPrologTok(encoding, s, end, &next);
+ return doProlog(parser, encoding, s, end, tok, next, nextPtr,
+ (XML_Bool)!ps_finalBuffer);
+ }
+ else
+#endif /* XML_DTD */
+ {
+ processor = contentProcessor;
+ /* see externalEntityContentProcessor vs contentProcessor */
+ return doContent(parser, parentParser ? 1 : 0, encoding, s, end,
+ nextPtr, (XML_Bool)!ps_finalBuffer);
+ }
+}
+
+static enum XML_Error PTRCALL
+errorProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ return errorCode;
+}
+
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+ const char *ptr, const char *end,
+ STRING_POOL *pool)
+{
+ enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr,
+ end, pool);
+ if (result)
+ return result;
+ if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
+ poolChop(pool);
+ if (!poolAppendChar(pool, XML_T('\0')))
+ return XML_ERROR_NO_MEMORY;
+ return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+ const char *ptr, const char *end,
+ STRING_POOL *pool)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ for (;;) {
+ const char *next;
+ int tok = XmlAttributeValueTok(enc, ptr, end, &next);
+ switch (tok) {
+ case XML_TOK_NONE:
+ return XML_ERROR_NONE;
+ case XML_TOK_INVALID:
+ if (enc == encoding)
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_CHAR_REF:
+ {
+ XML_Char buf[XML_ENCODE_MAX];
+ int i;
+ int n = XmlCharRefNumber(enc, ptr);
+ if (n < 0) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ if (!isCdata
+ && n == 0x20 /* space */
+ && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+ break;
+ n = XmlEncode(n, (ICHAR *)buf);
+ if (!n) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ for (i = 0; i < n; i++) {
+ if (!poolAppendChar(pool, buf[i]))
+ return XML_ERROR_NO_MEMORY;
+ }
+ }
+ break;
+ case XML_TOK_DATA_CHARS:
+ if (!poolAppend(pool, enc, ptr, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_TRAILING_CR:
+ next = ptr + enc->minBytesPerChar;
+ /* fall through */
+ case XML_TOK_ATTRIBUTE_VALUE_S:
+ case XML_TOK_DATA_NEWLINE:
+ if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+ break;
+ if (!poolAppendChar(pool, 0x20))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_ENTITY_REF:
+ {
+ const XML_Char *name;
+ ENTITY *entity;
+ char checkEntityDecl;
+ XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc,
+ ptr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (ch) {
+ if (!poolAppendChar(pool, ch))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ }
+ name = poolStoreString(&temp2Pool, enc,
+ ptr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0);
+ poolDiscard(&temp2Pool);
+ /* First, determine if a check for an existing declaration is needed;
+ if yes, check that the entity exists, and that it is internal.
+ */
+ if (pool == &dtd->pool) /* are we called from prolog? */
+ checkEntityDecl =
+#ifdef XML_DTD
+ prologState.documentEntity &&
+#endif /* XML_DTD */
+ (dtd->standalone
+ ? !openInternalEntities
+ : !dtd->hasParamEntityRefs);
+ else /* if (pool == &tempPool): we are called from content */
+ checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone;
+ if (checkEntityDecl) {
+ if (!entity)
+ return XML_ERROR_UNDEFINED_ENTITY;
+ else if (!entity->is_internal)
+ return XML_ERROR_ENTITY_DECLARED_IN_PE;
+ }
+ else if (!entity) {
+ /* Cannot report skipped entity here - see comments on
+ skippedEntityHandler.
+ if (skippedEntityHandler)
+ skippedEntityHandler(handlerArg, name, 0);
+ */
+ /* Cannot call the default handler because this would be
+ out of sync with the call to the startElementHandler.
+ if ((pool == &tempPool) && defaultHandler)
+ reportDefault(parser, enc, ptr, next);
+ */
+ break;
+ }
+ if (entity->open) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ }
+ if (entity->notation) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BINARY_ENTITY_REF;
+ }
+ if (!entity->textPtr) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
+ }
+ else {
+ enum XML_Error result;
+ const XML_Char *textEnd = entity->textPtr + entity->textLen;
+ entity->open = XML_TRUE;
+ result = appendAttributeValue(parser, internalEncoding, isCdata,
+ (char *)entity->textPtr,
+ (char *)textEnd, pool);
+ entity->open = XML_FALSE;
+ if (result)
+ return result;
+ }
+ }
+ break;
+ default:
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_UNEXPECTED_STATE;
+ }
+ ptr = next;
+ }
+ /* not reached */
+}
+
+static enum XML_Error
+storeEntityValue(XML_Parser parser,
+ const ENCODING *enc,
+ const char *entityTextPtr,
+ const char *entityTextEnd)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ STRING_POOL *pool = &(dtd->entityValuePool);
+ enum XML_Error result = XML_ERROR_NONE;
+#ifdef XML_DTD
+ int oldInEntityValue = prologState.inEntityValue;
+ prologState.inEntityValue = 1;
+#endif /* XML_DTD */
+ /* never return Null for the value argument in EntityDeclHandler,
+ since this would indicate an external entity; therefore we
+ have to make sure that entityValuePool.start is not null */
+ if (!pool->blocks) {
+ if (!poolGrow(pool))
+ return XML_ERROR_NO_MEMORY;
+ }
+
+ for (;;) {
+ const char *next;
+ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+#ifdef XML_DTD
+ if (isParamEntity || enc != encoding) {
+ const XML_Char *name;
+ ENTITY *entity;
+ name = poolStoreString(&tempPool, enc,
+ entityTextPtr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name) {
+ result = XML_ERROR_NO_MEMORY;
+ goto endEntityValue;
+ }
+ entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0);
+ poolDiscard(&tempPool);
+ if (!entity) {
+ /* not a well-formedness error - see XML 1.0: WFC Entity Declared */
+ /* cannot report skipped entity here - see comments on
+ skippedEntityHandler
+ if (skippedEntityHandler)
+ skippedEntityHandler(handlerArg, name, 0);
+ */
+ dtd->keepProcessing = dtd->standalone;
+ goto endEntityValue;
+ }
+ if (entity->open) {
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ result = XML_ERROR_RECURSIVE_ENTITY_REF;
+ goto endEntityValue;
+ }
+ if (entity->systemId) {
+ if (externalEntityRefHandler) {
+ dtd->paramEntityRead = XML_FALSE;
+ entity->open = XML_TRUE;
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ 0,
+ entity->base,
+ entity->systemId,
+ entity->publicId)) {
+ entity->open = XML_FALSE;
+ result = XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ goto endEntityValue;
+ }
+ entity->open = XML_FALSE;
+ if (!dtd->paramEntityRead)
+ dtd->keepProcessing = dtd->standalone;
+ }
+ else
+ dtd->keepProcessing = dtd->standalone;
+ }
+ else {
+ entity->open = XML_TRUE;
+ result = storeEntityValue(parser,
+ internalEncoding,
+ (char *)entity->textPtr,
+ (char *)(entity->textPtr
+ + entity->textLen));
+ entity->open = XML_FALSE;
+ if (result)
+ goto endEntityValue;
+ }
+ break;
+ }
+#endif /* XML_DTD */
+ /* In the internal subset, PE references are not legal
+ within markup declarations, e.g entity values in this case. */
+ eventPtr = entityTextPtr;
+ result = XML_ERROR_PARAM_ENTITY_REF;
+ goto endEntityValue;
+ case XML_TOK_NONE:
+ result = XML_ERROR_NONE;
+ goto endEntityValue;
+ case XML_TOK_ENTITY_REF:
+ case XML_TOK_DATA_CHARS:
+ if (!poolAppend(pool, enc, entityTextPtr, next)) {
+ result = XML_ERROR_NO_MEMORY;
+ goto endEntityValue;
+ }
+ break;
+ case XML_TOK_TRAILING_CR:
+ next = entityTextPtr + enc->minBytesPerChar;
+ /* fall through */
+ case XML_TOK_DATA_NEWLINE:
+ if (pool->end == pool->ptr && !poolGrow(pool)) {
+ result = XML_ERROR_NO_MEMORY;
+ goto endEntityValue;
+ }
+ *(pool->ptr)++ = 0xA;
+ break;
+ case XML_TOK_CHAR_REF:
+ {
+ XML_Char buf[XML_ENCODE_MAX];
+ int i;
+ int n = XmlCharRefNumber(enc, entityTextPtr);
+ if (n < 0) {
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ result = XML_ERROR_BAD_CHAR_REF;
+ goto endEntityValue;
+ }
+ n = XmlEncode(n, (ICHAR *)buf);
+ if (!n) {
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ result = XML_ERROR_BAD_CHAR_REF;
+ goto endEntityValue;
+ }
+ for (i = 0; i < n; i++) {
+ if (pool->end == pool->ptr && !poolGrow(pool)) {
+ result = XML_ERROR_NO_MEMORY;
+ goto endEntityValue;
+ }
+ *(pool->ptr)++ = buf[i];
+ }
+ }
+ break;
+ case XML_TOK_PARTIAL:
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ result = XML_ERROR_INVALID_TOKEN;
+ goto endEntityValue;
+ case XML_TOK_INVALID:
+ if (enc == encoding)
+ eventPtr = next;
+ result = XML_ERROR_INVALID_TOKEN;
+ goto endEntityValue;
+ default:
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ result = XML_ERROR_UNEXPECTED_STATE;
+ goto endEntityValue;
+ }
+ entityTextPtr = next;
+ }
+endEntityValue:
+#ifdef XML_DTD
+ prologState.inEntityValue = oldInEntityValue;
+#endif /* XML_DTD */
+ return result;
+}
+
+static void FASTCALL
+normalizeLines(XML_Char *s)
+{
+ XML_Char *p;
+ for (;; s++) {
+ if (*s == XML_T('\0'))
+ return;
+ if (*s == 0xD)
+ break;
+ }
+ p = s;
+ do {
+ if (*s == 0xD) {
+ *p++ = 0xA;
+ if (*++s == 0xA)
+ s++;
+ }
+ else
+ *p++ = *s++;
+ } while (*s);
+ *p = XML_T('\0');
+}
+
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end)
+{
+ const XML_Char *target;
+ XML_Char *data;
+ const char *tem;
+ if (!processingInstructionHandler) {
+ if (defaultHandler)
+ reportDefault(parser, enc, start, end);
+ return 1;
+ }
+ start += enc->minBytesPerChar * 2;
+ tem = start + XmlNameLength(enc, start);
+ target = poolStoreString(&tempPool, enc, start, tem);
+ if (!target)
+ return 0;
+ poolFinish(&tempPool);
+ data = poolStoreString(&tempPool, enc,
+ XmlSkipS(enc, tem),
+ end - enc->minBytesPerChar*2);
+ if (!data)
+ return 0;
+ normalizeLines(data);
+ processingInstructionHandler(handlerArg, target, data);
+ poolClear(&tempPool);
+ return 1;
+}
+
+static int
+reportComment(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end)
+{
+ XML_Char *data;
+ if (!commentHandler) {
+ if (defaultHandler)
+ reportDefault(parser, enc, start, end);
+ return 1;
+ }
+ data = poolStoreString(&tempPool,
+ enc,
+ start + enc->minBytesPerChar * 4,
+ end - enc->minBytesPerChar * 3);
+ if (!data)
+ return 0;
+ normalizeLines(data);
+ commentHandler(handlerArg, data);
+ poolClear(&tempPool);
+ return 1;
+}
+
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc,
+ const char *s, const char *end)
+{
+ if (MUST_CONVERT(enc, s)) {
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ do {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = s;
+ defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf));
+ *eventPP = s;
+ } while (s != end);
+ }
+ else
+ defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s));
+}
+
+
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
+ XML_Bool isId, const XML_Char *value, XML_Parser parser)
+{
+ DEFAULT_ATTRIBUTE *att;
+ if (value || isId) {
+ /* The handling of default attributes gets messed up if we have
+ a default which duplicates a non-default. */
+ int i;
+ for (i = 0; i < type->nDefaultAtts; i++)
+ if (attId == type->defaultAtts[i].id)
+ return 1;
+ if (isId && !type->idAtt && !attId->xmlns)
+ type->idAtt = attId;
+ }
+ if (type->nDefaultAtts == type->allocDefaultAtts) {
+ if (type->allocDefaultAtts == 0) {
+ type->allocDefaultAtts = 8;
+ type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts
+ * sizeof(DEFAULT_ATTRIBUTE));
+ if (!type->defaultAtts)
+ return 0;
+ }
+ else {
+ DEFAULT_ATTRIBUTE *temp;
+ int count = type->allocDefaultAtts * 2;
+ temp = (DEFAULT_ATTRIBUTE *)
+ REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE)));
+ if (temp == NULL)
+ return 0;
+ type->allocDefaultAtts = count;
+ type->defaultAtts = temp;
+ }
+ }
+ att = type->defaultAtts + type->nDefaultAtts;
+ att->id = attId;
+ att->value = value;
+ att->isCdata = isCdata;
+ if (!isCdata)
+ attId->maybeTokenized = XML_TRUE;
+ type->nDefaultAtts += 1;
+ return 1;
+}
+
+static int
+setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ const XML_Char *name;
+ for (name = elementType->name; *name; name++) {
+ if (*name == XML_T(ASCII_COLON)) {
+ PREFIX *prefix;
+ const XML_Char *s;
+ for (s = elementType->name; s != name; s++) {
+ if (!poolAppendChar(&dtd->pool, *s))
+ return 0;
+ }
+ if (!poolAppendChar(&dtd->pool, XML_T('\0')))
+ return 0;
+ prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool),
+ sizeof(PREFIX));
+ if (!prefix)
+ return 0;
+ if (prefix->name == poolStart(&dtd->pool))
+ poolFinish(&dtd->pool);
+ else
+ poolDiscard(&dtd->pool);
+ elementType->prefix = prefix;
+
+ }
+ }
+ return 1;
+}
+
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ ATTRIBUTE_ID *id;
+ const XML_Char *name;
+ if (!poolAppendChar(&dtd->pool, XML_T('\0')))
+ return NULL;
+ name = poolStoreString(&dtd->pool, enc, start, end);
+ if (!name)
+ return NULL;
+ /* skip quotation mark - its storage will be re-used (like in name[-1]) */
+ ++name;
+ id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, name, sizeof(ATTRIBUTE_ID));
+ if (!id)
+ return NULL;
+ if (id->name != name)
+ poolDiscard(&dtd->pool);
+ else {
+ poolFinish(&dtd->pool);
+ if (!ns)
+ ;
+ else if (name[0] == XML_T(ASCII_x)
+ && name[1] == XML_T(ASCII_m)
+ && name[2] == XML_T(ASCII_l)
+ && name[3] == XML_T(ASCII_n)
+ && name[4] == XML_T(ASCII_s)
+ && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) {
+ if (name[5] == XML_T('\0'))
+ id->prefix = &dtd->defaultPrefix;
+ else
+ id->prefix = (PREFIX *)lookup(&dtd->prefixes, name + 6, sizeof(PREFIX));
+ id->xmlns = XML_TRUE;
+ }
+ else {
+ int i;
+ for (i = 0; name[i]; i++) {
+ /* attributes without prefix are *not* in the default namespace */
+ if (name[i] == XML_T(ASCII_COLON)) {
+ int j;
+ for (j = 0; j < i; j++) {
+ if (!poolAppendChar(&dtd->pool, name[j]))
+ return NULL;
+ }
+ if (!poolAppendChar(&dtd->pool, XML_T('\0')))
+ return NULL;
+ id->prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool),
+ sizeof(PREFIX));
+ if (id->prefix->name == poolStart(&dtd->pool))
+ poolFinish(&dtd->pool);
+ else
+ poolDiscard(&dtd->pool);
+ break;
+ }
+ }
+ }
+ }
+ return id;
+}
+
+#define CONTEXT_SEP XML_T(ASCII_FF)
+
+static const XML_Char *
+getContext(XML_Parser parser)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ HASH_TABLE_ITER iter;
+ XML_Bool needSep = XML_FALSE;
+
+ if (dtd->defaultPrefix.binding) {
+ int i;
+ int len;
+ if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS)))
+ return NULL;
+ len = dtd->defaultPrefix.binding->uriLen;
+ if (namespaceSeparator)
+ len--;
+ for (i = 0; i < len; i++)
+ if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i]))
+ return NULL;
+ needSep = XML_TRUE;
+ }
+
+ hashTableIterInit(&iter, &(dtd->prefixes));
+ for (;;) {
+ int i;
+ int len;
+ const XML_Char *s;
+ PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter);
+ if (!prefix)
+ break;
+ if (!prefix->binding)
+ continue;
+ if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+ return NULL;
+ for (s = prefix->name; *s; s++)
+ if (!poolAppendChar(&tempPool, *s))
+ return NULL;
+ if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS)))
+ return NULL;
+ len = prefix->binding->uriLen;
+ if (namespaceSeparator)
+ len--;
+ for (i = 0; i < len; i++)
+ if (!poolAppendChar(&tempPool, prefix->binding->uri[i]))
+ return NULL;
+ needSep = XML_TRUE;
+ }
+
+
+ hashTableIterInit(&iter, &(dtd->generalEntities));
+ for (;;) {
+ const XML_Char *s;
+ ENTITY *e = (ENTITY *)hashTableIterNext(&iter);
+ if (!e)
+ break;
+ if (!e->open)
+ continue;
+ if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+ return NULL;
+ for (s = e->name; *s; s++)
+ if (!poolAppendChar(&tempPool, *s))
+ return 0;
+ needSep = XML_TRUE;
+ }
+
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return NULL;
+ return tempPool.start;
+}
+
+static XML_Bool
+setContext(XML_Parser parser, const XML_Char *context)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ const XML_Char *s = context;
+
+ while (*context != XML_T('\0')) {
+ if (*s == CONTEXT_SEP || *s == XML_T('\0')) {
+ ENTITY *e;
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return XML_FALSE;
+ e = (ENTITY *)lookup(&dtd->generalEntities, poolStart(&tempPool), 0);
+ if (e)
+ e->open = XML_TRUE;
+ if (*s != XML_T('\0'))
+ s++;
+ context = s;
+ poolDiscard(&tempPool);
+ }
+ else if (*s == XML_T(ASCII_EQUALS)) {
+ PREFIX *prefix;
+ if (poolLength(&tempPool) == 0)
+ prefix = &dtd->defaultPrefix;
+ else {
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return XML_FALSE;
+ prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&tempPool),
+ sizeof(PREFIX));
+ if (!prefix)
+ return XML_FALSE;
+ if (prefix->name == poolStart(&tempPool)) {
+ prefix->name = poolCopyString(&dtd->pool, prefix->name);
+ if (!prefix->name)
+ return XML_FALSE;
+ }
+ poolDiscard(&tempPool);
+ }
+ for (context = s + 1;
+ *context != CONTEXT_SEP && *context != XML_T('\0');
+ context++)
+ if (!poolAppendChar(&tempPool, *context))
+ return XML_FALSE;
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return XML_FALSE;
+ if (addBinding(parser, prefix, NULL, poolStart(&tempPool),
+ &inheritedBindings) != XML_ERROR_NONE)
+ return XML_FALSE;
+ poolDiscard(&tempPool);
+ if (*context != XML_T('\0'))
+ ++context;
+ s = context;
+ }
+ else {
+ if (!poolAppendChar(&tempPool, *s))
+ return XML_FALSE;
+ s++;
+ }
+ }
+ return XML_TRUE;
+}
+
+static void FASTCALL
+normalizePublicId(XML_Char *publicId)
+{
+ XML_Char *p = publicId;
+ XML_Char *s;
+ for (s = publicId; *s; s++) {
+ switch (*s) {
+ case 0x20:
+ case 0xD:
+ case 0xA:
+ if (p != publicId && p[-1] != 0x20)
+ *p++ = 0x20;
+ break;
+ default:
+ *p++ = *s;
+ }
+ }
+ if (p != publicId && p[-1] == 0x20)
+ --p;
+ *p = XML_T('\0');
+}
+
+static DTD *
+dtdCreate(const XML_Memory_Handling_Suite *ms)
+{
+ DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD));
+ if (p == NULL)
+ return p;
+ poolInit(&(p->pool), ms);
+ poolInit(&(p->entityValuePool), ms);
+ hashTableInit(&(p->generalEntities), ms);
+ hashTableInit(&(p->elementTypes), ms);
+ hashTableInit(&(p->attributeIds), ms);
+ hashTableInit(&(p->prefixes), ms);
+#ifdef XML_DTD
+ p->paramEntityRead = XML_FALSE;
+ hashTableInit(&(p->paramEntities), ms);
+#endif /* XML_DTD */
+ p->defaultPrefix.name = NULL;
+ p->defaultPrefix.binding = NULL;
+
+ p->in_eldecl = XML_FALSE;
+ p->scaffIndex = NULL;
+ p->scaffold = NULL;
+ p->scaffLevel = 0;
+ p->scaffSize = 0;
+ p->scaffCount = 0;
+ p->contentStringLen = 0;
+
+ p->keepProcessing = XML_TRUE;
+ p->hasParamEntityRefs = XML_FALSE;
+ p->standalone = XML_FALSE;
+ return p;
+}
+
+static void
+dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms)
+{
+ HASH_TABLE_ITER iter;
+ hashTableIterInit(&iter, &(p->elementTypes));
+ for (;;) {
+ ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+ if (!e)
+ break;
+ if (e->allocDefaultAtts != 0)
+ ms->free_fcn(e->defaultAtts);
+ }
+ hashTableClear(&(p->generalEntities));
+#ifdef XML_DTD
+ p->paramEntityRead = XML_FALSE;
+ hashTableClear(&(p->paramEntities));
+#endif /* XML_DTD */
+ hashTableClear(&(p->elementTypes));
+ hashTableClear(&(p->attributeIds));
+ hashTableClear(&(p->prefixes));
+ poolClear(&(p->pool));
+ poolClear(&(p->entityValuePool));
+ p->defaultPrefix.name = NULL;
+ p->defaultPrefix.binding = NULL;
+
+ p->in_eldecl = XML_FALSE;
+
+ ms->free_fcn(p->scaffIndex);
+ p->scaffIndex = NULL;
+ ms->free_fcn(p->scaffold);
+ p->scaffold = NULL;
+
+ p->scaffLevel = 0;
+ p->scaffSize = 0;
+ p->scaffCount = 0;
+ p->contentStringLen = 0;
+
+ p->keepProcessing = XML_TRUE;
+ p->hasParamEntityRefs = XML_FALSE;
+ p->standalone = XML_FALSE;
+}
+
+static void
+dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms)
+{
+ HASH_TABLE_ITER iter;
+ hashTableIterInit(&iter, &(p->elementTypes));
+ for (;;) {
+ ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+ if (!e)
+ break;
+ if (e->allocDefaultAtts != 0)
+ ms->free_fcn(e->defaultAtts);
+ }
+ hashTableDestroy(&(p->generalEntities));
+#ifdef XML_DTD
+ hashTableDestroy(&(p->paramEntities));
+#endif /* XML_DTD */
+ hashTableDestroy(&(p->elementTypes));
+ hashTableDestroy(&(p->attributeIds));
+ hashTableDestroy(&(p->prefixes));
+ poolDestroy(&(p->pool));
+ poolDestroy(&(p->entityValuePool));
+ if (isDocEntity) {
+ ms->free_fcn(p->scaffIndex);
+ ms->free_fcn(p->scaffold);
+ }
+ ms->free_fcn(p);
+}
+
+/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise.
+ The new DTD has already been initialized.
+*/
+static int
+dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms)
+{
+ HASH_TABLE_ITER iter;
+
+ /* Copy the prefix table. */
+
+ hashTableIterInit(&iter, &(oldDtd->prefixes));
+ for (;;) {
+ const XML_Char *name;
+ const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter);
+ if (!oldP)
+ break;
+ name = poolCopyString(&(newDtd->pool), oldP->name);
+ if (!name)
+ return 0;
+ if (!lookup(&(newDtd->prefixes), name, sizeof(PREFIX)))
+ return 0;
+ }
+
+ hashTableIterInit(&iter, &(oldDtd->attributeIds));
+
+ /* Copy the attribute id table. */
+
+ for (;;) {
+ ATTRIBUTE_ID *newA;
+ const XML_Char *name;
+ const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter);
+
+ if (!oldA)
+ break;
+ /* Remember to allocate the scratch byte before the name. */
+ if (!poolAppendChar(&(newDtd->pool), XML_T('\0')))
+ return 0;
+ name = poolCopyString(&(newDtd->pool), oldA->name);
+ if (!name)
+ return 0;
+ ++name;
+ newA = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), name,
+ sizeof(ATTRIBUTE_ID));
+ if (!newA)
+ return 0;
+ newA->maybeTokenized = oldA->maybeTokenized;
+ if (oldA->prefix) {
+ newA->xmlns = oldA->xmlns;
+ if (oldA->prefix == &oldDtd->defaultPrefix)
+ newA->prefix = &newDtd->defaultPrefix;
+ else
+ newA->prefix = (PREFIX *)lookup(&(newDtd->prefixes),
+ oldA->prefix->name, 0);
+ }
+ }
+
+ /* Copy the element type table. */
+
+ hashTableIterInit(&iter, &(oldDtd->elementTypes));
+
+ for (;;) {
+ int i;
+ ELEMENT_TYPE *newE;
+ const XML_Char *name;
+ const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+ if (!oldE)
+ break;
+ name = poolCopyString(&(newDtd->pool), oldE->name);
+ if (!name)
+ return 0;
+ newE = (ELEMENT_TYPE *)lookup(&(newDtd->elementTypes), name,
+ sizeof(ELEMENT_TYPE));
+ if (!newE)
+ return 0;
+ if (oldE->nDefaultAtts) {
+ newE->defaultAtts = (DEFAULT_ATTRIBUTE *)
+ ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+ if (!newE->defaultAtts) {
+ ms->free_fcn(newE);
+ return 0;
+ }
+ }
+ if (oldE->idAtt)
+ newE->idAtt = (ATTRIBUTE_ID *)
+ lookup(&(newDtd->attributeIds), oldE->idAtt->name, 0);
+ newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts;
+ if (oldE->prefix)
+ newE->prefix = (PREFIX *)lookup(&(newDtd->prefixes),
+ oldE->prefix->name, 0);
+ for (i = 0; i < newE->nDefaultAtts; i++) {
+ newE->defaultAtts[i].id = (ATTRIBUTE_ID *)
+ lookup(&(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0);
+ newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata;
+ if (oldE->defaultAtts[i].value) {
+ newE->defaultAtts[i].value
+ = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value);
+ if (!newE->defaultAtts[i].value)
+ return 0;
+ }
+ else
+ newE->defaultAtts[i].value = NULL;
+ }
+ }
+
+ /* Copy the entity tables. */
+ if (!copyEntityTable(&(newDtd->generalEntities),
+ &(newDtd->pool),
+ &(oldDtd->generalEntities)))
+ return 0;
+
+#ifdef XML_DTD
+ if (!copyEntityTable(&(newDtd->paramEntities),
+ &(newDtd->pool),
+ &(oldDtd->paramEntities)))
+ return 0;
+ newDtd->paramEntityRead = oldDtd->paramEntityRead;
+#endif /* XML_DTD */
+
+ newDtd->keepProcessing = oldDtd->keepProcessing;
+ newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs;
+ newDtd->standalone = oldDtd->standalone;
+
+ /* Don't want deep copying for scaffolding */
+ newDtd->in_eldecl = oldDtd->in_eldecl;
+ newDtd->scaffold = oldDtd->scaffold;
+ newDtd->contentStringLen = oldDtd->contentStringLen;
+ newDtd->scaffSize = oldDtd->scaffSize;
+ newDtd->scaffLevel = oldDtd->scaffLevel;
+ newDtd->scaffIndex = oldDtd->scaffIndex;
+
+ return 1;
+} /* End dtdCopy */
+
+static int
+copyEntityTable(HASH_TABLE *newTable,
+ STRING_POOL *newPool,
+ const HASH_TABLE *oldTable)
+{
+ HASH_TABLE_ITER iter;
+ const XML_Char *cachedOldBase = NULL;
+ const XML_Char *cachedNewBase = NULL;
+
+ hashTableIterInit(&iter, oldTable);
+
+ for (;;) {
+ ENTITY *newE;
+ const XML_Char *name;
+ const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter);
+ if (!oldE)
+ break;
+ name = poolCopyString(newPool, oldE->name);
+ if (!name)
+ return 0;
+ newE = (ENTITY *)lookup(newTable, name, sizeof(ENTITY));
+ if (!newE)
+ return 0;
+ if (oldE->systemId) {
+ const XML_Char *tem = poolCopyString(newPool, oldE->systemId);
+ if (!tem)
+ return 0;
+ newE->systemId = tem;
+ if (oldE->base) {
+ if (oldE->base == cachedOldBase)
+ newE->base = cachedNewBase;
+ else {
+ cachedOldBase = oldE->base;
+ tem = poolCopyString(newPool, cachedOldBase);
+ if (!tem)
+ return 0;
+ cachedNewBase = newE->base = tem;
+ }
+ }
+ if (oldE->publicId) {
+ tem = poolCopyString(newPool, oldE->publicId);
+ if (!tem)
+ return 0;
+ newE->publicId = tem;
+ }
+ }
+ else {
+ const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr,
+ oldE->textLen);
+ if (!tem)
+ return 0;
+ newE->textPtr = tem;
+ newE->textLen = oldE->textLen;
+ }
+ if (oldE->notation) {
+ const XML_Char *tem = poolCopyString(newPool, oldE->notation);
+ if (!tem)
+ return 0;
+ newE->notation = tem;
+ }
+ newE->is_param = oldE->is_param;
+ newE->is_internal = oldE->is_internal;
+ }
+ return 1;
+}
+
+#define INIT_POWER 6
+
+static XML_Bool FASTCALL
+keyeq(KEY s1, KEY s2)
+{
+ for (; *s1 == *s2; s1++, s2++)
+ if (*s1 == 0)
+ return XML_TRUE;
+ return XML_FALSE;
+}
+
+static unsigned long FASTCALL
+hash(KEY s)
+{
+ unsigned long h = 0;
+ while (*s)
+ h = CHAR_HASH(h, *s++);
+ return h;
+}
+
+static NAMED *
+lookup(HASH_TABLE *table, KEY name, size_t createSize)
+{
+ size_t i;
+ if (table->size == 0) {
+ size_t tsize;
+ if (!createSize)
+ return NULL;
+ table->power = INIT_POWER;
+ /* table->size is a power of 2 */
+ table->size = (size_t)1 << INIT_POWER;
+ tsize = table->size * sizeof(NAMED *);
+ table->v = (NAMED **)table->mem->malloc_fcn(tsize);
+ if (!table->v) {
+ table->size = 0;
+ return NULL;
+ }
+ memset(table->v, 0, tsize);
+ i = hash(name) & ((unsigned long)table->size - 1);
+ }
+ else {
+ unsigned long h = hash(name);
+ unsigned long mask = (unsigned long)table->size - 1;
+ unsigned char step = 0;
+ i = h & mask;
+ while (table->v[i]) {
+ if (keyeq(name, table->v[i]->name))
+ return table->v[i];
+ if (!step)
+ step = PROBE_STEP(h, mask, table->power);
+ i < step ? (i += table->size - step) : (i -= step);
+ }
+ if (!createSize)
+ return NULL;
+
+ /* check for overflow (table is half full) */
+ if (table->used >> (table->power - 1)) {
+ unsigned char newPower = table->power + 1;
+ size_t newSize = (size_t)1 << newPower;
+ unsigned long newMask = (unsigned long)newSize - 1;
+ size_t tsize = newSize * sizeof(NAMED *);
+ NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
+ if (!newV)
+ return NULL;
+ memset(newV, 0, tsize);
+ for (i = 0; i < table->size; i++)
+ if (table->v[i]) {
+ unsigned long newHash = hash(table->v[i]->name);
+ size_t j = newHash & newMask;
+ step = 0;
+ while (newV[j]) {
+ if (!step)
+ step = PROBE_STEP(newHash, newMask, newPower);
+ j < step ? (j += newSize - step) : (j -= step);
+ }
+ newV[j] = table->v[i];
+ }
+ table->mem->free_fcn(table->v);
+ table->v = newV;
+ table->power = newPower;
+ table->size = newSize;
+ i = h & newMask;
+ step = 0;
+ while (table->v[i]) {
+ if (!step)
+ step = PROBE_STEP(h, newMask, newPower);
+ i < step ? (i += newSize - step) : (i -= step);
+ }
+ }
+ }
+ table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize);
+ if (!table->v[i])
+ return NULL;
+ memset(table->v[i], 0, createSize);
+ table->v[i]->name = name;
+ (table->used)++;
+ return table->v[i];
+}
+
+static void FASTCALL
+hashTableClear(HASH_TABLE *table)
+{
+ size_t i;
+ for (i = 0; i < table->size; i++) {
+ table->mem->free_fcn(table->v[i]);
+ table->v[i] = NULL;
+ }
+ table->used = 0;
+}
+
+static void FASTCALL
+hashTableDestroy(HASH_TABLE *table)
+{
+ size_t i;
+ for (i = 0; i < table->size; i++)
+ table->mem->free_fcn(table->v[i]);
+ table->mem->free_fcn(table->v);
+}
+
+static void FASTCALL
+hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms)
+{
+ p->power = 0;
+ p->size = 0;
+ p->used = 0;
+ p->v = NULL;
+ p->mem = ms;
+}
+
+static void FASTCALL
+hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table)
+{
+ iter->p = table->v;
+ iter->end = iter->p + table->size;
+}
+
+static NAMED * FASTCALL
+hashTableIterNext(HASH_TABLE_ITER *iter)
+{
+ while (iter->p != iter->end) {
+ NAMED *tem = *(iter->p)++;
+ if (tem)
+ return tem;
+ }
+ return NULL;
+}
+
+static void FASTCALL
+poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms)
+{
+ pool->blocks = NULL;
+ pool->freeBlocks = NULL;
+ pool->start = NULL;
+ pool->ptr = NULL;
+ pool->end = NULL;
+ pool->mem = ms;
+}
+
+static void FASTCALL
+poolClear(STRING_POOL *pool)
+{
+ if (!pool->freeBlocks)
+ pool->freeBlocks = pool->blocks;
+ else {
+ BLOCK *p = pool->blocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ p->next = pool->freeBlocks;
+ pool->freeBlocks = p;
+ p = tem;
+ }
+ }
+ pool->blocks = NULL;
+ pool->start = NULL;
+ pool->ptr = NULL;
+ pool->end = NULL;
+}
+
+static void FASTCALL
+poolDestroy(STRING_POOL *pool)
+{
+ BLOCK *p = pool->blocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ pool->mem->free_fcn(p);
+ p = tem;
+ }
+ p = pool->freeBlocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ pool->mem->free_fcn(p);
+ p = tem;
+ }
+}
+
+static XML_Char *
+poolAppend(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end)
+{
+ if (!pool->ptr && !poolGrow(pool))
+ return NULL;
+ for (;;) {
+ XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end);
+ if (ptr == end)
+ break;
+ if (!poolGrow(pool))
+ return NULL;
+ }
+ return pool->start;
+}
+
+static const XML_Char * FASTCALL
+poolCopyString(STRING_POOL *pool, const XML_Char *s)
+{
+ do {
+ if (!poolAppendChar(pool, *s))
+ return NULL;
+ } while (*s++);
+ s = pool->start;
+ poolFinish(pool);
+ return s;
+}
+
+static const XML_Char *
+poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n)
+{
+ if (!pool->ptr && !poolGrow(pool))
+ return NULL;
+ for (; n > 0; --n, s++) {
+ if (!poolAppendChar(pool, *s))
+ return NULL;
+ }
+ s = pool->start;
+ poolFinish(pool);
+ return s;
+}
+
+static const XML_Char * FASTCALL
+poolAppendString(STRING_POOL *pool, const XML_Char *s)
+{
+ while (*s) {
+ if (!poolAppendChar(pool, *s))
+ return NULL;
+ s++;
+ }
+ return pool->start;
+}
+
+static XML_Char *
+poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end)
+{
+ if (!poolAppend(pool, enc, ptr, end))
+ return NULL;
+ if (pool->ptr == pool->end && !poolGrow(pool))
+ return NULL;
+ *(pool->ptr)++ = 0;
+ return pool->start;
+}
+
+static XML_Bool FASTCALL
+poolGrow(STRING_POOL *pool)
+{
+ if (pool->freeBlocks) {
+ if (pool->start == 0) {
+ pool->blocks = pool->freeBlocks;
+ pool->freeBlocks = pool->freeBlocks->next;
+ pool->blocks->next = NULL;
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + pool->blocks->size;
+ pool->ptr = pool->start;
+ return XML_TRUE;
+ }
+ if (pool->end - pool->start < pool->freeBlocks->size) {
+ BLOCK *tem = pool->freeBlocks->next;
+ pool->freeBlocks->next = pool->blocks;
+ pool->blocks = pool->freeBlocks;
+ pool->freeBlocks = tem;
+ memcpy(pool->blocks->s, pool->start,
+ (pool->end - pool->start) * sizeof(XML_Char));
+ pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + pool->blocks->size;
+ return XML_TRUE;
+ }
+ }
+ if (pool->blocks && pool->start == pool->blocks->s) {
+ int blockSize = (int)(pool->end - pool->start)*2;
+ pool->blocks = (BLOCK *)
+ pool->mem->realloc_fcn(pool->blocks,
+ (offsetof(BLOCK, s)
+ + blockSize * sizeof(XML_Char)));
+ if (pool->blocks == NULL)
+ return XML_FALSE;
+ pool->blocks->size = blockSize;
+ pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + blockSize;
+ }
+ else {
+ BLOCK *tem;
+ int blockSize = (int)(pool->end - pool->start);
+ if (blockSize < INIT_BLOCK_SIZE)
+ blockSize = INIT_BLOCK_SIZE;
+ else
+ blockSize *= 2;
+ tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s)
+ + blockSize * sizeof(XML_Char));
+ if (!tem)
+ return XML_FALSE;
+ tem->size = blockSize;
+ tem->next = pool->blocks;
+ pool->blocks = tem;
+ if (pool->ptr != pool->start)
+ memcpy(tem->s, pool->start,
+ (pool->ptr - pool->start) * sizeof(XML_Char));
+ pool->ptr = tem->s + (pool->ptr - pool->start);
+ pool->start = tem->s;
+ pool->end = tem->s + blockSize;
+ }
+ return XML_TRUE;
+}
+
+static int FASTCALL
+nextScaffoldPart(XML_Parser parser)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ CONTENT_SCAFFOLD * me;
+ int next;
+
+ if (!dtd->scaffIndex) {
+ dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int));
+ if (!dtd->scaffIndex)
+ return -1;
+ dtd->scaffIndex[0] = 0;
+ }
+
+ if (dtd->scaffCount >= dtd->scaffSize) {
+ CONTENT_SCAFFOLD *temp;
+ if (dtd->scaffold) {
+ temp = (CONTENT_SCAFFOLD *)
+ REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
+ if (temp == NULL)
+ return -1;
+ dtd->scaffSize *= 2;
+ }
+ else {
+ temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS
+ * sizeof(CONTENT_SCAFFOLD));
+ if (temp == NULL)
+ return -1;
+ dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS;
+ }
+ dtd->scaffold = temp;
+ }
+ next = dtd->scaffCount++;
+ me = &dtd->scaffold[next];
+ if (dtd->scaffLevel) {
+ CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]];
+ if (parent->lastchild) {
+ dtd->scaffold[parent->lastchild].nextsib = next;
+ }
+ if (!parent->childcnt)
+ parent->firstchild = next;
+ parent->lastchild = next;
+ parent->childcnt++;
+ }
+ me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0;
+ return next;
+}
+
+static void
+build_node(XML_Parser parser,
+ int src_node,
+ XML_Content *dest,
+ XML_Content **contpos,
+ XML_Char **strpos)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ dest->type = dtd->scaffold[src_node].type;
+ dest->quant = dtd->scaffold[src_node].quant;
+ if (dest->type == XML_CTYPE_NAME) {
+ const XML_Char *src;
+ dest->name = *strpos;
+ src = dtd->scaffold[src_node].name;
+ for (;;) {
+ *(*strpos)++ = *src;
+ if (!*src)
+ break;
+ src++;
+ }
+ dest->numchildren = 0;
+ dest->children = NULL;
+ }
+ else {
+ unsigned int i;
+ int cn;
+ dest->numchildren = dtd->scaffold[src_node].childcnt;
+ dest->children = *contpos;
+ *contpos += dest->numchildren;
+ for (i = 0, cn = dtd->scaffold[src_node].firstchild;
+ i < dest->numchildren;
+ i++, cn = dtd->scaffold[cn].nextsib) {
+ build_node(parser, cn, &(dest->children[i]), contpos, strpos);
+ }
+ dest->name = NULL;
+ }
+}
+
+static XML_Content *
+build_model (XML_Parser parser)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ XML_Content *ret;
+ XML_Content *cpos;
+ XML_Char * str;
+ int allocsize = (dtd->scaffCount * sizeof(XML_Content)
+ + (dtd->contentStringLen * sizeof(XML_Char)));
+
+ ret = (XML_Content *)MALLOC(allocsize);
+ if (!ret)
+ return NULL;
+
+ str = (XML_Char *) (&ret[dtd->scaffCount]);
+ cpos = &ret[1];
+
+ build_node(parser, 0, ret, &cpos, &str);
+ return ret;
+}
+
+static ELEMENT_TYPE *
+getElementType(XML_Parser parser,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end)
+{
+ DTD * const dtd = _dtd; /* save one level of indirection */
+ const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end);
+ ELEMENT_TYPE *ret;
+
+ if (!name)
+ return NULL;
+ ret = (ELEMENT_TYPE *) lookup(&dtd->elementTypes, name, sizeof(ELEMENT_TYPE));
+ if (!ret)
+ return NULL;
+ if (ret->name != name)
+ poolDiscard(&dtd->pool);
+ else {
+ poolFinish(&dtd->pool);
+ if (!setElementTypePrefix(parser, ret))
+ return NULL;
+ }
+ return ret;
+}
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlrole.c b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlrole.c
new file mode 100644
index 0000000..9c5e25b
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlrole.c
@@ -0,0 +1,1336 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+#include <stddef.h>
+
+#ifdef COMPILED_FROM_DSP
+#include "winconfig.h"
+#elif defined(MACOS_CLASSIC)
+#include "macconfig.h"
+#elif defined(__amigaos4__)
+#include "amigaconfig.h"
+#elif defined(__WATCOMC__)
+#include "watcomconfig.h"
+#else
+#ifdef HAVE_EXPAT_CONFIG_H
+#include <expat_config.h>
+#endif
+#endif /* ndef COMPILED_FROM_DSP */
+
+#include "expat_external.h"
+#include "internal.h"
+#include "xmlrole.h"
+#include "ascii.h"
+
+/* Doesn't check:
+
+ that ,| are not mixed in a model group
+ content of literals
+
+*/
+
+static const char KW_ANY[] = {
+ ASCII_A, ASCII_N, ASCII_Y, '\0' };
+static const char KW_ATTLIST[] = {
+ ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' };
+static const char KW_CDATA[] = {
+ ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_DOCTYPE[] = {
+ ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' };
+static const char KW_ELEMENT[] = {
+ ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' };
+static const char KW_EMPTY[] = {
+ ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' };
+static const char KW_ENTITIES[] = {
+ ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S,
+ '\0' };
+static const char KW_ENTITY[] = {
+ ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' };
+static const char KW_FIXED[] = {
+ ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' };
+static const char KW_ID[] = {
+ ASCII_I, ASCII_D, '\0' };
+static const char KW_IDREF[] = {
+ ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' };
+static const char KW_IDREFS[] = {
+ ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' };
+#ifdef XML_DTD
+static const char KW_IGNORE[] = {
+ ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' };
+#endif
+static const char KW_IMPLIED[] = {
+ ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' };
+#ifdef XML_DTD
+static const char KW_INCLUDE[] = {
+ ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' };
+#endif
+static const char KW_NDATA[] = {
+ ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_NMTOKEN[] = {
+ ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' };
+static const char KW_NMTOKENS[] = {
+ ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S,
+ '\0' };
+static const char KW_NOTATION[] =
+ { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N,
+ '\0' };
+static const char KW_PCDATA[] = {
+ ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_PUBLIC[] = {
+ ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' };
+static const char KW_REQUIRED[] = {
+ ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D,
+ '\0' };
+static const char KW_SYSTEM[] = {
+ ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' };
+
+#ifndef MIN_BYTES_PER_CHAR
+#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar)
+#endif
+
+#ifdef XML_DTD
+#define setTopLevel(state) \
+ ((state)->handler = ((state)->documentEntity \
+ ? internalSubset \
+ : externalSubset1))
+#else /* not XML_DTD */
+#define setTopLevel(state) ((state)->handler = internalSubset)
+#endif /* not XML_DTD */
+
+typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc);
+
+static PROLOG_HANDLER
+ prolog0, prolog1, prolog2,
+ doctype0, doctype1, doctype2, doctype3, doctype4, doctype5,
+ internalSubset,
+ entity0, entity1, entity2, entity3, entity4, entity5, entity6,
+ entity7, entity8, entity9, entity10,
+ notation0, notation1, notation2, notation3, notation4,
+ attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6,
+ attlist7, attlist8, attlist9,
+ element0, element1, element2, element3, element4, element5, element6,
+ element7,
+#ifdef XML_DTD
+ externalSubset0, externalSubset1,
+ condSect0, condSect1, condSect2,
+#endif /* XML_DTD */
+ declClose,
+ error;
+
+static int FASTCALL common(PROLOG_STATE *state, int tok);
+
+static int PTRCALL
+prolog0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ state->handler = prolog1;
+ return XML_ROLE_NONE;
+ case XML_TOK_XML_DECL:
+ state->handler = prolog1;
+ return XML_ROLE_XML_DECL;
+ case XML_TOK_PI:
+ state->handler = prolog1;
+ return XML_ROLE_PI;
+ case XML_TOK_COMMENT:
+ state->handler = prolog1;
+ return XML_ROLE_COMMENT;
+ case XML_TOK_BOM:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (!XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_DOCTYPE))
+ break;
+ state->handler = doctype0;
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+prolog1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_PI:
+ return XML_ROLE_PI;
+ case XML_TOK_COMMENT:
+ return XML_ROLE_COMMENT;
+ case XML_TOK_BOM:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (!XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_DOCTYPE))
+ break;
+ state->handler = doctype0;
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+prolog2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_PI:
+ return XML_ROLE_PI;
+ case XML_TOK_COMMENT:
+ return XML_ROLE_COMMENT;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+doctype0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = doctype1;
+ return XML_ROLE_DOCTYPE_NAME;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+doctype1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = internalSubset;
+ return XML_ROLE_DOCTYPE_INTERNAL_SUBSET;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+ state->handler = doctype3;
+ return XML_ROLE_DOCTYPE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+ state->handler = doctype2;
+ return XML_ROLE_DOCTYPE_NONE;
+ }
+ break;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+doctype2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = doctype3;
+ return XML_ROLE_DOCTYPE_PUBLIC_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+doctype3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = doctype4;
+ return XML_ROLE_DOCTYPE_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+doctype4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = internalSubset;
+ return XML_ROLE_DOCTYPE_INTERNAL_SUBSET;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+doctype5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+internalSubset(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_ENTITY)) {
+ state->handler = entity0;
+ return XML_ROLE_ENTITY_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_ATTLIST)) {
+ state->handler = attlist0;
+ return XML_ROLE_ATTLIST_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_ELEMENT)) {
+ state->handler = element0;
+ return XML_ROLE_ELEMENT_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_NOTATION)) {
+ state->handler = notation0;
+ return XML_ROLE_NOTATION_NONE;
+ }
+ break;
+ case XML_TOK_PI:
+ return XML_ROLE_PI;
+ case XML_TOK_COMMENT:
+ return XML_ROLE_COMMENT;
+ case XML_TOK_PARAM_ENTITY_REF:
+ return XML_ROLE_PARAM_ENTITY_REF;
+ case XML_TOK_CLOSE_BRACKET:
+ state->handler = doctype5;
+ return XML_ROLE_DOCTYPE_NONE;
+ case XML_TOK_NONE:
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+#ifdef XML_DTD
+
+static int PTRCALL
+externalSubset0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ state->handler = externalSubset1;
+ if (tok == XML_TOK_XML_DECL)
+ return XML_ROLE_TEXT_DECL;
+ return externalSubset1(state, tok, ptr, end, enc);
+}
+
+static int PTRCALL
+externalSubset1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_COND_SECT_OPEN:
+ state->handler = condSect0;
+ return XML_ROLE_NONE;
+ case XML_TOK_COND_SECT_CLOSE:
+ if (state->includeLevel == 0)
+ break;
+ state->includeLevel -= 1;
+ return XML_ROLE_NONE;
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_BRACKET:
+ break;
+ case XML_TOK_NONE:
+ if (state->includeLevel)
+ break;
+ return XML_ROLE_NONE;
+ default:
+ return internalSubset(state, tok, ptr, end, enc);
+ }
+ return common(state, tok);
+}
+
+#endif /* XML_DTD */
+
+static int PTRCALL
+entity0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_PERCENT:
+ state->handler = entity1;
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_NAME:
+ state->handler = entity2;
+ return XML_ROLE_GENERAL_ENTITY_NAME;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_NAME:
+ state->handler = entity7;
+ return XML_ROLE_PARAM_ENTITY_NAME;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+ state->handler = entity4;
+ return XML_ROLE_ENTITY_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+ state->handler = entity3;
+ return XML_ROLE_ENTITY_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ENTITY_NONE;
+ return XML_ROLE_ENTITY_VALUE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity4;
+ return XML_ROLE_ENTITY_PUBLIC_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity5;
+ return XML_ROLE_ENTITY_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return XML_ROLE_ENTITY_COMPLETE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) {
+ state->handler = entity6;
+ return XML_ROLE_ENTITY_NONE;
+ }
+ break;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_NAME:
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ENTITY_NONE;
+ return XML_ROLE_ENTITY_NOTATION_NAME;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+ state->handler = entity9;
+ return XML_ROLE_ENTITY_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+ state->handler = entity8;
+ return XML_ROLE_ENTITY_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ENTITY_NONE;
+ return XML_ROLE_ENTITY_VALUE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity8(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity9;
+ return XML_ROLE_ENTITY_PUBLIC_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity9(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity10;
+ return XML_ROLE_ENTITY_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+entity10(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ENTITY_NONE;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return XML_ROLE_ENTITY_COMPLETE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+notation0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NOTATION_NONE;
+ case XML_TOK_NAME:
+ state->handler = notation1;
+ return XML_ROLE_NOTATION_NAME;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+notation1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NOTATION_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+ state->handler = notation3;
+ return XML_ROLE_NOTATION_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+ state->handler = notation2;
+ return XML_ROLE_NOTATION_NONE;
+ }
+ break;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+notation2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NOTATION_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = notation4;
+ return XML_ROLE_NOTATION_PUBLIC_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+notation3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NOTATION_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ state->role_none = XML_ROLE_NOTATION_NONE;
+ return XML_ROLE_NOTATION_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+notation4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NOTATION_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ state->role_none = XML_ROLE_NOTATION_NONE;
+ return XML_ROLE_NOTATION_SYSTEM_ID;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return XML_ROLE_NOTATION_NO_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist1;
+ return XML_ROLE_ATTLIST_ELEMENT_NAME;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist2;
+ return XML_ROLE_ATTRIBUTE_NAME;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_NAME:
+ {
+ static const char * const types[] = {
+ KW_CDATA,
+ KW_ID,
+ KW_IDREF,
+ KW_IDREFS,
+ KW_ENTITY,
+ KW_ENTITIES,
+ KW_NMTOKEN,
+ KW_NMTOKENS,
+ };
+ int i;
+ for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++)
+ if (XmlNameMatchesAscii(enc, ptr, end, types[i])) {
+ state->handler = attlist8;
+ return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i;
+ }
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) {
+ state->handler = attlist5;
+ return XML_ROLE_ATTLIST_NONE;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = attlist3;
+ return XML_ROLE_ATTLIST_NONE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_NMTOKEN:
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist4;
+ return XML_ROLE_ATTRIBUTE_ENUM_VALUE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->handler = attlist8;
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_OR:
+ state->handler = attlist3;
+ return XML_ROLE_ATTLIST_NONE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = attlist6;
+ return XML_ROLE_ATTLIST_NONE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_NAME:
+ state->handler = attlist7;
+ return XML_ROLE_ATTRIBUTE_NOTATION_VALUE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->handler = attlist8;
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_OR:
+ state->handler = attlist6;
+ return XML_ROLE_ATTLIST_NONE;
+ }
+ return common(state, tok);
+}
+
+/* default value */
+static int PTRCALL
+attlist8(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_POUND_NAME:
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_IMPLIED)) {
+ state->handler = attlist1;
+ return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_REQUIRED)) {
+ state->handler = attlist1;
+ return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_FIXED)) {
+ state->handler = attlist9;
+ return XML_ROLE_ATTLIST_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = attlist1;
+ return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+attlist9(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ATTLIST_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = attlist1;
+ return XML_ROLE_FIXED_ATTRIBUTE_VALUE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+element0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ELEMENT_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element1;
+ return XML_ROLE_ELEMENT_NAME;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+element1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ELEMENT_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) {
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ return XML_ROLE_CONTENT_EMPTY;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) {
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ return XML_ROLE_CONTENT_ANY;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = element2;
+ state->level = 1;
+ return XML_ROLE_GROUP_OPEN;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+element2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ELEMENT_NONE;
+ case XML_TOK_POUND_NAME:
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_PCDATA)) {
+ state->handler = element3;
+ return XML_ROLE_CONTENT_PCDATA;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->level = 2;
+ state->handler = element6;
+ return XML_ROLE_GROUP_OPEN;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT;
+ case XML_TOK_NAME_QUESTION:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_OPT;
+ case XML_TOK_NAME_ASTERISK:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_REP;
+ case XML_TOK_NAME_PLUS:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_PLUS;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+element3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ELEMENT_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ return XML_ROLE_GROUP_CLOSE;
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_OR:
+ state->handler = element4;
+ return XML_ROLE_ELEMENT_NONE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+element4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ELEMENT_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element5;
+ return XML_ROLE_CONTENT_ELEMENT;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+element5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ELEMENT_NONE;
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_OR:
+ state->handler = element4;
+ return XML_ROLE_ELEMENT_NONE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+element6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ELEMENT_NONE;
+ case XML_TOK_OPEN_PAREN:
+ state->level += 1;
+ return XML_ROLE_GROUP_OPEN;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT;
+ case XML_TOK_NAME_QUESTION:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_OPT;
+ case XML_TOK_NAME_ASTERISK:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_REP;
+ case XML_TOK_NAME_PLUS:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_PLUS;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+element7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_ELEMENT_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->level -= 1;
+ if (state->level == 0) {
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ }
+ return XML_ROLE_GROUP_CLOSE;
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->level -= 1;
+ if (state->level == 0) {
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ }
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_CLOSE_PAREN_QUESTION:
+ state->level -= 1;
+ if (state->level == 0) {
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ }
+ return XML_ROLE_GROUP_CLOSE_OPT;
+ case XML_TOK_CLOSE_PAREN_PLUS:
+ state->level -= 1;
+ if (state->level == 0) {
+ state->handler = declClose;
+ state->role_none = XML_ROLE_ELEMENT_NONE;
+ }
+ return XML_ROLE_GROUP_CLOSE_PLUS;
+ case XML_TOK_COMMA:
+ state->handler = element6;
+ return XML_ROLE_GROUP_SEQUENCE;
+ case XML_TOK_OR:
+ state->handler = element6;
+ return XML_ROLE_GROUP_CHOICE;
+ }
+ return common(state, tok);
+}
+
+#ifdef XML_DTD
+
+static int PTRCALL
+condSect0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) {
+ state->handler = condSect1;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) {
+ state->handler = condSect2;
+ return XML_ROLE_NONE;
+ }
+ break;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+condSect1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = externalSubset1;
+ state->includeLevel += 1;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+condSect2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = externalSubset1;
+ return XML_ROLE_IGNORE_SECT;
+ }
+ return common(state, tok);
+}
+
+#endif /* XML_DTD */
+
+static int PTRCALL
+declClose(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return state->role_none;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return state->role_none;
+ }
+ return common(state, tok);
+}
+
+static int PTRCALL
+error(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ return XML_ROLE_NONE;
+}
+
+static int FASTCALL
+common(PROLOG_STATE *state, int tok)
+{
+#ifdef XML_DTD
+ if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF)
+ return XML_ROLE_INNER_PARAM_ENTITY_REF;
+#endif
+ state->handler = error;
+ return XML_ROLE_ERROR;
+}
+
+void
+XmlPrologStateInit(PROLOG_STATE *state)
+{
+ state->handler = prolog0;
+#ifdef XML_DTD
+ state->documentEntity = 1;
+ state->includeLevel = 0;
+ state->inEntityValue = 0;
+#endif /* XML_DTD */
+}
+
+#ifdef XML_DTD
+
+void
+XmlPrologStateInitExternalEntity(PROLOG_STATE *state)
+{
+ state->handler = externalSubset0;
+ state->documentEntity = 0;
+ state->includeLevel = 0;
+}
+
+#endif /* XML_DTD */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlrole.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlrole.h
new file mode 100644
index 0000000..4dd9f06
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmlrole.h
@@ -0,0 +1,114 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+#ifndef XmlRole_INCLUDED
+#define XmlRole_INCLUDED 1
+
+#ifdef __VMS
+/* 0 1 2 3 0 1 2 3
+ 1234567890123456789012345678901 1234567890123456789012345678901 */
+#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt
+#endif
+
+#include "xmltok.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ XML_ROLE_ERROR = -1,
+ XML_ROLE_NONE = 0,
+ XML_ROLE_XML_DECL,
+ XML_ROLE_INSTANCE_START,
+ XML_ROLE_DOCTYPE_NONE,
+ XML_ROLE_DOCTYPE_NAME,
+ XML_ROLE_DOCTYPE_SYSTEM_ID,
+ XML_ROLE_DOCTYPE_PUBLIC_ID,
+ XML_ROLE_DOCTYPE_INTERNAL_SUBSET,
+ XML_ROLE_DOCTYPE_CLOSE,
+ XML_ROLE_GENERAL_ENTITY_NAME,
+ XML_ROLE_PARAM_ENTITY_NAME,
+ XML_ROLE_ENTITY_NONE,
+ XML_ROLE_ENTITY_VALUE,
+ XML_ROLE_ENTITY_SYSTEM_ID,
+ XML_ROLE_ENTITY_PUBLIC_ID,
+ XML_ROLE_ENTITY_COMPLETE,
+ XML_ROLE_ENTITY_NOTATION_NAME,
+ XML_ROLE_NOTATION_NONE,
+ XML_ROLE_NOTATION_NAME,
+ XML_ROLE_NOTATION_SYSTEM_ID,
+ XML_ROLE_NOTATION_NO_SYSTEM_ID,
+ XML_ROLE_NOTATION_PUBLIC_ID,
+ XML_ROLE_ATTRIBUTE_NAME,
+ XML_ROLE_ATTRIBUTE_TYPE_CDATA,
+ XML_ROLE_ATTRIBUTE_TYPE_ID,
+ XML_ROLE_ATTRIBUTE_TYPE_IDREF,
+ XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
+ XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
+ XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
+ XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
+ XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
+ XML_ROLE_ATTRIBUTE_ENUM_VALUE,
+ XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
+ XML_ROLE_ATTLIST_NONE,
+ XML_ROLE_ATTLIST_ELEMENT_NAME,
+ XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
+ XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
+ XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
+ XML_ROLE_FIXED_ATTRIBUTE_VALUE,
+ XML_ROLE_ELEMENT_NONE,
+ XML_ROLE_ELEMENT_NAME,
+ XML_ROLE_CONTENT_ANY,
+ XML_ROLE_CONTENT_EMPTY,
+ XML_ROLE_CONTENT_PCDATA,
+ XML_ROLE_GROUP_OPEN,
+ XML_ROLE_GROUP_CLOSE,
+ XML_ROLE_GROUP_CLOSE_REP,
+ XML_ROLE_GROUP_CLOSE_OPT,
+ XML_ROLE_GROUP_CLOSE_PLUS,
+ XML_ROLE_GROUP_CHOICE,
+ XML_ROLE_GROUP_SEQUENCE,
+ XML_ROLE_CONTENT_ELEMENT,
+ XML_ROLE_CONTENT_ELEMENT_REP,
+ XML_ROLE_CONTENT_ELEMENT_OPT,
+ XML_ROLE_CONTENT_ELEMENT_PLUS,
+ XML_ROLE_PI,
+ XML_ROLE_COMMENT,
+#ifdef XML_DTD
+ XML_ROLE_TEXT_DECL,
+ XML_ROLE_IGNORE_SECT,
+ XML_ROLE_INNER_PARAM_ENTITY_REF,
+#endif /* XML_DTD */
+ XML_ROLE_PARAM_ENTITY_REF
+};
+
+typedef struct prolog_state {
+ int (PTRCALL *handler) (struct prolog_state *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc);
+ unsigned level;
+ int role_none;
+#ifdef XML_DTD
+ unsigned includeLevel;
+ int documentEntity;
+ int inEntityValue;
+#endif /* XML_DTD */
+} PROLOG_STATE;
+
+void XmlPrologStateInit(PROLOG_STATE *);
+#ifdef XML_DTD
+void XmlPrologStateInitExternalEntity(PROLOG_STATE *);
+#endif /* XML_DTD */
+
+#define XmlTokenRole(state, tok, ptr, end, enc) \
+ (((state)->handler)(state, tok, ptr, end, enc))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlRole_INCLUDED */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok.c b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok.c
new file mode 100644
index 0000000..068afde
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok.c
@@ -0,0 +1,1651 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+#include <stddef.h>
+
+#ifdef COMPILED_FROM_DSP
+#include "winconfig.h"
+#elif defined(MACOS_CLASSIC)
+#include "macconfig.h"
+#elif defined(__amigaos4__)
+#include "amigaconfig.h"
+#elif defined(__WATCOMC__)
+#include "watcomconfig.h"
+#else
+#ifdef HAVE_EXPAT_CONFIG_H
+#include <expat_config.h>
+#endif
+#endif /* ndef COMPILED_FROM_DSP */
+
+#include "expat_external.h"
+#include "internal.h"
+#include "xmltok.h"
+#include "nametab.h"
+
+#ifdef XML_DTD
+#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok)
+#else
+#define IGNORE_SECTION_TOK_VTABLE /* as nothing */
+#endif
+
+#define VTABLE1 \
+ { PREFIX(prologTok), PREFIX(contentTok), \
+ PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \
+ { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \
+ PREFIX(sameName), \
+ PREFIX(nameMatchesAscii), \
+ PREFIX(nameLength), \
+ PREFIX(skipS), \
+ PREFIX(getAtts), \
+ PREFIX(charRefNumber), \
+ PREFIX(predefinedEntityName), \
+ PREFIX(updatePosition), \
+ PREFIX(isPublicId)
+
+#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16)
+
+#define UCS2_GET_NAMING(pages, hi, lo) \
+ (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F)))
+
+/* A 2 byte UTF-8 representation splits the characters 11 bits between
+ the bottom 5 and 6 bits of the bytes. We need 8 bits to index into
+ pages, 3 bits to add to that index and 5 bits to generate the mask.
+*/
+#define UTF8_GET_NAMING2(pages, byte) \
+ (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \
+ + ((((byte)[0]) & 3) << 1) \
+ + ((((byte)[1]) >> 5) & 1)] \
+ & (1 << (((byte)[1]) & 0x1F)))
+
+/* A 3 byte UTF-8 representation splits the characters 16 bits between
+ the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index
+ into pages, 3 bits to add to that index and 5 bits to generate the
+ mask.
+*/
+#define UTF8_GET_NAMING3(pages, byte) \
+ (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \
+ + ((((byte)[1]) >> 2) & 0xF)] \
+ << 3) \
+ + ((((byte)[1]) & 3) << 1) \
+ + ((((byte)[2]) >> 5) & 1)] \
+ & (1 << (((byte)[2]) & 0x1F)))
+
+#define UTF8_GET_NAMING(pages, p, n) \
+ ((n) == 2 \
+ ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \
+ : ((n) == 3 \
+ ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \
+ : 0))
+
+/* Detection of invalid UTF-8 sequences is based on Table 3.1B
+ of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
+ with the additional restriction of not allowing the Unicode
+ code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE).
+ Implementation details:
+ (A & 0x80) == 0 means A < 0x80
+ and
+ (A & 0xC0) == 0xC0 means A > 0xBF
+*/
+
+#define UTF8_INVALID2(p) \
+ ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0)
+
+#define UTF8_INVALID3(p) \
+ (((p)[2] & 0x80) == 0 \
+ || \
+ ((*p) == 0xEF && (p)[1] == 0xBF \
+ ? \
+ (p)[2] > 0xBD \
+ : \
+ ((p)[2] & 0xC0) == 0xC0) \
+ || \
+ ((*p) == 0xE0 \
+ ? \
+ (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \
+ : \
+ ((p)[1] & 0x80) == 0 \
+ || \
+ ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0)))
+
+#define UTF8_INVALID4(p) \
+ (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \
+ || \
+ ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \
+ || \
+ ((*p) == 0xF0 \
+ ? \
+ (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \
+ : \
+ ((p)[1] & 0x80) == 0 \
+ || \
+ ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0)))
+
+static int PTRFASTCALL
+isNever(const ENCODING *enc, const char *p)
+{
+ return 0;
+}
+
+static int PTRFASTCALL
+utf8_isName2(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING2(namePages, (const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isName3(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING3(namePages, (const unsigned char *)p);
+}
+
+#define utf8_isName4 isNever
+
+static int PTRFASTCALL
+utf8_isNmstrt2(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isNmstrt3(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p);
+}
+
+#define utf8_isNmstrt4 isNever
+
+static int PTRFASTCALL
+utf8_isInvalid2(const ENCODING *enc, const char *p)
+{
+ return UTF8_INVALID2((const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isInvalid3(const ENCODING *enc, const char *p)
+{
+ return UTF8_INVALID3((const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isInvalid4(const ENCODING *enc, const char *p)
+{
+ return UTF8_INVALID4((const unsigned char *)p);
+}
+
+struct normal_encoding {
+ ENCODING enc;
+ unsigned char type[256];
+#ifdef XML_MIN_SIZE
+ int (PTRFASTCALL *byteType)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *);
+ int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *);
+ int (PTRCALL *charMatches)(const ENCODING *, const char *, int);
+#endif /* XML_MIN_SIZE */
+ int (PTRFASTCALL *isName2)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isName3)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isName4)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *);
+ int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *);
+};
+
+#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc))
+
+#ifdef XML_MIN_SIZE
+
+#define STANDARD_VTABLE(E) \
+ E ## byteType, \
+ E ## isNameMin, \
+ E ## isNmstrtMin, \
+ E ## byteToAscii, \
+ E ## charMatches,
+
+#else
+
+#define STANDARD_VTABLE(E) /* as nothing */
+
+#endif
+
+#define NORMAL_VTABLE(E) \
+ E ## isName2, \
+ E ## isName3, \
+ E ## isName4, \
+ E ## isNmstrt2, \
+ E ## isNmstrt3, \
+ E ## isNmstrt4, \
+ E ## isInvalid2, \
+ E ## isInvalid3, \
+ E ## isInvalid4
+
+static int FASTCALL checkCharRefNumber(int);
+
+#include "xmltok_impl.h"
+#include "ascii.h"
+
+#ifdef XML_MIN_SIZE
+#define sb_isNameMin isNever
+#define sb_isNmstrtMin isNever
+#endif
+
+#ifdef XML_MIN_SIZE
+#define MINBPC(enc) ((enc)->minBytesPerChar)
+#else
+/* minimum bytes per character */
+#define MINBPC(enc) 1
+#endif
+
+#define SB_BYTE_TYPE(enc, p) \
+ (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)])
+
+#ifdef XML_MIN_SIZE
+static int PTRFASTCALL
+sb_byteType(const ENCODING *enc, const char *p)
+{
+ return SB_BYTE_TYPE(enc, p);
+}
+#define BYTE_TYPE(enc, p) \
+ (AS_NORMAL_ENCODING(enc)->byteType(enc, p))
+#else
+#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define BYTE_TO_ASCII(enc, p) \
+ (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p))
+static int PTRFASTCALL
+sb_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return *p;
+}
+#else
+#define BYTE_TO_ASCII(enc, p) (*(p))
+#endif
+
+#define IS_NAME_CHAR(enc, p, n) \
+ (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p))
+#define IS_NMSTRT_CHAR(enc, p, n) \
+ (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p))
+#define IS_INVALID_CHAR(enc, p, n) \
+ (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p))
+
+#ifdef XML_MIN_SIZE
+#define IS_NAME_CHAR_MINBPC(enc, p) \
+ (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p))
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p))
+#else
+#define IS_NAME_CHAR_MINBPC(enc, p) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define CHAR_MATCHES(enc, p, c) \
+ (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c))
+static int PTRCALL
+sb_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return *p == c;
+}
+#else
+/* c is an ASCII character */
+#define CHAR_MATCHES(enc, p, c) (*(p) == c)
+#endif
+
+#define PREFIX(ident) normal_ ## ident
+#define XML_TOK_IMPL_C
+#include "xmltok_impl.c"
+#undef XML_TOK_IMPL_C
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */
+ UTF8_cval1 = 0x00,
+ UTF8_cval2 = 0xc0,
+ UTF8_cval3 = 0xe0,
+ UTF8_cval4 = 0xf0
+};
+
+static void PTRCALL
+utf8_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ char *to;
+ const char *from;
+ if (fromLim - *fromP > toLim - *toP) {
+ /* Avoid copying partial characters. */
+ for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--)
+ if (((unsigned char)fromLim[-1] & 0xc0) != 0x80)
+ break;
+ }
+ for (to = *toP, from = *fromP; from != fromLim; from++, to++)
+ *to = *from;
+ *fromP = from;
+ *toP = to;
+}
+
+static void PTRCALL
+utf8_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ unsigned short *to = *toP;
+ const char *from = *fromP;
+ while (from != fromLim && to != toLim) {
+ switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) {
+ case BT_LEAD2:
+ *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f));
+ from += 2;
+ break;
+ case BT_LEAD3:
+ *to++ = (unsigned short)(((from[0] & 0xf) << 12)
+ | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f));
+ from += 3;
+ break;
+ case BT_LEAD4:
+ {
+ unsigned long n;
+ if (to + 1 == toLim)
+ goto after;
+ n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12)
+ | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f);
+ n -= 0x10000;
+ to[0] = (unsigned short)((n >> 10) | 0xD800);
+ to[1] = (unsigned short)((n & 0x3FF) | 0xDC00);
+ to += 2;
+ from += 4;
+ }
+ break;
+ default:
+ *to++ = *from++;
+ break;
+ }
+ }
+after:
+ *fromP = from;
+ *toP = to;
+}
+
+#ifdef XML_NS
+static const struct normal_encoding utf8_encoding_ns = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#include "asciitab.h"
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+#endif
+
+static const struct normal_encoding utf8_encoding = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_utf8_encoding_ns = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#include "iasciitab.h"
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+#endif
+
+static const struct normal_encoding internal_utf8_encoding = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+static void PTRCALL
+latin1_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ for (;;) {
+ unsigned char c;
+ if (*fromP == fromLim)
+ break;
+ c = (unsigned char)**fromP;
+ if (c & 0x80) {
+ if (toLim - *toP < 2)
+ break;
+ *(*toP)++ = (char)((c >> 6) | UTF8_cval2);
+ *(*toP)++ = (char)((c & 0x3f) | 0x80);
+ (*fromP)++;
+ }
+ else {
+ if (*toP == toLim)
+ break;
+ *(*toP)++ = *(*fromP)++;
+ }
+ }
+}
+
+static void PTRCALL
+latin1_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ while (*fromP != fromLim && *toP != toLim)
+ *(*toP)++ = (unsigned char)*(*fromP)++;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding latin1_encoding_ns = {
+ { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(sb_)
+};
+
+#endif
+
+static const struct normal_encoding latin1_encoding = {
+ { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(sb_)
+};
+
+static void PTRCALL
+ascii_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ while (*fromP != fromLim && *toP != toLim)
+ *(*toP)++ = *(*fromP)++;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding ascii_encoding_ns = {
+ { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+ {
+#include "asciitab.h"
+/* BT_NONXML == 0 */
+ },
+ STANDARD_VTABLE(sb_)
+};
+
+#endif
+
+static const struct normal_encoding ascii_encoding = {
+ { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+/* BT_NONXML == 0 */
+ },
+ STANDARD_VTABLE(sb_)
+};
+
+static int PTRFASTCALL
+unicode_byte_type(char hi, char lo)
+{
+ switch ((unsigned char)hi) {
+ case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+ return BT_LEAD4;
+ case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+ return BT_TRAIL;
+ case 0xFF:
+ switch ((unsigned char)lo) {
+ case 0xFF:
+ case 0xFE:
+ return BT_NONXML;
+ }
+ break;
+ }
+ return BT_NONASCII;
+}
+
+#define DEFINE_UTF16_TO_UTF8(E) \
+static void PTRCALL \
+E ## toUtf8(const ENCODING *enc, \
+ const char **fromP, const char *fromLim, \
+ char **toP, const char *toLim) \
+{ \
+ const char *from; \
+ for (from = *fromP; from != fromLim; from += 2) { \
+ int plane; \
+ unsigned char lo2; \
+ unsigned char lo = GET_LO(from); \
+ unsigned char hi = GET_HI(from); \
+ switch (hi) { \
+ case 0: \
+ if (lo < 0x80) { \
+ if (*toP == toLim) { \
+ *fromP = from; \
+ return; \
+ } \
+ *(*toP)++ = lo; \
+ break; \
+ } \
+ /* fall through */ \
+ case 0x1: case 0x2: case 0x3: \
+ case 0x4: case 0x5: case 0x6: case 0x7: \
+ if (toLim - *toP < 2) { \
+ *fromP = from; \
+ return; \
+ } \
+ *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \
+ *(*toP)++ = ((lo & 0x3f) | 0x80); \
+ break; \
+ default: \
+ if (toLim - *toP < 3) { \
+ *fromP = from; \
+ return; \
+ } \
+ /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \
+ *(*toP)++ = ((hi >> 4) | UTF8_cval3); \
+ *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \
+ *(*toP)++ = ((lo & 0x3f) | 0x80); \
+ break; \
+ case 0xD8: case 0xD9: case 0xDA: case 0xDB: \
+ if (toLim - *toP < 4) { \
+ *fromP = from; \
+ return; \
+ } \
+ plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \
+ *(*toP)++ = ((plane >> 2) | UTF8_cval4); \
+ *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \
+ from += 2; \
+ lo2 = GET_LO(from); \
+ *(*toP)++ = (((lo & 0x3) << 4) \
+ | ((GET_HI(from) & 0x3) << 2) \
+ | (lo2 >> 6) \
+ | 0x80); \
+ *(*toP)++ = ((lo2 & 0x3f) | 0x80); \
+ break; \
+ } \
+ } \
+ *fromP = from; \
+}
+
+#define DEFINE_UTF16_TO_UTF16(E) \
+static void PTRCALL \
+E ## toUtf16(const ENCODING *enc, \
+ const char **fromP, const char *fromLim, \
+ unsigned short **toP, const unsigned short *toLim) \
+{ \
+ /* Avoid copying first half only of surrogate */ \
+ if (fromLim - *fromP > ((toLim - *toP) << 1) \
+ && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \
+ fromLim -= 2; \
+ for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \
+ *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \
+}
+
+#define SET2(ptr, ch) \
+ (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[0])
+#define GET_HI(ptr) ((unsigned char)(ptr)[1])
+
+DEFINE_UTF16_TO_UTF8(little2_)
+DEFINE_UTF16_TO_UTF16(little2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define SET2(ptr, ch) \
+ (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[1])
+#define GET_HI(ptr) ((unsigned char)(ptr)[0])
+
+DEFINE_UTF16_TO_UTF8(big2_)
+DEFINE_UTF16_TO_UTF16(big2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define LITTLE2_BYTE_TYPE(enc, p) \
+ ((p)[1] == 0 \
+ ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \
+ : unicode_byte_type((p)[1], (p)[0]))
+#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1)
+#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c)
+#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0])
+#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0])
+
+#ifdef XML_MIN_SIZE
+
+static int PTRFASTCALL
+little2_byteType(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_BYTE_TYPE(enc, p);
+}
+
+static int PTRFASTCALL
+little2_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_BYTE_TO_ASCII(enc, p);
+}
+
+static int PTRCALL
+little2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return LITTLE2_CHAR_MATCHES(enc, p, c);
+}
+
+static int PTRFASTCALL
+little2_isNameMin(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static int PTRFASTCALL
+little2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) little2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p)
+#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#define XML_TOK_IMPL_C
+#include "xmltok_impl.c"
+#undef XML_TOK_IMPL_C
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding little2_encoding_ns = {
+ { VTABLE, 2, 0,
+#if BYTEORDER == 1234
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+};
+
+#endif
+
+static const struct normal_encoding little2_encoding = {
+ { VTABLE, 2, 0,
+#if BYTEORDER == 1234
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+};
+
+#if BYTEORDER != 4321
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_little2_encoding_ns = {
+ { VTABLE, 2, 0, 1 },
+ {
+#include "iasciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+};
+
+#endif
+
+static const struct normal_encoding internal_little2_encoding = {
+ { VTABLE, 2, 0, 1 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+};
+
+#endif
+
+
+#define BIG2_BYTE_TYPE(enc, p) \
+ ((p)[0] == 0 \
+ ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \
+ : unicode_byte_type((p)[0], (p)[1]))
+#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1)
+#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c)
+#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1])
+#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1])
+
+#ifdef XML_MIN_SIZE
+
+static int PTRFASTCALL
+big2_byteType(const ENCODING *enc, const char *p)
+{
+ return BIG2_BYTE_TYPE(enc, p);
+}
+
+static int PTRFASTCALL
+big2_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return BIG2_BYTE_TO_ASCII(enc, p);
+}
+
+static int PTRCALL
+big2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return BIG2_CHAR_MATCHES(enc, p, c);
+}
+
+static int PTRFASTCALL
+big2_isNameMin(const ENCODING *enc, const char *p)
+{
+ return BIG2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static int PTRFASTCALL
+big2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+ return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) big2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p)
+#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#define XML_TOK_IMPL_C
+#include "xmltok_impl.c"
+#undef XML_TOK_IMPL_C
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding big2_encoding_ns = {
+ { VTABLE, 2, 0,
+#if BYTEORDER == 4321
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+};
+
+#endif
+
+static const struct normal_encoding big2_encoding = {
+ { VTABLE, 2, 0,
+#if BYTEORDER == 4321
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+};
+
+#if BYTEORDER != 1234
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_big2_encoding_ns = {
+ { VTABLE, 2, 0, 1 },
+ {
+#include "iasciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+};
+
+#endif
+
+static const struct normal_encoding internal_big2_encoding = {
+ { VTABLE, 2, 0, 1 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+};
+
+#endif
+
+#undef PREFIX
+
+static int FASTCALL
+streqci(const char *s1, const char *s2)
+{
+ for (;;) {
+ char c1 = *s1++;
+ char c2 = *s2++;
+ if (ASCII_a <= c1 && c1 <= ASCII_z)
+ c1 += ASCII_A - ASCII_a;
+ if (ASCII_a <= c2 && c2 <= ASCII_z)
+ c2 += ASCII_A - ASCII_a;
+ if (c1 != c2)
+ return 0;
+ if (!c1)
+ break;
+ }
+ return 1;
+}
+
+static void PTRCALL
+initUpdatePosition(const ENCODING *enc, const char *ptr,
+ const char *end, POSITION *pos)
+{
+ normal_updatePosition(&utf8_encoding.enc, ptr, end, pos);
+}
+
+static int
+toAscii(const ENCODING *enc, const char *ptr, const char *end)
+{
+ char buf[1];
+ char *p = buf;
+ XmlUtf8Convert(enc, &ptr, end, &p, p + 1);
+ if (p == buf)
+ return -1;
+ else
+ return buf[0];
+}
+
+static int FASTCALL
+isSpace(int c)
+{
+ switch (c) {
+ case 0x20:
+ case 0xD:
+ case 0xA:
+ case 0x9:
+ return 1;
+ }
+ return 0;
+}
+
+/* Return 1 if there's just optional white space or there's an S
+ followed by name=val.
+*/
+static int
+parsePseudoAttribute(const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **namePtr,
+ const char **nameEndPtr,
+ const char **valPtr,
+ const char **nextTokPtr)
+{
+ int c;
+ char open;
+ if (ptr == end) {
+ *namePtr = NULL;
+ return 1;
+ }
+ if (!isSpace(toAscii(enc, ptr, end))) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ do {
+ ptr += enc->minBytesPerChar;
+ } while (isSpace(toAscii(enc, ptr, end)));
+ if (ptr == end) {
+ *namePtr = NULL;
+ return 1;
+ }
+ *namePtr = ptr;
+ for (;;) {
+ c = toAscii(enc, ptr, end);
+ if (c == -1) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ if (c == ASCII_EQUALS) {
+ *nameEndPtr = ptr;
+ break;
+ }
+ if (isSpace(c)) {
+ *nameEndPtr = ptr;
+ do {
+ ptr += enc->minBytesPerChar;
+ } while (isSpace(c = toAscii(enc, ptr, end)));
+ if (c != ASCII_EQUALS) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ break;
+ }
+ ptr += enc->minBytesPerChar;
+ }
+ if (ptr == *namePtr) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ ptr += enc->minBytesPerChar;
+ c = toAscii(enc, ptr, end);
+ while (isSpace(c)) {
+ ptr += enc->minBytesPerChar;
+ c = toAscii(enc, ptr, end);
+ }
+ if (c != ASCII_QUOT && c != ASCII_APOS) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ open = (char)c;
+ ptr += enc->minBytesPerChar;
+ *valPtr = ptr;
+ for (;; ptr += enc->minBytesPerChar) {
+ c = toAscii(enc, ptr, end);
+ if (c == open)
+ break;
+ if (!(ASCII_a <= c && c <= ASCII_z)
+ && !(ASCII_A <= c && c <= ASCII_Z)
+ && !(ASCII_0 <= c && c <= ASCII_9)
+ && c != ASCII_PERIOD
+ && c != ASCII_MINUS
+ && c != ASCII_UNDERSCORE) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ }
+ *nextTokPtr = ptr + enc->minBytesPerChar;
+ return 1;
+}
+
+static const char KW_version[] = {
+ ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0'
+};
+
+static const char KW_encoding[] = {
+ ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0'
+};
+
+static const char KW_standalone[] = {
+ ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o,
+ ASCII_n, ASCII_e, '\0'
+};
+
+static const char KW_yes[] = {
+ ASCII_y, ASCII_e, ASCII_s, '\0'
+};
+
+static const char KW_no[] = {
+ ASCII_n, ASCII_o, '\0'
+};
+
+static int
+doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *,
+ const char *,
+ const char *),
+ int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **versionEndPtr,
+ const char **encodingName,
+ const ENCODING **encoding,
+ int *standalone)
+{
+ const char *val = NULL;
+ const char *name = NULL;
+ const char *nameEnd = NULL;
+ ptr += 5 * enc->minBytesPerChar;
+ end -= 2 * enc->minBytesPerChar;
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)
+ || !name) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) {
+ if (!isGeneralTextEntity) {
+ *badPtr = name;
+ return 0;
+ }
+ }
+ else {
+ if (versionPtr)
+ *versionPtr = val;
+ if (versionEndPtr)
+ *versionEndPtr = ptr;
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!name) {
+ if (isGeneralTextEntity) {
+ /* a TextDecl must have an EncodingDecl */
+ *badPtr = ptr;
+ return 0;
+ }
+ return 1;
+ }
+ }
+ if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) {
+ int c = toAscii(enc, val, end);
+ if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) {
+ *badPtr = val;
+ return 0;
+ }
+ if (encodingName)
+ *encodingName = val;
+ if (encoding)
+ *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar);
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!name)
+ return 1;
+ }
+ if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone)
+ || isGeneralTextEntity) {
+ *badPtr = name;
+ return 0;
+ }
+ if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) {
+ if (standalone)
+ *standalone = 1;
+ }
+ else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) {
+ if (standalone)
+ *standalone = 0;
+ }
+ else {
+ *badPtr = val;
+ return 0;
+ }
+ while (isSpace(toAscii(enc, ptr, end)))
+ ptr += enc->minBytesPerChar;
+ if (ptr != end) {
+ *badPtr = ptr;
+ return 0;
+ }
+ return 1;
+}
+
+static int FASTCALL
+checkCharRefNumber(int result)
+{
+ switch (result >> 8) {
+ case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+ case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+ return -1;
+ case 0:
+ if (latin1_encoding.type[result] == BT_NONXML)
+ return -1;
+ break;
+ case 0xFF:
+ if (result == 0xFFFE || result == 0xFFFF)
+ return -1;
+ break;
+ }
+ return result;
+}
+
+int FASTCALL
+XmlUtf8Encode(int c, char *buf)
+{
+ enum {
+ /* minN is minimum legal resulting value for N byte sequence */
+ min2 = 0x80,
+ min3 = 0x800,
+ min4 = 0x10000
+ };
+
+ if (c < 0)
+ return 0;
+ if (c < min2) {
+ buf[0] = (char)(c | UTF8_cval1);
+ return 1;
+ }
+ if (c < min3) {
+ buf[0] = (char)((c >> 6) | UTF8_cval2);
+ buf[1] = (char)((c & 0x3f) | 0x80);
+ return 2;
+ }
+ if (c < min4) {
+ buf[0] = (char)((c >> 12) | UTF8_cval3);
+ buf[1] = (char)(((c >> 6) & 0x3f) | 0x80);
+ buf[2] = (char)((c & 0x3f) | 0x80);
+ return 3;
+ }
+ if (c < 0x110000) {
+ buf[0] = (char)((c >> 18) | UTF8_cval4);
+ buf[1] = (char)(((c >> 12) & 0x3f) | 0x80);
+ buf[2] = (char)(((c >> 6) & 0x3f) | 0x80);
+ buf[3] = (char)((c & 0x3f) | 0x80);
+ return 4;
+ }
+ return 0;
+}
+
+int FASTCALL
+XmlUtf16Encode(int charNum, unsigned short *buf)
+{
+ if (charNum < 0)
+ return 0;
+ if (charNum < 0x10000) {
+ buf[0] = (unsigned short)charNum;
+ return 1;
+ }
+ if (charNum < 0x110000) {
+ charNum -= 0x10000;
+ buf[0] = (unsigned short)((charNum >> 10) + 0xD800);
+ buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00);
+ return 2;
+ }
+ return 0;
+}
+
+struct unknown_encoding {
+ struct normal_encoding normal;
+ CONVERTER convert;
+ void *userData;
+ unsigned short utf16[256];
+ char utf8[256][4];
+};
+
+#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc))
+
+int
+XmlSizeOfUnknownEncoding(void)
+{
+ return sizeof(struct unknown_encoding);
+}
+
+static int PTRFASTCALL
+unknown_isName(const ENCODING *enc, const char *p)
+{
+ const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+ int c = uenc->convert(uenc->userData, p);
+ if (c & ~0xFFFF)
+ return 0;
+ return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF);
+}
+
+static int PTRFASTCALL
+unknown_isNmstrt(const ENCODING *enc, const char *p)
+{
+ const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+ int c = uenc->convert(uenc->userData, p);
+ if (c & ~0xFFFF)
+ return 0;
+ return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF);
+}
+
+static int PTRFASTCALL
+unknown_isInvalid(const ENCODING *enc, const char *p)
+{
+ const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+ int c = uenc->convert(uenc->userData, p);
+ return (c & ~0xFFFF) || checkCharRefNumber(c) < 0;
+}
+
+static void PTRCALL
+unknown_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+ char buf[XML_UTF8_ENCODE_MAX];
+ for (;;) {
+ const char *utf8;
+ int n;
+ if (*fromP == fromLim)
+ break;
+ utf8 = uenc->utf8[(unsigned char)**fromP];
+ n = *utf8++;
+ if (n == 0) {
+ int c = uenc->convert(uenc->userData, *fromP);
+ n = XmlUtf8Encode(c, buf);
+ if (n > toLim - *toP)
+ break;
+ utf8 = buf;
+ *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP]
+ - (BT_LEAD2 - 2));
+ }
+ else {
+ if (n > toLim - *toP)
+ break;
+ (*fromP)++;
+ }
+ do {
+ *(*toP)++ = *utf8++;
+ } while (--n != 0);
+ }
+}
+
+static void PTRCALL
+unknown_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+ while (*fromP != fromLim && *toP != toLim) {
+ unsigned short c = uenc->utf16[(unsigned char)**fromP];
+ if (c == 0) {
+ c = (unsigned short)
+ uenc->convert(uenc->userData, *fromP);
+ *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP]
+ - (BT_LEAD2 - 2));
+ }
+ else
+ (*fromP)++;
+ *(*toP)++ = c;
+ }
+}
+
+ENCODING *
+XmlInitUnknownEncoding(void *mem,
+ int *table,
+ CONVERTER convert,
+ void *userData)
+{
+ int i;
+ struct unknown_encoding *e = (struct unknown_encoding *)mem;
+ for (i = 0; i < (int)sizeof(struct normal_encoding); i++)
+ ((char *)mem)[i] = ((char *)&latin1_encoding)[i];
+ for (i = 0; i < 128; i++)
+ if (latin1_encoding.type[i] != BT_OTHER
+ && latin1_encoding.type[i] != BT_NONXML
+ && table[i] != i)
+ return 0;
+ for (i = 0; i < 256; i++) {
+ int c = table[i];
+ if (c == -1) {
+ e->normal.type[i] = BT_MALFORM;
+ /* This shouldn't really get used. */
+ e->utf16[i] = 0xFFFF;
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = 0;
+ }
+ else if (c < 0) {
+ if (c < -4)
+ return 0;
+ e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2));
+ e->utf8[i][0] = 0;
+ e->utf16[i] = 0;
+ }
+ else if (c < 0x80) {
+ if (latin1_encoding.type[c] != BT_OTHER
+ && latin1_encoding.type[c] != BT_NONXML
+ && c != i)
+ return 0;
+ e->normal.type[i] = latin1_encoding.type[c];
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = (char)c;
+ e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c);
+ }
+ else if (checkCharRefNumber(c) < 0) {
+ e->normal.type[i] = BT_NONXML;
+ /* This shouldn't really get used. */
+ e->utf16[i] = 0xFFFF;
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = 0;
+ }
+ else {
+ if (c > 0xFFFF)
+ return 0;
+ if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff))
+ e->normal.type[i] = BT_NMSTRT;
+ else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff))
+ e->normal.type[i] = BT_NAME;
+ else
+ e->normal.type[i] = BT_OTHER;
+ e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1);
+ e->utf16[i] = (unsigned short)c;
+ }
+ }
+ e->userData = userData;
+ e->convert = convert;
+ if (convert) {
+ e->normal.isName2 = unknown_isName;
+ e->normal.isName3 = unknown_isName;
+ e->normal.isName4 = unknown_isName;
+ e->normal.isNmstrt2 = unknown_isNmstrt;
+ e->normal.isNmstrt3 = unknown_isNmstrt;
+ e->normal.isNmstrt4 = unknown_isNmstrt;
+ e->normal.isInvalid2 = unknown_isInvalid;
+ e->normal.isInvalid3 = unknown_isInvalid;
+ e->normal.isInvalid4 = unknown_isInvalid;
+ }
+ e->normal.enc.utf8Convert = unknown_toUtf8;
+ e->normal.enc.utf16Convert = unknown_toUtf16;
+ return &(e->normal.enc);
+}
+
+/* If this enumeration is changed, getEncodingIndex and encodings
+must also be changed. */
+enum {
+ UNKNOWN_ENC = -1,
+ ISO_8859_1_ENC = 0,
+ US_ASCII_ENC,
+ UTF_8_ENC,
+ UTF_16_ENC,
+ UTF_16BE_ENC,
+ UTF_16LE_ENC,
+ /* must match encodingNames up to here */
+ NO_ENC
+};
+
+static const char KW_ISO_8859_1[] = {
+ ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9,
+ ASCII_MINUS, ASCII_1, '\0'
+};
+static const char KW_US_ASCII[] = {
+ ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I,
+ '\0'
+};
+static const char KW_UTF_8[] = {
+ ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0'
+};
+static const char KW_UTF_16[] = {
+ ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0'
+};
+static const char KW_UTF_16BE[] = {
+ ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E,
+ '\0'
+};
+static const char KW_UTF_16LE[] = {
+ ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E,
+ '\0'
+};
+
+static int FASTCALL
+getEncodingIndex(const char *name)
+{
+ static const char * const encodingNames[] = {
+ KW_ISO_8859_1,
+ KW_US_ASCII,
+ KW_UTF_8,
+ KW_UTF_16,
+ KW_UTF_16BE,
+ KW_UTF_16LE,
+ };
+ int i;
+ if (name == NULL)
+ return NO_ENC;
+ for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++)
+ if (streqci(name, encodingNames[i]))
+ return i;
+ return UNKNOWN_ENC;
+}
+
+/* For binary compatibility, we store the index of the encoding
+ specified at initialization in the isUtf16 member.
+*/
+
+#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16)
+#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i)
+
+/* This is what detects the encoding. encodingTable maps from
+ encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of
+ the external (protocol) specified encoding; state is
+ XML_CONTENT_STATE if we're parsing an external text entity, and
+ XML_PROLOG_STATE otherwise.
+*/
+
+
+static int
+initScan(const ENCODING * const *encodingTable,
+ const INIT_ENCODING *enc,
+ int state,
+ const char *ptr,
+ const char *end,
+ const char **nextTokPtr)
+{
+ const ENCODING **encPtr;
+
+ if (ptr == end)
+ return XML_TOK_NONE;
+ encPtr = enc->encPtr;
+ if (ptr + 1 == end) {
+ /* only a single byte available for auto-detection */
+#ifndef XML_DTD /* FIXME */
+ /* a well-formed document entity must have more than one byte */
+ if (state != XML_CONTENT_STATE)
+ return XML_TOK_PARTIAL;
+#endif
+ /* so we're parsing an external text entity... */
+ /* if UTF-16 was externally specified, then we need at least 2 bytes */
+ switch (INIT_ENC_INDEX(enc)) {
+ case UTF_16_ENC:
+ case UTF_16LE_ENC:
+ case UTF_16BE_ENC:
+ return XML_TOK_PARTIAL;
+ }
+ switch ((unsigned char)*ptr) {
+ case 0xFE:
+ case 0xFF:
+ case 0xEF: /* possibly first byte of UTF-8 BOM */
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ /* fall through */
+ case 0x00:
+ case 0x3C:
+ return XML_TOK_PARTIAL;
+ }
+ }
+ else {
+ switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) {
+ case 0xFEFF:
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ *nextTokPtr = ptr + 2;
+ *encPtr = encodingTable[UTF_16BE_ENC];
+ return XML_TOK_BOM;
+ /* 00 3C is handled in the default case */
+ case 0x3C00:
+ if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC
+ || INIT_ENC_INDEX(enc) == UTF_16_ENC)
+ && state == XML_CONTENT_STATE)
+ break;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ case 0xFFFE:
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ *nextTokPtr = ptr + 2;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XML_TOK_BOM;
+ case 0xEFBB:
+ /* Maybe a UTF-8 BOM (EF BB BF) */
+ /* If there's an explicitly specified (external) encoding
+ of ISO-8859-1 or some flavour of UTF-16
+ and this is an external text entity,
+ don't look for the BOM,
+ because it might be a legal data.
+ */
+ if (state == XML_CONTENT_STATE) {
+ int e = INIT_ENC_INDEX(enc);
+ if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC
+ || e == UTF_16LE_ENC || e == UTF_16_ENC)
+ break;
+ }
+ if (ptr + 2 == end)
+ return XML_TOK_PARTIAL;
+ if ((unsigned char)ptr[2] == 0xBF) {
+ *nextTokPtr = ptr + 3;
+ *encPtr = encodingTable[UTF_8_ENC];
+ return XML_TOK_BOM;
+ }
+ break;
+ default:
+ if (ptr[0] == '\0') {
+ /* 0 isn't a legal data character. Furthermore a document
+ entity can only start with ASCII characters. So the only
+ way this can fail to be big-endian UTF-16 if it it's an
+ external parsed general entity that's labelled as
+ UTF-16LE.
+ */
+ if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC)
+ break;
+ *encPtr = encodingTable[UTF_16BE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ }
+ else if (ptr[1] == '\0') {
+ /* We could recover here in the case:
+ - parsing an external entity
+ - second byte is 0
+ - no externally specified encoding
+ - no encoding declaration
+ by assuming UTF-16LE. But we don't, because this would mean when
+ presented just with a single byte, we couldn't reliably determine
+ whether we needed further bytes.
+ */
+ if (state == XML_CONTENT_STATE)
+ break;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ }
+ break;
+ }
+ }
+ *encPtr = encodingTable[INIT_ENC_INDEX(enc)];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+}
+
+
+#define NS(x) x
+#define ns(x) x
+#define XML_TOK_NS_C
+#include "xmltok_ns.c"
+#undef XML_TOK_NS_C
+#undef NS
+#undef ns
+
+#ifdef XML_NS
+
+#define NS(x) x ## NS
+#define ns(x) x ## _ns
+
+#define XML_TOK_NS_C
+#include "xmltok_ns.c"
+#undef XML_TOK_NS_C
+
+#undef NS
+#undef ns
+
+ENCODING *
+XmlInitUnknownEncodingNS(void *mem,
+ int *table,
+ CONVERTER convert,
+ void *userData)
+{
+ ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData);
+ if (enc)
+ ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON;
+ return enc;
+}
+
+#endif /* XML_NS */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok.h
new file mode 100644
index 0000000..ca867aa
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok.h
@@ -0,0 +1,316 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+#ifndef XmlTok_INCLUDED
+#define XmlTok_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following token may be returned by XmlContentTok */
+#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be
+ start of illegal ]]> sequence */
+/* The following tokens may be returned by both XmlPrologTok and
+ XmlContentTok.
+*/
+#define XML_TOK_NONE -4 /* The string to be scanned is empty */
+#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan;
+ might be part of CRLF sequence */
+#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
+#define XML_TOK_PARTIAL -1 /* only part of a token */
+#define XML_TOK_INVALID 0
+
+/* The following tokens are returned by XmlContentTok; some are also
+ returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok.
+*/
+#define XML_TOK_START_TAG_WITH_ATTS 1
+#define XML_TOK_START_TAG_NO_ATTS 2
+#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
+#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
+#define XML_TOK_END_TAG 5
+#define XML_TOK_DATA_CHARS 6
+#define XML_TOK_DATA_NEWLINE 7
+#define XML_TOK_CDATA_SECT_OPEN 8
+#define XML_TOK_ENTITY_REF 9
+#define XML_TOK_CHAR_REF 10 /* numeric character reference */
+
+/* The following tokens may be returned by both XmlPrologTok and
+ XmlContentTok.
+*/
+#define XML_TOK_PI 11 /* processing instruction */
+#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
+#define XML_TOK_COMMENT 13
+#define XML_TOK_BOM 14 /* Byte order mark */
+
+/* The following tokens are returned only by XmlPrologTok */
+#define XML_TOK_PROLOG_S 15
+#define XML_TOK_DECL_OPEN 16 /* <!foo */
+#define XML_TOK_DECL_CLOSE 17 /* > */
+#define XML_TOK_NAME 18
+#define XML_TOK_NMTOKEN 19
+#define XML_TOK_POUND_NAME 20 /* #name */
+#define XML_TOK_OR 21 /* | */
+#define XML_TOK_PERCENT 22
+#define XML_TOK_OPEN_PAREN 23
+#define XML_TOK_CLOSE_PAREN 24
+#define XML_TOK_OPEN_BRACKET 25
+#define XML_TOK_CLOSE_BRACKET 26
+#define XML_TOK_LITERAL 27
+#define XML_TOK_PARAM_ENTITY_REF 28
+#define XML_TOK_INSTANCE_START 29
+
+/* The following occur only in element type declarations */
+#define XML_TOK_NAME_QUESTION 30 /* name? */
+#define XML_TOK_NAME_ASTERISK 31 /* name* */
+#define XML_TOK_NAME_PLUS 32 /* name+ */
+#define XML_TOK_COND_SECT_OPEN 33 /* <![ */
+#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
+#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
+#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
+#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
+#define XML_TOK_COMMA 38
+
+/* The following token is returned only by XmlAttributeValueTok */
+#define XML_TOK_ATTRIBUTE_VALUE_S 39
+
+/* The following token is returned only by XmlCdataSectionTok */
+#define XML_TOK_CDATA_SECT_CLOSE 40
+
+/* With namespace processing this is returned by XmlPrologTok for a
+ name with a colon.
+*/
+#define XML_TOK_PREFIXED_NAME 41
+
+#ifdef XML_DTD
+#define XML_TOK_IGNORE_SECT 42
+#endif /* XML_DTD */
+
+#ifdef XML_DTD
+#define XML_N_STATES 4
+#else /* not XML_DTD */
+#define XML_N_STATES 3
+#endif /* not XML_DTD */
+
+#define XML_PROLOG_STATE 0
+#define XML_CONTENT_STATE 1
+#define XML_CDATA_SECTION_STATE 2
+#ifdef XML_DTD
+#define XML_IGNORE_SECTION_STATE 3
+#endif /* XML_DTD */
+
+#define XML_N_LITERAL_TYPES 2
+#define XML_ATTRIBUTE_VALUE_LITERAL 0
+#define XML_ENTITY_VALUE_LITERAL 1
+
+/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
+#define XML_UTF8_ENCODE_MAX 4
+/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
+#define XML_UTF16_ENCODE_MAX 2
+
+typedef struct position {
+ /* first line and first column are 0 not 1 */
+ XML_Size lineNumber;
+ XML_Size columnNumber;
+} POSITION;
+
+typedef struct {
+ const char *name;
+ const char *valuePtr;
+ const char *valueEnd;
+ char normalized;
+} ATTRIBUTE;
+
+struct encoding;
+typedef struct encoding ENCODING;
+
+typedef int (PTRCALL *SCANNER)(const ENCODING *,
+ const char *,
+ const char *,
+ const char **);
+
+struct encoding {
+ SCANNER scanners[XML_N_STATES];
+ SCANNER literalScanners[XML_N_LITERAL_TYPES];
+ int (PTRCALL *sameName)(const ENCODING *,
+ const char *,
+ const char *);
+ int (PTRCALL *nameMatchesAscii)(const ENCODING *,
+ const char *,
+ const char *,
+ const char *);
+ int (PTRFASTCALL *nameLength)(const ENCODING *, const char *);
+ const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *);
+ int (PTRCALL *getAtts)(const ENCODING *enc,
+ const char *ptr,
+ int attsMax,
+ ATTRIBUTE *atts);
+ int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr);
+ int (PTRCALL *predefinedEntityName)(const ENCODING *,
+ const char *,
+ const char *);
+ void (PTRCALL *updatePosition)(const ENCODING *,
+ const char *ptr,
+ const char *end,
+ POSITION *);
+ int (PTRCALL *isPublicId)(const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr);
+ void (PTRCALL *utf8Convert)(const ENCODING *enc,
+ const char **fromP,
+ const char *fromLim,
+ char **toP,
+ const char *toLim);
+ void (PTRCALL *utf16Convert)(const ENCODING *enc,
+ const char **fromP,
+ const char *fromLim,
+ unsigned short **toP,
+ const unsigned short *toLim);
+ int minBytesPerChar;
+ char isUtf8;
+ char isUtf16;
+};
+
+/* Scan the string starting at ptr until the end of the next complete
+ token, but do not scan past eptr. Return an integer giving the
+ type of token.
+
+ Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
+
+ Return XML_TOK_PARTIAL when the string does not contain a complete
+ token; nextTokPtr will not be set.
+
+ Return XML_TOK_INVALID when the string does not start a valid
+ token; nextTokPtr will be set to point to the character which made
+ the token invalid.
+
+ Otherwise the string starts with a valid token; nextTokPtr will be
+ set to point to the character following the end of that token.
+
+ Each data character counts as a single token, but adjacent data
+ characters may be returned together. Similarly for characters in
+ the prolog outside literals, comments and processing instructions.
+*/
+
+
+#define XmlTok(enc, state, ptr, end, nextTokPtr) \
+ (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
+
+#define XmlPrologTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
+
+#define XmlContentTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
+
+#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
+
+#ifdef XML_DTD
+
+#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
+
+#endif /* XML_DTD */
+
+/* This is used for performing a 2nd-level tokenization on the content
+ of a literal that has already been returned by XmlTok.
+*/
+#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
+ (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
+
+#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
+ XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
+ XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2))
+
+#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \
+ (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
+
+#define XmlNameLength(enc, ptr) \
+ (((enc)->nameLength)(enc, ptr))
+
+#define XmlSkipS(enc, ptr) \
+ (((enc)->skipS)(enc, ptr))
+
+#define XmlGetAttributes(enc, ptr, attsMax, atts) \
+ (((enc)->getAtts)(enc, ptr, attsMax, atts))
+
+#define XmlCharRefNumber(enc, ptr) \
+ (((enc)->charRefNumber)(enc, ptr))
+
+#define XmlPredefinedEntityName(enc, ptr, end) \
+ (((enc)->predefinedEntityName)(enc, ptr, end))
+
+#define XmlUpdatePosition(enc, ptr, end, pos) \
+ (((enc)->updatePosition)(enc, ptr, end, pos))
+
+#define XmlIsPublicId(enc, ptr, end, badPtr) \
+ (((enc)->isPublicId)(enc, ptr, end, badPtr))
+
+#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
+ (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
+
+#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
+ (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
+
+typedef struct {
+ ENCODING initEnc;
+ const ENCODING **encPtr;
+} INIT_ENCODING;
+
+int XmlParseXmlDecl(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **versionEndPtr,
+ const char **encodingNamePtr,
+ const ENCODING **namedEncodingPtr,
+ int *standalonePtr);
+
+int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING *XmlGetUtf8InternalEncoding(void);
+const ENCODING *XmlGetUtf16InternalEncoding(void);
+int FASTCALL XmlUtf8Encode(int charNumber, char *buf);
+int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf);
+int XmlSizeOfUnknownEncoding(void);
+
+
+typedef int (XMLCALL *CONVERTER) (void *userData, const char *p);
+
+ENCODING *
+XmlInitUnknownEncoding(void *mem,
+ int *table,
+ CONVERTER convert,
+ void *userData);
+
+int XmlParseXmlDeclNS(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **versionEndPtr,
+ const char **encodingNamePtr,
+ const ENCODING **namedEncodingPtr,
+ int *standalonePtr);
+
+int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING *XmlGetUtf8InternalEncodingNS(void);
+const ENCODING *XmlGetUtf16InternalEncodingNS(void);
+ENCODING *
+XmlInitUnknownEncodingNS(void *mem,
+ int *table,
+ CONVERTER convert,
+ void *userData);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlTok_INCLUDED */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_impl.c b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_impl.c
new file mode 100644
index 0000000..8154c1a
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_impl.c
@@ -0,0 +1,1786 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+/* This file is included! */
+#ifdef XML_TOK_IMPL_C
+
+#ifndef IS_INVALID_CHAR
+#define IS_INVALID_CHAR(enc, ptr, n) (0)
+#endif
+
+#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (IS_INVALID_CHAR(enc, ptr, n)) { \
+ *(nextTokPtr) = (ptr); \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define INVALID_CASES(ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(2, ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(3, ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(4, ptr, nextTokPtr) \
+ case BT_NONXML: \
+ case BT_MALFORM: \
+ case BT_TRAIL: \
+ *(nextTokPtr) = (ptr); \
+ return XML_TOK_INVALID;
+
+#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (!IS_NAME_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \
+ case BT_NONASCII: \
+ if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ case BT_NMSTRT: \
+ case BT_HEX: \
+ case BT_DIGIT: \
+ case BT_NAME: \
+ case BT_MINUS: \
+ ptr += MINBPC(enc); \
+ break; \
+ CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \
+ CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \
+ CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr)
+
+#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \
+ case BT_NONASCII: \
+ if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ case BT_NMSTRT: \
+ case BT_HEX: \
+ ptr += MINBPC(enc); \
+ break; \
+ CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \
+ CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \
+ CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr)
+
+#ifndef PREFIX
+#define PREFIX(ident) ident
+#endif
+
+/* ptr points to character following "<!-" */
+
+static int PTRCALL
+PREFIX(scanComment)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ if (ptr != end) {
+ if (!CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_MINUS:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COMMENT;
+ }
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<!" */
+
+static int PTRCALL
+PREFIX(scanDecl)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_MINUS:
+ return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LSQB:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COND_SECT_OPEN;
+ case BT_NMSTRT:
+ case BT_HEX:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_PERCNT:
+ if (ptr + MINBPC(enc) == end)
+ return XML_TOK_PARTIAL;
+ /* don't allow <!ENTITY% foo "whatever"> */
+ switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) {
+ case BT_S: case BT_CR: case BT_LF: case BT_PERCNT:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ /* fall through */
+ case BT_S: case BT_CR: case BT_LF:
+ *nextTokPtr = ptr;
+ return XML_TOK_DECL_OPEN;
+ case BT_NMSTRT:
+ case BT_HEX:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr,
+ const char *end, int *tokPtr)
+{
+ int upper = 0;
+ *tokPtr = XML_TOK_PI;
+ if (end - ptr != MINBPC(enc)*3)
+ return 1;
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_x:
+ break;
+ case ASCII_X:
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ ptr += MINBPC(enc);
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_m:
+ break;
+ case ASCII_M:
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ ptr += MINBPC(enc);
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_l:
+ break;
+ case ASCII_L:
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ if (upper)
+ return 0;
+ *tokPtr = XML_TOK_XML_DECL;
+ return 1;
+}
+
+/* ptr points to character following "<?" */
+
+static int PTRCALL
+PREFIX(scanPi)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ int tok;
+ const char *target = ptr;
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_CR: case BT_LF:
+ if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_QUEST:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return tok;
+ }
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ return XML_TOK_PARTIAL;
+ case BT_QUEST:
+ if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return tok;
+ }
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(scanCdataSection)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ static const char CDATA_LSQB[] = { ASCII_C, ASCII_D, ASCII_A,
+ ASCII_T, ASCII_A, ASCII_LSQB };
+ int i;
+ /* CDATA[ */
+ if (end - ptr < 6 * MINBPC(enc))
+ return XML_TOK_PARTIAL;
+ for (i = 0; i < 6; i++, ptr += MINBPC(enc)) {
+ if (!CHAR_MATCHES(enc, ptr, CDATA_LSQB[i])) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_CDATA_SECT_OPEN;
+}
+
+static int PTRCALL
+PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB))
+ break;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ ptr -= MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CDATA_SECT_CLOSE;
+ case BT_CR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ case BT_LF:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ INVALID_CASES(ptr, nextTokPtr)
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_DATA_CHARS; \
+ } \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONXML:
+ case BT_MALFORM:
+ case BT_TRAIL:
+ case BT_CR:
+ case BT_LF:
+ case BT_RSQB:
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "</" */
+
+static int PTRCALL
+PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_CR: case BT_LF:
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_CR: case BT_LF:
+ break;
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_END_TAG;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+#ifdef XML_NS
+ case BT_COLON:
+ /* no need to check qname syntax here,
+ since end-tag must match exactly */
+ ptr += MINBPC(enc);
+ break;
+#endif
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_END_TAG;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#X" */
+
+static int PTRCALL
+PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ if (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ break;
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CHAR_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#" */
+
+static int PTRCALL
+PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ if (ptr != end) {
+ if (CHAR_MATCHES(enc, ptr, ASCII_x))
+ return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ break;
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CHAR_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&" */
+
+static int PTRCALL
+PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_NUM:
+ return PREFIX(scanCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_ENTITY_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following first character of attribute name */
+
+static int PTRCALL
+PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+#ifdef XML_NS
+ int hadColon = 0;
+#endif
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+ case BT_COLON:
+ if (hadColon) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ hadColon = 1;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+#endif
+ case BT_S: case BT_CR: case BT_LF:
+ for (;;) {
+ int t;
+
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ t = BYTE_TYPE(enc, ptr);
+ if (t == BT_EQUALS)
+ break;
+ switch (t) {
+ case BT_S:
+ case BT_LF:
+ case BT_CR:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ /* fall through */
+ case BT_EQUALS:
+ {
+ int open;
+#ifdef XML_NS
+ hadColon = 0;
+#endif
+ for (;;) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ open = BYTE_TYPE(enc, ptr);
+ if (open == BT_QUOT || open == BT_APOS)
+ break;
+ switch (open) {
+ case BT_S:
+ case BT_LF:
+ case BT_CR:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ ptr += MINBPC(enc);
+ /* in attribute value */
+ for (;;) {
+ int t;
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ t = BYTE_TYPE(enc, ptr);
+ if (t == open)
+ break;
+ switch (t) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_AMP:
+ {
+ int tok = PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, &ptr);
+ if (tok <= 0) {
+ if (tok == XML_TOK_INVALID)
+ *nextTokPtr = ptr;
+ return tok;
+ }
+ break;
+ }
+ case BT_LT:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S:
+ case BT_CR:
+ case BT_LF:
+ break;
+ case BT_SOL:
+ goto sol;
+ case BT_GT:
+ goto gt;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ /* ptr points to closing quote */
+ for (;;) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_CR: case BT_LF:
+ continue;
+ case BT_GT:
+ gt:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_START_TAG_WITH_ATTS;
+ case BT_SOL:
+ sol:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_EMPTY_ELEMENT_WITH_ATTS;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+ }
+ break;
+ }
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<" */
+
+static int PTRCALL
+PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+#ifdef XML_NS
+ int hadColon;
+#endif
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_EXCL:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_MINUS:
+ return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LSQB:
+ return PREFIX(scanCdataSection)(enc, ptr + MINBPC(enc),
+ end, nextTokPtr);
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_QUEST:
+ return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_SOL:
+ return PREFIX(scanEndTag)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+#ifdef XML_NS
+ hadColon = 0;
+#endif
+ /* we have a start-tag */
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+ case BT_COLON:
+ if (hadColon) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ hadColon = 1;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+#endif
+ case BT_S: case BT_CR: case BT_LF:
+ {
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_GT:
+ goto gt;
+ case BT_SOL:
+ goto sol;
+ case BT_S: case BT_CR: case BT_LF:
+ ptr += MINBPC(enc);
+ continue;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr);
+ }
+ return XML_TOK_PARTIAL;
+ }
+ case BT_GT:
+ gt:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_START_TAG_NO_ATTS;
+ case BT_SOL:
+ sol:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_EMPTY_ELEMENT_NO_ATTS;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_LT:
+ return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_AMP:
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_CR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ case BT_LF:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_RSQB;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB))
+ break;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_RSQB;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ ptr -= MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ INVALID_CASES(ptr, nextTokPtr)
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_DATA_CHARS; \
+ } \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_RSQB:
+ if (ptr + MINBPC(enc) != end) {
+ if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) {
+ ptr += MINBPC(enc);
+ break;
+ }
+ if (ptr + 2*MINBPC(enc) != end) {
+ if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) {
+ ptr += MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr + 2*MINBPC(enc);
+ return XML_TOK_INVALID;
+ }
+ }
+ /* fall through */
+ case BT_AMP:
+ case BT_LT:
+ case BT_NONXML:
+ case BT_MALFORM:
+ case BT_TRAIL:
+ case BT_CR:
+ case BT_LF:
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "%" */
+
+static int PTRCALL
+PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return -XML_TOK_PERCENT;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_LF: case BT_CR: case BT_PERCNT:
+ *nextTokPtr = ptr;
+ return XML_TOK_PERCENT;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_PARAM_ENTITY_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_CR: case BT_LF: case BT_S:
+ case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR:
+ *nextTokPtr = ptr;
+ return XML_TOK_POUND_NAME;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return -XML_TOK_POUND_NAME;
+}
+
+static int PTRCALL
+PREFIX(scanLit)(int open, const ENCODING *enc,
+ const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ while (ptr != end) {
+ int t = BYTE_TYPE(enc, ptr);
+ switch (t) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_QUOT:
+ case BT_APOS:
+ ptr += MINBPC(enc);
+ if (t != open)
+ break;
+ if (ptr == end)
+ return -XML_TOK_LITERAL;
+ *nextTokPtr = ptr;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_CR: case BT_LF:
+ case BT_GT: case BT_PERCNT: case BT_LSQB:
+ return XML_TOK_LITERAL;
+ default:
+ return XML_TOK_INVALID;
+ }
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ int tok;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_QUOT:
+ return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_APOS:
+ return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LT:
+ {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_EXCL:
+ return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_QUEST:
+ return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_NMSTRT:
+ case BT_HEX:
+ case BT_NONASCII:
+ case BT_LEAD2:
+ case BT_LEAD3:
+ case BT_LEAD4:
+ *nextTokPtr = ptr - MINBPC(enc);
+ return XML_TOK_INSTANCE_START;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ case BT_CR:
+ if (ptr + MINBPC(enc) == end) {
+ *nextTokPtr = end;
+ /* indicate that this might be part of a CR/LF pair */
+ return -XML_TOK_PROLOG_S;
+ }
+ /* fall through */
+ case BT_S: case BT_LF:
+ for (;;) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ break;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_LF:
+ break;
+ case BT_CR:
+ /* don't split CR/LF pair */
+ if (ptr + MINBPC(enc) != end)
+ break;
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_PROLOG_S;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_PROLOG_S;
+ case BT_PERCNT:
+ return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_COMMA:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COMMA;
+ case BT_LSQB:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OPEN_BRACKET;
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return -XML_TOK_CLOSE_BRACKET;
+ if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
+ if (ptr + MINBPC(enc) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) {
+ *nextTokPtr = ptr + 2*MINBPC(enc);
+ return XML_TOK_COND_SECT_CLOSE;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_CLOSE_BRACKET;
+ case BT_LPAR:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OPEN_PAREN;
+ case BT_RPAR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return -XML_TOK_CLOSE_PAREN;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_AST:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_ASTERISK;
+ case BT_QUEST:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_QUESTION;
+ case BT_PLUS:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_PLUS;
+ case BT_CR: case BT_LF: case BT_S:
+ case BT_GT: case BT_COMMA: case BT_VERBAR:
+ case BT_RPAR:
+ *nextTokPtr = ptr;
+ return XML_TOK_CLOSE_PAREN;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_VERBAR:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OR;
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DECL_CLOSE;
+ case BT_NUM:
+ return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ ptr += n; \
+ tok = XML_TOK_NAME; \
+ break; \
+ } \
+ if (IS_NAME_CHAR(enc, ptr, n)) { \
+ ptr += n; \
+ tok = XML_TOK_NMTOKEN; \
+ break; \
+ } \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NMSTRT:
+ case BT_HEX:
+ tok = XML_TOK_NAME;
+ ptr += MINBPC(enc);
+ break;
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ tok = XML_TOK_NMTOKEN;
+ ptr += MINBPC(enc);
+ break;
+ case BT_NONASCII:
+ if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) {
+ ptr += MINBPC(enc);
+ tok = XML_TOK_NAME;
+ break;
+ }
+ if (IS_NAME_CHAR_MINBPC(enc, ptr)) {
+ ptr += MINBPC(enc);
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_GT: case BT_RPAR: case BT_COMMA:
+ case BT_VERBAR: case BT_LSQB: case BT_PERCNT:
+ case BT_S: case BT_CR: case BT_LF:
+ *nextTokPtr = ptr;
+ return tok;
+#ifdef XML_NS
+ case BT_COLON:
+ ptr += MINBPC(enc);
+ switch (tok) {
+ case XML_TOK_NAME:
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ tok = XML_TOK_PREFIXED_NAME;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ break;
+ case XML_TOK_PREFIXED_NAME:
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ break;
+#endif
+ case BT_PLUS:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_PLUS;
+ case BT_AST:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_ASTERISK;
+ case BT_QUEST:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_QUESTION;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return -tok;
+}
+
+static int PTRCALL
+PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ const char *start;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ start = ptr;
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_AMP:
+ if (ptr == start)
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_LT:
+ /* this is for inside entity references */
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_LF:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_CR:
+ if (ptr == start) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_S:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_ATTRIBUTE_VALUE_S;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+static int PTRCALL
+PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ const char *start;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ start = ptr;
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_AMP:
+ if (ptr == start)
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_PERCNT:
+ if (ptr == start) {
+ int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc),
+ end, nextTokPtr);
+ return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_LF:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_CR:
+ if (ptr == start) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+#ifdef XML_DTD
+
+static int PTRCALL
+PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr,
+ const char *end, const char **nextTokPtr)
+{
+ int level = 0;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ end = ptr + n;
+ }
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_LT:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) {
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) {
+ ++level;
+ ptr += MINBPC(enc);
+ }
+ }
+ break;
+ case BT_RSQB:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ ptr += MINBPC(enc);
+ if (level == 0) {
+ *nextTokPtr = ptr;
+ return XML_TOK_IGNORE_SECT;
+ }
+ --level;
+ }
+ }
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+#endif /* XML_DTD */
+
+static int PTRCALL
+PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **badPtr)
+{
+ ptr += MINBPC(enc);
+ end -= MINBPC(enc);
+ for (; ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ case BT_MINUS:
+ case BT_APOS:
+ case BT_LPAR:
+ case BT_RPAR:
+ case BT_PLUS:
+ case BT_COMMA:
+ case BT_SOL:
+ case BT_EQUALS:
+ case BT_QUEST:
+ case BT_CR:
+ case BT_LF:
+ case BT_SEMI:
+ case BT_EXCL:
+ case BT_AST:
+ case BT_PERCNT:
+ case BT_NUM:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ break;
+ case BT_S:
+ if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) {
+ *badPtr = ptr;
+ return 0;
+ }
+ break;
+ case BT_NAME:
+ case BT_NMSTRT:
+ if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f))
+ break;
+ default:
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case 0x24: /* $ */
+ case 0x40: /* @ */
+ break;
+ default:
+ *badPtr = ptr;
+ return 0;
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+/* This must only be called for a well-formed start-tag or empty
+ element tag. Returns the number of attributes. Pointers to the
+ first attsMax attributes are stored in atts.
+*/
+
+static int PTRCALL
+PREFIX(getAtts)(const ENCODING *enc, const char *ptr,
+ int attsMax, ATTRIBUTE *atts)
+{
+ enum { other, inName, inValue } state = inName;
+ int nAtts = 0;
+ int open = 0; /* defined when state == inValue;
+ initialization just to shut up compilers */
+
+ for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define START_NAME \
+ if (state == other) { \
+ if (nAtts < attsMax) { \
+ atts[nAtts].name = ptr; \
+ atts[nAtts].normalized = 1; \
+ } \
+ state = inName; \
+ }
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONASCII:
+ case BT_NMSTRT:
+ case BT_HEX:
+ START_NAME
+ break;
+#undef START_NAME
+ case BT_QUOT:
+ if (state != inValue) {
+ if (nAtts < attsMax)
+ atts[nAtts].valuePtr = ptr + MINBPC(enc);
+ state = inValue;
+ open = BT_QUOT;
+ }
+ else if (open == BT_QUOT) {
+ state = other;
+ if (nAtts < attsMax)
+ atts[nAtts].valueEnd = ptr;
+ nAtts++;
+ }
+ break;
+ case BT_APOS:
+ if (state != inValue) {
+ if (nAtts < attsMax)
+ atts[nAtts].valuePtr = ptr + MINBPC(enc);
+ state = inValue;
+ open = BT_APOS;
+ }
+ else if (open == BT_APOS) {
+ state = other;
+ if (nAtts < attsMax)
+ atts[nAtts].valueEnd = ptr;
+ nAtts++;
+ }
+ break;
+ case BT_AMP:
+ if (nAtts < attsMax)
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_S:
+ if (state == inName)
+ state = other;
+ else if (state == inValue
+ && nAtts < attsMax
+ && atts[nAtts].normalized
+ && (ptr == atts[nAtts].valuePtr
+ || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE
+ || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE
+ || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open))
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_CR: case BT_LF:
+ /* This case ensures that the first attribute name is counted
+ Apart from that we could just change state on the quote. */
+ if (state == inName)
+ state = other;
+ else if (state == inValue && nAtts < attsMax)
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_GT:
+ case BT_SOL:
+ if (state != inValue)
+ return nAtts;
+ break;
+ default:
+ break;
+ }
+ }
+ /* not reached */
+}
+
+static int PTRFASTCALL
+PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr)
+{
+ int result = 0;
+ /* skip &# */
+ ptr += 2*MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_x)) {
+ for (ptr += MINBPC(enc);
+ !CHAR_MATCHES(enc, ptr, ASCII_SEMI);
+ ptr += MINBPC(enc)) {
+ int c = BYTE_TO_ASCII(enc, ptr);
+ switch (c) {
+ case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4:
+ case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9:
+ result <<= 4;
+ result |= (c - ASCII_0);
+ break;
+ case ASCII_A: case ASCII_B: case ASCII_C:
+ case ASCII_D: case ASCII_E: case ASCII_F:
+ result <<= 4;
+ result += 10 + (c - ASCII_A);
+ break;
+ case ASCII_a: case ASCII_b: case ASCII_c:
+ case ASCII_d: case ASCII_e: case ASCII_f:
+ result <<= 4;
+ result += 10 + (c - ASCII_a);
+ break;
+ }
+ if (result >= 0x110000)
+ return -1;
+ }
+ }
+ else {
+ for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) {
+ int c = BYTE_TO_ASCII(enc, ptr);
+ result *= 10;
+ result += (c - ASCII_0);
+ if (result >= 0x110000)
+ return -1;
+ }
+ }
+ return checkCharRefNumber(result);
+}
+
+static int PTRCALL
+PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr,
+ const char *end)
+{
+ switch ((end - ptr)/MINBPC(enc)) {
+ case 2:
+ if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) {
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_l:
+ return ASCII_LT;
+ case ASCII_g:
+ return ASCII_GT;
+ }
+ }
+ break;
+ case 3:
+ if (CHAR_MATCHES(enc, ptr, ASCII_a)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_m)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_p))
+ return ASCII_AMP;
+ }
+ }
+ break;
+ case 4:
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_q:
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_u)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_o)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_t))
+ return ASCII_QUOT;
+ }
+ }
+ break;
+ case ASCII_a:
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_p)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_o)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_s))
+ return ASCII_APOS;
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int PTRCALL
+PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2)
+{
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr1)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (*ptr1++ != *ptr2++) \
+ return 0;
+ LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2)
+#undef LEAD_CASE
+ /* fall through */
+ if (*ptr1++ != *ptr2++)
+ return 0;
+ break;
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 1) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 2) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 3) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ }
+ }
+ }
+ break;
+ default:
+ if (MINBPC(enc) == 1 && *ptr1 == *ptr2)
+ return 1;
+ switch (BYTE_TYPE(enc, ptr2)) {
+ case BT_LEAD2:
+ case BT_LEAD3:
+ case BT_LEAD4:
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ return 0;
+ default:
+ return 1;
+ }
+ }
+ }
+ /* not reached */
+}
+
+static int PTRCALL
+PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1,
+ const char *end1, const char *ptr2)
+{
+ for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
+ if (ptr1 == end1)
+ return 0;
+ if (!CHAR_MATCHES(enc, ptr1, *ptr2))
+ return 0;
+ }
+ return ptr1 == end1;
+}
+
+static int PTRFASTCALL
+PREFIX(nameLength)(const ENCODING *enc, const char *ptr)
+{
+ const char *start = ptr;
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ return (int)(ptr - start);
+ }
+ }
+}
+
+static const char * PTRFASTCALL
+PREFIX(skipS)(const ENCODING *enc, const char *ptr)
+{
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_LF:
+ case BT_CR:
+ case BT_S:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ return ptr;
+ }
+ }
+}
+
+static void PTRCALL
+PREFIX(updatePosition)(const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ POSITION *pos)
+{
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) { \
+ return; \
+ } \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_LF:
+ pos->columnNumber = (XML_Size)-1;
+ pos->lineNumber++;
+ ptr += MINBPC(enc);
+ break;
+ case BT_CR:
+ pos->lineNumber++;
+ ptr += MINBPC(enc);
+ if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ pos->columnNumber = (XML_Size)-1;
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ pos->columnNumber++;
+ }
+}
+
+#undef DO_LEAD_CASE
+#undef MULTIBYTE_CASES
+#undef INVALID_CASES
+#undef CHECK_NAME_CASE
+#undef CHECK_NAME_CASES
+#undef CHECK_NMSTRT_CASE
+#undef CHECK_NMSTRT_CASES
+
+#endif /* XML_TOK_IMPL_C */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_impl.h b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_impl.h
new file mode 100644
index 0000000..da0ea60
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_impl.h
@@ -0,0 +1,46 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file COPYING for copying permission.
+*/
+
+enum {
+ BT_NONXML,
+ BT_MALFORM,
+ BT_LT,
+ BT_AMP,
+ BT_RSQB,
+ BT_LEAD2,
+ BT_LEAD3,
+ BT_LEAD4,
+ BT_TRAIL,
+ BT_CR,
+ BT_LF,
+ BT_GT,
+ BT_QUOT,
+ BT_APOS,
+ BT_EQUALS,
+ BT_QUEST,
+ BT_EXCL,
+ BT_SOL,
+ BT_SEMI,
+ BT_NUM,
+ BT_LSQB,
+ BT_S,
+ BT_NMSTRT,
+ BT_COLON,
+ BT_HEX,
+ BT_DIGIT,
+ BT_NAME,
+ BT_MINUS,
+ BT_OTHER, /* known not to be a name or name start character */
+ BT_NONASCII, /* might be a name or name start character */
+ BT_PERCNT,
+ BT_LPAR,
+ BT_RPAR,
+ BT_AST,
+ BT_PLUS,
+ BT_COMMA,
+ BT_VERBAR
+};
+
+#include <stddef.h>
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_ns.c b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_ns.c
new file mode 100644
index 0000000..c3b88fd
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/Source/lib/xmltok_ns.c
@@ -0,0 +1,115 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+ See the file COPYING for copying permission.
+*/
+
+/* This file is included! */
+#ifdef XML_TOK_NS_C
+
+const ENCODING *
+NS(XmlGetUtf8InternalEncoding)(void)
+{
+ return &ns(internal_utf8_encoding).enc;
+}
+
+const ENCODING *
+NS(XmlGetUtf16InternalEncoding)(void)
+{
+#if BYTEORDER == 1234
+ return &ns(internal_little2_encoding).enc;
+#elif BYTEORDER == 4321
+ return &ns(internal_big2_encoding).enc;
+#else
+ const short n = 1;
+ return (*(const char *)&n
+ ? &ns(internal_little2_encoding).enc
+ : &ns(internal_big2_encoding).enc);
+#endif
+}
+
+static const ENCODING * const NS(encodings)[] = {
+ &ns(latin1_encoding).enc,
+ &ns(ascii_encoding).enc,
+ &ns(utf8_encoding).enc,
+ &ns(big2_encoding).enc,
+ &ns(big2_encoding).enc,
+ &ns(little2_encoding).enc,
+ &ns(utf8_encoding).enc /* NO_ENC */
+};
+
+static int PTRCALL
+NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ return initScan(NS(encodings), (const INIT_ENCODING *)enc,
+ XML_PROLOG_STATE, ptr, end, nextTokPtr);
+}
+
+static int PTRCALL
+NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ return initScan(NS(encodings), (const INIT_ENCODING *)enc,
+ XML_CONTENT_STATE, ptr, end, nextTokPtr);
+}
+
+int
+NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr,
+ const char *name)
+{
+ int i = getEncodingIndex(name);
+ if (i == UNKNOWN_ENC)
+ return 0;
+ SET_INIT_ENC_INDEX(p, i);
+ p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
+ p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
+ p->initEnc.updatePosition = initUpdatePosition;
+ p->encPtr = encPtr;
+ *encPtr = &(p->initEnc);
+ return 1;
+}
+
+static const ENCODING *
+NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end)
+{
+#define ENCODING_MAX 128
+ char buf[ENCODING_MAX];
+ char *p = buf;
+ int i;
+ XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
+ if (ptr != end)
+ return 0;
+ *p = 0;
+ if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2)
+ return enc;
+ i = getEncodingIndex(buf);
+ if (i == UNKNOWN_ENC)
+ return 0;
+ return NS(encodings)[i];
+}
+
+int
+NS(XmlParseXmlDecl)(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **versionEndPtr,
+ const char **encodingName,
+ const ENCODING **encoding,
+ int *standalone)
+{
+ return doParseXmlDecl(NS(findEncoding),
+ isGeneralTextEntity,
+ enc,
+ ptr,
+ end,
+ badPtr,
+ versionPtr,
+ versionEndPtr,
+ encodingName,
+ encoding,
+ standalone);
+}
+
+#endif /* XML_TOK_NS_C */
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/expat.scons b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/expat.scons
new file mode 100644
index 0000000..620ba32
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/expat.scons
@@ -0,0 +1,24 @@
+# SConscript - expat
+# Copyright 2009 Google Inc. All Rights Reserved.
+# Author: vitalybuka@google.com (Vitaly Buka)
+
+Import('env')
+
+expat_env = env.Clone()
+expat_env.Append(
+ CPPDEFINES = [
+ "_LIB",
+ "XML_LARGE_SIZE",
+ "XML_STATIC",
+ "COMPILED_FROM_DSP",
+ ],
+)
+
+expat_env.ComponentLibrary(
+ 'expat.lib',
+ source = [
+ 'Source/lib/xmlparse.c',
+ 'Source/lib/xmlrole.c',
+ 'Source/lib/xmltok.c',
+ ],
+)
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/mk_file b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/mk_file
new file mode 100644
index 0000000..b09e3ce
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/mk_file
@@ -0,0 +1,16 @@
+t = NewTarget()
+t.OUTPUTS = ["expat.lib"]
+t.INPUTS = ["./Source/lib/xmlparse.c",
+ "./Source/lib/xmlrole.c",
+ "./Source/lib/xmltok.c",
+ ]
+t.INC_DIRS += ["."]
+t.C_DEFINES += ["_LIB",
+ "XML_STATIC",
+ "COMPILED_FROM_DSP",
+ ]
+
+# No precompiled headers.
+t.PRECOMPILE_STOPFILE = ""
+t.PRECOMPILE_BUILDER = ""
+DoBuild(t)
diff --git a/third_party/libjingle/source/talk/third_party/expat/v2_0_1/setup_env.bat b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/setup_env.bat
new file mode 100755
index 0000000..3fc0798
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/expat/v2_0_1/setup_env.bat
@@ -0,0 +1,8 @@
+:: This script must not rely on any external tools or PATH values.
+@echo OFF
+
+:: Let advanced users checkout the tools in just one P4 enlistment.
+if "%SETUP_ENV_EXPAT%"=="done" goto :EOF
+set SETUP_ENV_EXPAT=done
+
+set INCLUDE=%INCLUDE%;%~dp0Source\lib
diff --git a/third_party/libjingle/source/talk/third_party/gipslite/Interface/GipsVoiceEngineLite.h b/third_party/libjingle/source/talk/third_party/gipslite/Interface/GipsVoiceEngineLite.h
new file mode 100644
index 0000000..29b6a7a
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/gipslite/Interface/GipsVoiceEngineLite.h
@@ -0,0 +1,132 @@
+// GipsVoiceEngineLib.h
+//
+//////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////
+//
+// Created by: Fredrik Galschiödt
+// Date : 011202
+//
+// $Change$
+//
+// Public API for GIPS Voice Engine on a PC platform.
+//
+// Copyright (c) 2001
+// Global IP Sound AB, Organization number: 5565739017
+// Rosenlundsgatan 54, SE-118 63 Stockholm, Sweden
+// All rights reserved.
+//
+//////////////////////////////////////////////////////////////////////
+
+#ifndef PUBLIC_GIPS_VOICE_ENGINE_LITE_H
+#define PUBLIC_GIPS_VOICE_ENGINE_LITE_H
+
+#include "GIPS_common_types.h"
+
+#ifdef GIPS_EXPORT
+#define VOICEENGINE_DLLEXPORT _declspec(dllexport)
+#elif GIPS_DLL
+#define VOICEENGINE_DLLEXPORT _declspec(dllimport)
+#else
+#define VOICEENGINE_DLLEXPORT
+#endif
+
+
+//////////////////////////////////////////////////////////////////////
+// GipsVoiceEngineLib
+//
+// Public interface to the GIPS Voice Engine for PC platforms
+//////////////////////////////////////////////////////////////////////
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+class VOICEENGINE_DLLEXPORT GipsVoiceEngineLite
+{
+public:
+ virtual int GIPSVE_Init(bool recordAEC =false, bool multiCore = false,int month = 0,int day = 0,int year = 0 ) = 0;
+ virtual int GIPSVE_SetNetworkStatus(int networktype) = 0;
+ virtual int GIPSVE_GetNetworkStatus() = 0;
+ virtual int GIPSVE_CreateChannel() = 0;
+ virtual int GIPSVE_DeleteChannel(int channel) = 0;
+ virtual int GIPSVE_GetCodec(short listnr, GIPS_CodecInst *codec_inst) = 0;
+ virtual int GIPSVE_GetNofCodecs() = 0;
+ virtual int GIPSVE_SetSendCodec(int channel, GIPS_CodecInst *codec_inst) = 0;
+ virtual int GIPSVE_GetCurrentSendCodec(short channel, GIPS_CodecInst *gipsve_inst) = 0;
+ virtual int GIPSVE_GetRecCodec(int channel, GIPS_CodecInst *recCodec) = 0;
+ virtual int GIPSVE_SetRecPort(int channel, int portnr, char * multiCastAddr = NULL, char * ip = NULL) = 0;
+ virtual int GIPSVE_GetRecPort(int channel) = 0;
+ virtual int GIPSVE_SetSendPort(int channel, int portnr) = 0;
+ virtual int GIPSVE_SetSrcPort(int channel, int portnr) = 0;
+ virtual int GIPSVE_GetSendPort(int channel) = 0;
+ virtual int GIPSVE_SetSendIP(int channel, char *ipadr) = 0;
+ virtual int GIPSVE_GetSendIP(int channel, char *ipadr, int bufsize) = 0;
+ virtual int GIPSVE_StartListen(int channel) = 0;
+ virtual int GIPSVE_StartPlayout(int channel) = 0;
+ virtual int GIPSVE_StartSend(int channel) = 0;
+ virtual int GIPSVE_StopListen(int channel) = 0;
+ virtual int GIPSVE_StopPlayout(int channel) = 0;
+ virtual int GIPSVE_StopSend(int channel) = 0;
+ virtual int GIPSVE_GetLastError() = 0;
+ virtual int GIPSVE_SetSpeakerVolume(unsigned int level) = 0;
+ virtual int GIPSVE_GetSpeakerVolume() = 0;
+ virtual int GIPSVE_SetMicVolume(unsigned int level) = 0;
+ virtual int GIPSVE_GetMicVolume() = 0;
+ virtual int GIPSVE_SetAGCStatus(int mode) = 0;
+ virtual int GIPSVE_GetAGCStatus() = 0;
+ virtual int GIPSVE_GetVersion(char *version, int buflen) = 0;
+ virtual int GIPSVE_Terminate() = 0;
+ virtual int GIPSVE_SetDTMFPayloadType(int channel, int payloadType) = 0;
+ virtual int GIPSVE_SendDTMF(int channel, int eventnr, int inBand) = 0;
+ virtual int GIPSVE_PlayDTMFTone(int eventnr) = 0;
+ virtual unsigned short GIPSVE_GetFromPort(int channel)=0;
+ virtual int GIPSVE_SetFilterPort(int channel,unsigned short filter) = 0;
+ virtual int GIPSVE_SetFilterIP(int channel,char *IPaddress) = 0;
+ virtual unsigned short GIPSVE_GetFilterPort(int channel) = 0;
+ virtual int GIPSVE_SetRecPayloadType(short channel, GIPS_CodecInst *codec_inst)=0;
+ virtual int GIPSVE_RTCPStat(int channel, unsigned short *fraction_lost, unsigned long *cum_lost, unsigned long *ext_max, unsigned long *jitter, int *RTT)=0;
+ virtual int GIPSVE_SetSoundDevices(unsigned int WaveInDevice, unsigned int WaveOutDevice, bool disableMicBoost = false)= 0;
+ virtual int GIPSVE_SetDTMFFeedbackStatus(int mode) = 0;
+ virtual int GIPSVE_GetDTMFFeedbackStatus() = 0;
+
+ virtual int GIPSVE_GetNoOfChannels() = 0;
+ virtual int GIPSVE_GetInputLevel() = 0;
+ virtual int GIPSVE_GetOutputLevel(int channel = -1) = 0;
+ virtual int GIPSVE_MuteMic(int channel,int Mute) = 0;
+ virtual int GIPSVE_PutOnHold(int channel,bool enable) = 0;
+ virtual int GIPSVE_AddToConference(int channel,bool enable, bool includeCSRCs = false, bool includeVoiceLevel = false) = 0;
+
+ virtual int GIPSVE_CheckIfAudioIsAvailable(int checkPlay, int checkRec) = 0;
+
+ // RTCP calls
+ virtual int GIPSVE_EnableRTCP(int channel, int enable) = 0;
+ virtual int GIPSVE_SetRTCPCNAME(int channel, char * str) = 0;
+ virtual int GIPSVE_getRemoteRTCPCNAME(int channel, char * str) = 0;
+
+ virtual int GIPSVE_SetPacketTimeout(int channel, bool enable, int time_sec) = 0;
+
+ // Send extra packet over RTP / RTCP channel (no RTP headers added)
+ virtual int sendExtraPacket_RTP(int channel, unsigned char* data, int nbytes) = 0;
+ virtual int sendExtraPacket_RTCP(int channel, unsigned char* data, int nbytes) = 0;
+
+ // Voice Activity
+ virtual int GIPSVE_GetVoiceActivityIndicator(int channel) = 0;
+
+ // Use these function calls ONLY when a customer specific transport protocol is going to be used
+ virtual int GIPSVE_SetSendTransport(int channel, GIPS_transport &transport) = 0;
+ virtual int GIPSVE_ReceivedRTPPacket(int channel, const void *data, int len) = 0;
+ virtual int GIPSVE_ReceivedRTCPPacket(int channel, const void *data, int len) = 0;
+
+ virtual ~GipsVoiceEngineLite();
+};
+
+//////////////////////////////////////////////////////////////////////
+// Factory method
+//////////////////////////////////////////////////////////////////////
+
+VOICEENGINE_DLLEXPORT GipsVoiceEngineLite &GetGipsVoiceEngineLite();
+
+
+
+#endif // PUBLIC_GIPS_VOICE_ENGINE_LIB_H
diff --git a/third_party/libjingle/source/talk/third_party/gipslite/Interface/expiration.h b/third_party/libjingle/source/talk/third_party/gipslite/Interface/expiration.h
new file mode 100644
index 0000000..622c3ea
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/gipslite/Interface/expiration.h
@@ -0,0 +1,6 @@
+#define GIPS_EXPIRATION_MONTH 5
+
+#define GIPS_EXPIRATION_DAY 5
+
+#define GIPS_EXPIRATION_YEAR 2010
+
diff --git a/third_party/libjingle/source/talk/third_party/gipslite/Libraries/GipsVoiceEngineLite.dll b/third_party/libjingle/source/talk/third_party/gipslite/Libraries/GipsVoiceEngineLite.dll
new file mode 100644
index 0000000..e3a9cc9
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/gipslite/Libraries/GipsVoiceEngineLite.dll
Binary files differ
diff --git a/third_party/libjingle/source/talk/third_party/gipslite/Libraries/GipsVoiceEngineLite.lib b/third_party/libjingle/source/talk/third_party/gipslite/Libraries/GipsVoiceEngineLite.lib
new file mode 100644
index 0000000..6abed76
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/gipslite/Libraries/GipsVoiceEngineLite.lib
Binary files differ
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/Makefile.am b/third_party/libjingle/source/talk/third_party/mediastreamer/Makefile.am
new file mode 100644
index 0000000..9dcf686
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/Makefile.am
@@ -0,0 +1,91 @@
+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)
+
+AM_CFLAGS=$(GLIB_CFLAGS) -D__ALSA_ENABLED__ -DHAVE_ALSA_ASOUNDLIB_H -DG_LOG_DOMAIN=\"MediaStreamer\" $(ORTP_CFLAGS) $(IPV6_CFLAGS) $(ILBC_CFLAGS) $(SPEEX_CFLAGS)
+
+INCLUDES= -I$(top_srcdir) $(ORTP_CFLAGS)
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/affine.c b/third_party/libjingle/source/talk/third_party/mediastreamer/affine.c
new file mode 100644
index 0000000..99f33d7
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/affine.c
@@ -0,0 +1,144 @@
+/*
+ * affine.c -- Affine Transforms for 2d objects
+ * Copyright (C) 2002 Charles Yates <charles.yates@pandora.be>
+ * Portions Copyright (C) 2003 Dan Dennedy <dan@dennedy.org>
+ * ported from C++ to C
+ * wrote affine_scale()
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 "affine.h"
+
+static inline void Multiply( affine_transform_t *this, affine_transform_t *that )
+{
+ double output[2][2];
+ register int i, j;
+
+ for ( i = 0; i < 2; i ++ )
+ for ( j = 0; j < 2; j ++ )
+ output[ i ][ j ] = this->matrix[ i ][ 0 ] * that->matrix[ j ][ 0 ] +
+ this->matrix[ i ][ 1 ] * that->matrix[ j ][ 1 ];
+
+ this->matrix[ 0 ][ 0 ] = output[ 0 ][ 0 ];
+ this->matrix[ 0 ][ 1 ] = output[ 0 ][ 1 ];
+ this->matrix[ 1 ][ 0 ] = output[ 1 ][ 0 ];
+ this->matrix[ 1 ][ 1 ] = output[ 1 ][ 1 ];
+}
+
+void affine_transform_init( affine_transform_t *this )
+{
+ this->matrix[ 0 ][ 0 ] = 1;
+ this->matrix[ 0 ][ 1 ] = 0;
+ this->matrix[ 1 ][ 0 ] = 0;
+ this->matrix[ 1 ][ 1 ] = 1;
+}
+
+// Rotate by a given angle
+void affine_transform_rotate( affine_transform_t *this, double angle )
+{
+ affine_transform_t affine;
+ affine.matrix[ 0 ][ 0 ] = cos( angle * M_PI / 180 );
+ affine.matrix[ 0 ][ 1 ] = 0 - sin( angle * M_PI / 180 );
+ affine.matrix[ 1 ][ 0 ] = sin( angle * M_PI / 180 );
+ affine.matrix[ 1 ][ 1 ] = cos( angle * M_PI / 180 );
+ Multiply( this, &affine );
+}
+
+// Shear by a given value
+void affine_transform_shear( affine_transform_t *this, double shear )
+{
+ affine_transform_t affine;
+ affine.matrix[ 0 ][ 0 ] = 1;
+ affine.matrix[ 0 ][ 1 ] = shear;
+ affine.matrix[ 1 ][ 0 ] = 0;
+ affine.matrix[ 1 ][ 1 ] = 1;
+ Multiply( this, &affine );
+}
+
+void affine_transform_scale( affine_transform_t *this, double sx, double sy )
+{
+ affine_transform_t affine;
+ affine.matrix[ 0 ][ 0 ] = sx;
+ affine.matrix[ 0 ][ 1 ] = 0;
+ affine.matrix[ 1 ][ 0 ] = 0;
+ affine.matrix[ 1 ][ 1 ] = sy;
+ Multiply( this, &affine );
+}
+
+// Obtain the mapped x coordinate of the input
+double affine_transform_mapx( affine_transform_t *this, int x, int y )
+{
+ return this->matrix[0][0] * x + this->matrix[0][1] * y;
+}
+
+// Obtain the mapped y coordinate of the input
+double affine_transform_mapy( affine_transform_t *this, int x, int y )
+{
+ return this->matrix[1][0] * x + this->matrix[1][1] * y;
+}
+
+#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+
+void affine_scale( const unsigned char *src, unsigned char *dest, int src_width, int src_height, int dest_width, int dest_height, int bpp )
+{
+ affine_transform_t affine;
+ double scale_x = (double) dest_width / (double) src_width;
+ double scale_y = (double) dest_height / (double) src_height;
+ register unsigned char *d = dest;
+ register const unsigned char *s = src;
+ register int i, j, k, x, y;
+
+ affine_transform_init( &affine );
+
+ if ( scale_x <= 1.0 && scale_y <= 1.0 )
+ {
+ affine_transform_scale( &affine, scale_x, scale_y );
+
+ for( j = 0; j < src_height; j++ )
+ for( i = 0; i < src_width; i++ )
+ {
+ x = (int) ( affine_transform_mapx( &affine, i - src_width/2, j - src_height/2 ) );
+ y = (int) ( affine_transform_mapy( &affine, i - src_width/2, j - src_height/2 ) );
+ x += dest_width/2;
+ x = CLAMP( x, 0, dest_width);
+ y += dest_height/2;
+ y = CLAMP( y, 0, dest_height);
+ s = src + (j*src_width*bpp) + i*bpp; // + (bpp-1);
+ d = dest + y*dest_width*bpp + x*bpp;
+ for ( k = 0; k < bpp; k++ )
+ *d++ = *s++;
+ }
+ }
+ else if ( scale_x > 1.0 && scale_y > 1.0 )
+ {
+ affine_transform_scale( &affine, 1.0/scale_x, 1.0/scale_y );
+
+ for( y = 0; y < dest_height; y++ )
+ for( x = 0; x < dest_width; x++ )
+ {
+ i = (int) ( affine_transform_mapx( &affine, x - dest_width/2, y - dest_height/2 ) );
+ j = (int) ( affine_transform_mapy( &affine, x - dest_width/2, y - dest_height/2 ) );
+ i += src_width/2;
+ i = CLAMP( i, 0, dest_width);
+ j += src_height/2;
+ j = CLAMP( j, 0, dest_height);
+ s = src + (j*src_width*bpp) + i*bpp; // + (bpp-1);
+ d = dest + y*dest_width*bpp + x*bpp;
+ for ( k = 0; k < bpp; k++ )
+ *d++ = *s++;
+ }
+ }
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/affine.h b/third_party/libjingle/source/talk/third_party/mediastreamer/affine.h
new file mode 100644
index 0000000..620fdc9
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/alsacard.c b/third_party/libjingle/source/talk/third_party/mediastreamer/alsacard.c
new file mode 100644
index 0000000..ebf2909
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/alsacard.h b/third_party/libjingle/source/talk/third_party/mediastreamer/alsacard.h
new file mode 100644
index 0000000..df3372f
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/audiostream.c b/third_party/libjingle/source/talk/third_party/mediastreamer/audiostream.c
new file mode 100644
index 0000000..67a4485
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/g711common.h b/third_party/libjingle/source/talk/third_party/mediastreamer/g711common.h
new file mode 100644
index 0000000..3f5ad16
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/hpuxsndcard.c b/third_party/libjingle/source/talk/third_party/mediastreamer/hpuxsndcard.c
new file mode 100644
index 0000000..8210e29
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/jackcard.c b/third_party/libjingle/source/talk/third_party/mediastreamer/jackcard.c
new file mode 100644
index 0000000..b929cce
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/jackcard.h b/third_party/libjingle/source/talk/third_party/mediastreamer/jackcard.h
new file mode 100644
index 0000000..33ec46d
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mediastream.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mediastream.c
new file mode 100644
index 0000000..e4ea789
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/mediastream.c
@@ -0,0 +1,117 @@
+/*
+ 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"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int cond=1;
+
+void stop_handler(int signum)
+{
+ cond--;
+ if (cond<0) exit(-1);
+}
+
+void parse_addr(gchar *addr, char **ip, int *port)
+{
+ char *semicolon;
+ gint iplen;
+
+ *ip=NULL;
+ *port=0;
+ semicolon=strchr(addr,':');
+ if (semicolon==NULL) return;
+ iplen=semicolon-addr;
+ *ip=g_malloc(iplen+1);
+ strncpy(*ip,addr,iplen);
+ (*ip)[iplen]='\0';
+ *port=atoi(semicolon+1);
+}
+
+char *usage="mediastream --local <port> --remote <ip:port> --payload <payload type>\n";
+void run_media_streams(gint localport, gchar *remote_ip, gint remoteport, gint payload);
+
+int main(int argc, char * argv[])
+{
+ gint i;
+ gint localport=0,remoteport=0,payload=0;
+ gchar *ip;
+ gchar *tmp;
+
+ /*create the rtp session */
+ ortp_init();
+ ortp_set_debug_file("oRTP",NULL);
+ rtp_profile_set_payload(&av_profile,115,&lpc1015);
+ rtp_profile_set_payload(&av_profile,110,&speex_nb);
+ rtp_profile_set_payload(&av_profile,8,&pcma8000);
+
+
+ if (argc<4) {
+ printf(usage);
+ return -1;
+ }
+ for (i=1;i<argc;i++){
+ if (strcmp(argv[i],"--local")==0){
+ i++;
+ localport=atoi(argv[i]);
+ }else if (strcmp(argv[i],"--remote")==0){
+ i++;
+ parse_addr(argv[i],&ip,&remoteport);
+ if (ip==NULL) {
+ printf(usage);
+ return -1;
+ }
+ printf("Remote addr: ip=%s port=%i\n",ip,remoteport);
+ }else if (strcmp(argv[i],"--payload")==0){
+ i++;
+ payload=atoi(argv[i]);
+ }
+ }
+ tmp=getenv("defcard");
+ if (tmp!=NULL) audio_stream_set_default_card(atoi(tmp));
+ run_media_streams(localport,ip,remoteport,payload);
+ return 0;
+}
+
+void run_media_streams(gint localport, gchar *remote_ip, gint remoteport, gint payload)
+{
+ AudioStream *audio;
+ ms_init();
+ ms_speex_codec_init();
+ ms_ilbc_codec_init();
+ signal(SIGINT,stop_handler);
+
+ audio=audio_stream_start(&av_profile,localport,remote_ip,remoteport,payload,250);
+ while(cond)
+ {
+ /* sleep until we receive SIGINT */
+ sleep(1);
+ }
+
+ printf("stoping all...\n");
+
+ audio_stream_stop(audio);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/mediastream.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mediastream.h
new file mode 100644
index 0000000..3ccbab6
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/ms.c b/third_party/libjingle/source/talk/third_party/mediastreamer/ms.c
new file mode 100644
index 0000000..a4a57f8
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/ms.h b/third_party/libjingle/source/talk/third_party/mediastreamer/ms.h
new file mode 100644
index 0000000..51c69b9
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msAlawdec.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msAlawdec.c
new file mode 100644
index 0000000..70cc906
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msAlawdec.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msAlawdec.h
new file mode 100644
index 0000000..7db4c75
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msAlawenc.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msAlawenc.c
new file mode 100644
index 0000000..fd1f9ab
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msAlawenc.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msAlawenc.h
new file mode 100644
index 0000000..608a988
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMdecoder.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMdecoder.c
new file mode 100644
index 0000000..1c18517
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMdecoder.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 "msGSMdecoder.h"
+
+extern MSFilter * ms_GSMencoder_new(void);
+
+MSCodecInfo GSMinfo={
+ {
+ "GSM codec",
+ 0,
+ MS_FILTER_AUDIO_CODEC,
+ ms_GSMencoder_new,
+ "This is the codec widely used in european mobile phones. This implementation was done by "
+ "Jutta Degener and Carsten Bormann."
+ },
+ ms_GSMencoder_new,
+ ms_GSMdecoder_new,
+ 320,
+ 33,
+ 13800,
+ 8000,
+ 3,
+ "GSM",
+ 1,
+ 1,
+};
+
+static MSGSMDecoderClass *ms_GSMdecoder_class=NULL;
+
+MSFilter * ms_GSMdecoder_new(void)
+{
+ MSGSMDecoder *r;
+
+ r=g_new(MSGSMDecoder,1);
+ ms_GSMdecoder_init(r);
+ if (ms_GSMdecoder_class==NULL)
+ {
+ ms_GSMdecoder_class=g_new(MSGSMDecoderClass,1);
+ ms_GSMdecoder_class_init(ms_GSMdecoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_GSMdecoder_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_GSMdecoder_init(MSGSMDecoder *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=33;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSGSMDECODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSGSMDECODER_MAX_INPUTS);
+ r->gsm_handle=gsm_create();
+}
+
+void ms_GSMdecoder_class_init(MSGSMDecoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"GSMDecoder");
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&GSMinfo;
+ MS_FILTER_CLASS(klass)->max_finputs=MSGSMDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSGSMDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=33;
+ MS_FILTER_CLASS(klass)->w_maxgran=2*160;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_GSMdecoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_GSMdecoder_process;
+}
+
+void ms_GSMdecoder_process(MSGSMDecoder *r)
+{
+ MSFifo *fi,*fo;
+ int err1;
+ 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,33,&s);
+ if (err1>0)
+ {
+ err1=ms_fifo_get_write_ptr(fo,160*2,&d);
+ if (d!=NULL) gsm_decode(r->gsm_handle,s,(gsm_signal*)d);
+ }
+
+ }
+}
+
+void ms_GSMdecoder_uninit(MSGSMDecoder *obj)
+{
+ gsm_destroy(obj->gsm_handle);
+}
+
+void ms_GSMdecoder_destroy( MSGSMDecoder *obj)
+{
+ ms_GSMdecoder_uninit(obj);
+ g_free(obj);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMdecoder.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMdecoder.h
new file mode 100644
index 0000000..e73fb33
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMencoder.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMencoder.c
new file mode 100644
index 0000000..44570d7
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMencoder.c
@@ -0,0 +1,101 @@
+/*
+ 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 "msGSMencoder.h"
+#include "mscodec.h"
+
+extern MSCodecInfo GSMinfo;
+
+static MSGSMEncoderClass *ms_GSMencoder_class=NULL;
+
+MSFilter * ms_GSMencoder_new(void)
+{
+ MSGSMEncoder *r;
+
+ r=g_new(MSGSMEncoder,1);
+ ms_GSMencoder_init(r);
+ if (ms_GSMencoder_class==NULL)
+ {
+ ms_GSMencoder_class=g_new(MSGSMEncoderClass,1);
+ ms_GSMencoder_class_init(ms_GSMencoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_GSMencoder_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_GSMencoder_init(MSGSMEncoder *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=2*160;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSGSMENCODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSGSMENCODER_MAX_INPUTS);
+ r->gsm_handle=gsm_create();
+}
+
+void ms_GSMencoder_class_init(MSGSMEncoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"GSMEncoder");
+ MS_FILTER_CLASS(klass)->max_finputs=MSGSMENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSGSMENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=2*160;
+ MS_FILTER_CLASS(klass)->w_maxgran=33;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_GSMencoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_GSMencoder_process;
+ MS_FILTER_CLASS(klass)->info=MS_FILTER_INFO(&GSMinfo);
+}
+
+void ms_GSMencoder_process(MSGSMEncoder *r)
+{
+ MSFifo *fi,*fo;
+ int err1;
+ 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,160*2,&s);
+ if (err1>0)
+ {
+ err1=ms_fifo_get_write_ptr(fo,33,&d);
+ if (d!=NULL) gsm_encode(r->gsm_handle,(gsm_signal*)s,(gsm_byte*)d);
+ }
+
+ }
+}
+
+void ms_GSMencoder_uninit(MSGSMEncoder *obj)
+{
+ gsm_destroy(obj->gsm_handle);
+}
+
+void ms_GSMencoder_destroy( MSGSMEncoder *obj)
+{
+ ms_GSMencoder_uninit(obj);
+ g_free(obj);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMencoder.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msGSMencoder.h
new file mode 100644
index 0000000..2deae38
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10decoder.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10decoder.c
new file mode 100644
index 0000000..1d398be
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10decoder.c
@@ -0,0 +1,129 @@
+/*
+ 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 "msLPC10decoder.h"
+#include "msLPC10encoder.h"
+#include <stdlib.h>
+#include <lpc10.h>
+
+extern MSFilter * ms_LPC10encoder_new(void);
+
+MSCodecInfo LPC10info={
+ {
+ "LPC10-15 codec",
+ 0,
+ MS_FILTER_AUDIO_CODEC,
+ ms_LPC10encoder_new,
+ "A low quality but very low bit rate codec from the U.S. Department of Defense."
+ },
+ ms_LPC10encoder_new,
+ ms_LPC10decoder_new,
+ 360,
+ 7,
+ 2400,
+ 8000,
+ 115,
+ "1015",
+ 1,
+ 1,
+};
+
+static MSLPC10DecoderClass *ms_LPC10decoder_class=NULL;
+
+MSFilter * ms_LPC10decoder_new(void)
+{
+ MSLPC10Decoder *r;
+
+ r=g_new(MSLPC10Decoder,1);
+ ms_LPC10decoder_init(r);
+ if (ms_LPC10decoder_class==NULL)
+ {
+ ms_LPC10decoder_class=g_new(MSLPC10DecoderClass,1);
+ ms_LPC10decoder_class_init(ms_LPC10decoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_LPC10decoder_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_LPC10decoder_init(MSLPC10Decoder *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=7;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSLPC10DECODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSLPC10DECODER_MAX_INPUTS);
+ r->lpc10_dec=create_lpc10_decoder_state();
+}
+
+void ms_LPC10decoder_class_init(MSLPC10DecoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"LPC10Dec");
+ MS_FILTER_CLASS(klass)->max_finputs=MSLPC10DECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSLPC10DECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=7;
+ MS_FILTER_CLASS(klass)->w_maxgran=LPC10_SAMPLES_PER_FRAME*2;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_LPC10decoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_LPC10decoder_process;
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&LPC10info;
+}
+
+void ms_LPC10decoder_process(MSLPC10Decoder *r)
+{
+ MSFifo *fi,*fo;
+ int err1;
+ void *s,*d;
+ float speech[LPC10_SAMPLES_PER_FRAME];
+ INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME];
+
+ /* 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,7,&s);
+ if (err1>0)
+ {
+ err1=ms_fifo_get_write_ptr(fo,LPC10_SAMPLES_PER_FRAME*2,&d);
+ if (d!=NULL)
+ {
+ read_bits(s, bits, LPC10_BITS_IN_COMPRESSED_FRAME);
+ lpc10_decode(bits,speech, r->lpc10_dec);
+ write_16bit_samples((INT16*)d, speech, LPC10_SAMPLES_PER_FRAME);
+ }
+ }
+ }
+}
+
+void ms_LPC10decoder_uninit(MSLPC10Decoder *obj)
+{
+ free(obj->lpc10_dec);
+}
+
+void ms_LPC10decoder_destroy( MSLPC10Decoder *obj)
+{
+ ms_LPC10decoder_uninit(obj);
+ g_free(obj);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10decoder.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10decoder.h
new file mode 100644
index 0000000..59d9dec
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10encoder.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10encoder.c
new file mode 100644
index 0000000..2c083f3
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10encoder.c
@@ -0,0 +1,251 @@
+/*
+ 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 <stdlib.h>
+#include "msLPC10encoder.h"
+#include <lpc10.h>
+
+
+extern MSCodecInfo LPC10info;
+
+/* 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
+read_16bit_samples(INT16 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] = ((float) int16samples[i]) / 32768.0;
+ }
+
+ return (n);
+}
+
+
+
+int
+write_16bit_samples(INT16 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++) {
+ real_sample = 32768.0 * speech[i];
+ if (real_sample < -32768.0) {
+ int16samples[i] = -32768;
+ } else if (real_sample > 32767.0) {
+ int16samples[i] = 32767;
+ } else {
+ int16samples[i] = real_sample;
+ }
+ }
+ 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
+write_bits(unsigned char *data, INT32 *bits, int len)
+{
+ int i; /* generic loop variable */
+ unsigned char mask; /* The next bit position within the
+ variable "data" to place the next
+ bit. */
+
+
+ /* Fill in the array bits.
+ * The first compressed output bit will be the most significant
+ * bit of the byte, so initialize mask to 0x80. The next byte of
+ * compressed data is initially 0, and the desired bits will be
+ * turned on below.
+ */
+ mask = 0x80;
+ *data = 0;
+
+ for (i = 0; i < len; i++) {
+ /* Turn on the next bit of output data, if necessary. */
+ if (bits[i]) {
+ (*data) |= mask;
+ }
+ /*
+ * If the byte data is full, determined by mask becoming 0,
+ * then write the byte to the output file, and reinitialize
+ * data and mask for the next output byte. Also add the byte
+ * if (i == len-1), because if len is not a multiple of 8,
+ * then mask won't yet be 0. */
+ mask >>= 1;
+ if ((mask == 0) || (i == len-1)) {
+ data++;
+ *data = 0;
+ mask = 0x80;
+ }
+ }
+}
+
+
+
+/*
+
+Read bits from file f into bits[0] through bits[len-1], in "packed"
+format.
+
+Read ceiling(len/8) characters from file f, if that many are available
+to read, otherwise read to the end of the file. The first character's
+8 bits, in order from MSB to LSB, are used to fill bits[0] through
+bits[7]. The second character's bits are used to fill bits[8] through
+bits[15], and so on. If ceiling(len/8) characters are available to
+read, and len is not a multiple of 8, then some of the least
+significant bits of the last character read are completely ignored.
+Every entry of bits[] that is modified is changed to either a 0 or a
+1.
+
+The number of bits successfully read is returned, and is always in the
+range 0 to len, inclusive. If it is less than len, it will always be
+a multiple of 8.
+
+*/
+
+int
+read_bits(unsigned char *data, INT32 *bits, int len)
+{
+ int i,ind=0; /* generic loop variable */
+ int c=0;
+
+ /* Unpack the array bits into coded_frame. */
+ for (i = 0; i < len; i++) {
+ if ((i % 8) == 0) {
+ c = (int)(data[ind]);
+ ind++;
+ }
+ if (c & (0x80 >> (i & 7))) {
+ bits[i] = 1;
+ } else {
+ bits[i] = 0;
+ }
+ }
+ return (len);
+}
+
+
+
+
+static MSLPC10EncoderClass *ms_LPC10encoder_class=NULL;
+
+MSFilter * ms_LPC10encoder_new(void)
+{
+ MSLPC10Encoder *r;
+
+ r=g_new(MSLPC10Encoder,1);
+ ms_LPC10encoder_init(r);
+ if (ms_LPC10encoder_class==NULL)
+ {
+ ms_LPC10encoder_class=g_new(MSLPC10EncoderClass,1);
+ ms_LPC10encoder_class_init(ms_LPC10encoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_LPC10encoder_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_LPC10encoder_init(MSLPC10Encoder *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=LPC10_SAMPLES_PER_FRAME*2;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSLPC10ENCODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSLPC10ENCODER_MAX_INPUTS);
+ r->lpc10_enc=create_lpc10_encoder_state();
+}
+
+void ms_LPC10encoder_class_init(MSLPC10EncoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"LPC10Enc");
+ MS_FILTER_CLASS(klass)->max_finputs=MSLPC10ENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSLPC10ENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=LPC10_SAMPLES_PER_FRAME*2;
+ MS_FILTER_CLASS(klass)->w_maxgran=7;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_LPC10encoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_LPC10encoder_process;
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&LPC10info;
+}
+
+void ms_LPC10encoder_process(MSLPC10Encoder *r)
+{
+ MSFifo *fi,*fo;
+ int err1;
+ void *s,*d;
+ float speech[LPC10_SAMPLES_PER_FRAME];
+ INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME];
+
+ /* 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,LPC10_SAMPLES_PER_FRAME*2,&s);
+ if (err1>0)
+ {
+ err1=ms_fifo_get_write_ptr(fo,7,&d);
+ if (d!=NULL)
+ {
+ read_16bit_samples((INT16*)s, speech, LPC10_SAMPLES_PER_FRAME);
+ lpc10_encode(speech, bits, r->lpc10_enc);
+ write_bits(d, bits, LPC10_BITS_IN_COMPRESSED_FRAME);
+ }
+ }
+
+ }
+}
+
+void ms_LPC10encoder_uninit(MSLPC10Encoder *obj)
+{
+ free(obj->lpc10_enc);
+}
+
+void ms_LPC10encoder_destroy( MSLPC10Encoder *obj)
+{
+ ms_LPC10encoder_uninit(obj);
+ g_free(obj);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10encoder.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msLPC10encoder.h
new file mode 100644
index 0000000..4db1643
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawdec.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawdec.c
new file mode 100644
index 0000000..500f238
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawdec.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawdec.h
new file mode 100644
index 0000000..c135d21
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawenc.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawenc.c
new file mode 100644
index 0000000..2f740d8
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawenc.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msMUlawenc.h
new file mode 100644
index 0000000..52f7666
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msavdecoder.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msavdecoder.c
new file mode 100644
index 0000000..bff2778
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/msavdecoder.c
@@ -0,0 +1,278 @@
+/*
+ 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 "msavdecoder.h"
+#include "mscodec.h"
+extern MSFilter *ms_mpeg_encoder_new();
+extern MSFilter *ms_mpeg4_encoder_new();
+extern MSFilter *ms_h263_encoder_new();
+
+
+MSCodecInfo MPEGinfo={
+ {
+ "MPEG1 codec",
+ 0,
+ MS_FILTER_VIDEO_CODEC,
+ ms_mpeg_encoder_new,
+ "This is a MPEG1 codec taken from the ffmpeg project."
+ },
+ ms_mpeg_encoder_new,
+ ms_mpeg_decoder_new,
+ 0,
+ 0,
+ 0, /*bitrate */
+ 0, /*sample freq */
+ 0,
+ "MPV",
+ 1,
+ 1
+};
+
+MSCodecInfo h263info={
+ {
+ "H263 codec",
+ 0,
+ MS_FILTER_VIDEO_CODEC,
+ ms_h263_encoder_new,
+ "This is a H263 codec taken from the ffmpeg project."
+ },
+ ms_h263_encoder_new,
+ ms_h263_decoder_new,
+ 0,
+ 0,
+ 0, /*bitrate */
+ 0, /*sample freq */
+ 0,
+ "H263",
+ 1,
+ 1
+};
+
+MSCodecInfo MPEG4info={
+ {
+ "MPEG4 codec",
+ 0,
+ MS_FILTER_VIDEO_CODEC,
+ ms_mpeg4_encoder_new,
+ "This is a MPEG4 codec taken from the ffmpeg project."
+ },
+ ms_mpeg4_encoder_new,
+ ms_mpeg4_decoder_new,
+ 0,
+ 0,
+ 0, /*bitrate */
+ 0, /*sample freq */
+ 0,
+ "MP4V-ES",
+ 1,
+ 1
+};
+
+
+void ms_AVCodec_init()
+{
+ avcodec_init();
+ avcodec_register_all();
+ ms_filter_register((MSFilterInfo*)&h263info);
+ //ms_filter_register((MSFilterInfo*)&MPEG4info);
+}
+
+
+static MSAVDecoderClass *ms_avdecoder_class=NULL;
+
+MSFilter *ms_h263decoder_new()
+{
+ return ms_AVdecoder_new_with_codec(CODEC_ID_H263);
+}
+
+MSFilter *ms_mpeg_decoder_new()
+{
+ return ms_AVdecoder_new_with_codec(CODEC_ID_MPEG1VIDEO);
+}
+
+MSFilter *ms_mpeg4_decoder_new()
+{
+ return ms_AVdecoder_new_with_codec(CODEC_ID_MPEG4);
+}
+
+MSFilter *ms_h263_decoder_new(){
+ return ms_AVdecoder_new_with_codec(CODEC_ID_H263);
+}
+
+MSFilter * ms_AVdecoder_new_with_codec(enum CodecID codec_id)
+{
+ MSAVDecoder *enc;
+
+ enc=g_malloc0(sizeof(MSAVDecoder));
+ if (ms_avdecoder_class==NULL)
+ {
+ ms_avdecoder_class=g_malloc0(sizeof(MSAVDecoderClass));
+ ms_AVdecoder_class_init(ms_avdecoder_class);
+ }
+ MS_FILTER(enc)->klass=(MSFilterClass*)ms_avdecoder_class;
+ ms_AVdecoder_init(enc,avcodec_find_decoder(codec_id));
+ return MS_FILTER(enc);
+}
+
+
+void ms_AVdecoder_init(MSAVDecoder *dec, AVCodec *codec)
+{
+ gint error;
+
+ ms_filter_init(MS_FILTER(dec));
+ MS_FILTER(dec)->inqueues=dec->q_inputs;
+ MS_FILTER(dec)->outqueues=dec->q_outputs;
+ avcodec_get_context_defaults(&dec->av_context);
+ dec->av_codec=codec;
+ dec->av_opened=0;
+ dec->skip_gob=1;
+ dec->obufwrap=NULL;
+}
+
+void ms_AVdecoder_class_init(MSAVDecoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name( MS_FILTER_CLASS(klass),"AVdecoder");
+ MS_FILTER_CLASS(klass)->max_qinputs=MSAVDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_qoutputs=MSAVDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=0;
+ MS_FILTER_CLASS(klass)->w_maxgran=0;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_AVdecoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_AVdecoder_process;
+}
+
+void ms_AVdecoder_uninit(MSAVDecoder *dec)
+{
+ if (dec->obufwrap!=NULL) ms_buffer_destroy(dec->obufwrap);
+ if (dec->av_opened) avcodec_close(&dec->av_context);
+}
+void ms_AVdecoder_destroy( MSAVDecoder *obj)
+{
+ ms_AVdecoder_uninit(obj);
+ g_free(obj);
+}
+
+gint ms_AVdecoder_set_format(MSAVDecoder *dec, gchar *fmt)
+{
+ gint format;
+ if (strcmp(fmt,"YUV420P")==0) format=PIX_FMT_YUV420P;
+ else if (strcmp(fmt,"YUV422")==0) format=PIX_FMT_YUV422;
+ else if (strcmp(fmt,"RGB24")==0) format=PIX_FMT_RGB24;
+ else if (strcmp(fmt,"BGR24")==0) format=PIX_FMT_BGR24;
+ else if (strcmp(fmt,"YUV422P")==0) format=PIX_FMT_YUV422P;
+ else if (strcmp(fmt,"YUV444P")==0) format=PIX_FMT_YUV444P;
+ else {
+ g_warning("ms_AVdecoder_set_format: unsupported format %s.",fmt);
+ return -1;
+ }
+ dec->output_pix_fmt=format;
+ return 0;
+}
+
+void ms_AVdecoder_process(MSAVDecoder *r)
+{
+ AVFrame orig;
+ AVFrame transformed;
+ MSQueue *inq,*outq;
+ MSMessage *inm,*outm;
+ gint error;
+ gint got_picture;
+ gint len;
+ unsigned char *data;
+ AVCodecContext *ctx=&r->av_context;
+ gint gob_num;
+
+ inq=r->q_inputs[0];
+ outq=r->q_outputs[0];
+
+ /* get a picture from the input queue */
+ inm=ms_queue_get(inq);
+ g_return_if_fail(inm!=NULL);
+ if (inm->size > 0)
+ {
+ guint32 *p = inm->data;
+
+ if (!r->av_opened){
+ error=avcodec_open(&r->av_context, r->av_codec);
+ if (error!=0) g_warning("avcodec_open() failed: %i",error);
+ else r->av_opened=1;
+ }
+
+ gob_num = (ntohl(*p) >> 10) & 0x1f;
+ ms_trace("gob %i, size %i", gob_num, inm->size);
+ ms_trace("ms_AVdecoder_process: received %08x %08x", ntohl(p[0]), ntohl(p[1]));
+
+ /* remove H.263 Payload Header */
+ p[0] = htonl( ntohl(p[0]) & 0x0000ffff );
+
+ if (gob_num == 0){
+ if (r->skip_gob == 0)
+ {
+ unsigned char *data = r->buf_compressed;
+ ms_trace("ms_AVdecoder_process: decoding %08x %08x %08x", ntohl(((unsigned int *)data)[0]), ntohl(((unsigned int *)data)[1]), ntohl(((unsigned int *)data)[2]));
+ while (r->buf_size > 0) {
+ len=avcodec_decode_video(&r->av_context,&orig,&got_picture,data,r->buf_size /*inm->size*/);
+ if (len<0) {
+ ms_warning("ms_AVdecoder_process: error %i.",len);
+ break;
+ }
+ if (got_picture) {
+ ms_trace("ms_AVdecoder_process: got_picture: width=%i height=%i fmt=%i",
+ ctx->width,ctx->height,ctx->pix_fmt);
+ /* set the image in the wanted format */
+ outm=ms_message_alloc();
+ if (r->obufwrap==NULL){
+ r->obufwrap=ms_buffer_new(avpicture_get_size(r->output_pix_fmt,ctx->width,ctx->height));
+ r->obufwrap->ref_count++;
+ }
+ ms_message_set_buf(outm,r->obufwrap);
+ avpicture_fill(&transformed,outm->data,r->output_pix_fmt,ctx->width,ctx->height);
+ img_convert(&transformed, r->output_pix_fmt,
+ &orig,ctx->pix_fmt,ctx->width,ctx->height);
+ ms_queue_put(outq,outm);
+ }
+ r->buf_size -= len;
+ data += len;
+ }
+ }
+ else {
+ r->skip_gob = 0;
+ }
+ memcpy(r->buf_compressed, inm->data, inm->size);
+ r->buf_size = inm->size;
+ }
+ else {
+ memcpy(r->buf_compressed + r->buf_size, inm->data, inm->size);
+ r->buf_size += inm->size;
+ }
+ }
+ ms_message_destroy(inm);
+}
+
+
+void ms_AVdecoder_set_width(MSAVDecoder *av,gint w)
+{
+ av->av_context.width=av->width=w;
+}
+
+void ms_AVdecoder_set_height(MSAVDecoder *av,gint h)
+{
+ av->av_context.height=av->height=h;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/msavdecoder.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msavdecoder.h
new file mode 100644
index 0000000..e7c880b
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msavencoder.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msavencoder.c
new file mode 100644
index 0000000..ae65c37
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/msavencoder.c
@@ -0,0 +1,235 @@
+/*
+ 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 "msavencoder.h"
+#include "msutils.h"
+
+extern MSCodecInfo MPEG4info;
+extern MSCodecInfo MPEGinfo;
+extern MSCodecInfo h263info;
+
+static MSAVEncoderClass *ms_avencoder_class=NULL;
+static void ms_AVencoder_rtp_callback (AVCodecContext *ctx,void *data, int size, int packet_number);
+
+MSFilter *ms_h263_encoder_new()
+{
+ return ms_AVencoder_new_with_codec(CODEC_ID_H263,&h263info);
+}
+
+MSFilter *ms_mpeg_encoder_new()
+{
+ return ms_AVencoder_new_with_codec(CODEC_ID_MPEG1VIDEO, &MPEGinfo);
+}
+
+MSFilter *ms_mpeg4_encoder_new()
+{
+ return ms_AVencoder_new_with_codec(CODEC_ID_MPEG4,&MPEG4info);
+}
+
+MSFilter * ms_AVencoder_new_with_codec(enum CodecID codec_id, MSCodecInfo *info)
+{
+ MSAVEncoder *enc;
+ AVCodec *avc;
+ enc=g_malloc0(sizeof(MSAVEncoder));
+ if (ms_avencoder_class==NULL)
+ {
+ ms_avencoder_class=g_malloc0(sizeof(MSAVEncoderClass));
+ ms_AVencoder_class_init(ms_avencoder_class);
+ }
+ MS_FILTER(enc)->klass=(MSFilterClass*)ms_avencoder_class;
+ avc=avcodec_find_encoder(codec_id);
+ if (avc==NULL) g_error("unknown av codec.");
+ ms_AVencoder_init(enc,avc);
+ return MS_FILTER(enc);
+}
+
+
+void ms_AVencoder_init(MSAVEncoder *enc, AVCodec *codec)
+{
+ gint error;
+ AVCodecContext *c=&enc->av_context;
+
+ ms_filter_init(MS_FILTER(enc));
+ MS_FILTER(enc)->inqueues=enc->q_inputs;
+ MS_FILTER(enc)->outqueues=enc->q_outputs;
+ /* put default values */
+ memset(c, 0, sizeof(AVCodecContext));
+ avcodec_get_context_defaults(c);
+
+ /* put sample parameters */
+ c->bit_rate = 400000;
+ /* resolution must be a multiple of two */
+ c->width = VIDEO_SIZE_CIF_W;
+ c->height = VIDEO_SIZE_CIF_H;
+ /* frames per second */
+ c->frame_rate = 15;
+ c->frame_rate_base = 1;
+ c->gop_size = 10; /* emit one intra frame every x frames */
+ c->rtp_mode = 1;
+ c->rtp_payload_size = 1488;
+ c->opaque = (void *) enc;
+ c->rtp_callback = ms_AVencoder_rtp_callback;
+ c->pix_fmt=PIX_FMT_YUV420P;
+
+ enc->av_opened=0;
+ enc->av_codec=codec;
+ enc->yuv_buf=NULL;
+ enc->comp_buf=NULL;
+ /*set default input format */
+ ms_AVencoder_set_format(enc,"RGB24");
+}
+
+void ms_AVencoder_class_init(MSAVEncoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ MS_FILTER_CLASS(klass)->info=0;
+ MS_FILTER_CLASS(klass)->max_qinputs=MSAVENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_qoutputs=MSAVENCODER_MAX_OUTPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=0;
+ MS_FILTER_CLASS(klass)->w_maxgran=0;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_AVencoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_AVencoder_process;
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"AVEncoder");
+
+}
+
+void ms_AVencoder_uninit(MSAVEncoder *enc)
+{
+ if (enc->av_opened)
+ avcodec_close(&enc->av_context);
+ if (enc->comp_buf!=NULL) {
+ ms_buffer_destroy(enc->comp_buf);
+ enc->comp_buf=NULL;
+ }
+ if (enc->yuv_buf!=NULL) {
+ ms_buffer_destroy(enc->yuv_buf);
+ enc->yuv_buf=NULL;
+ }
+
+}
+void ms_AVencoder_destroy( MSAVEncoder *obj)
+{
+ ms_AVencoder_uninit(obj);
+ g_free(obj);
+}
+
+
+void ms_AVencoder_set_frame_rate(MSAVEncoder *obj, gint frame_rate, gint frame_rate_base)
+{
+ obj->av_context.frame_rate = frame_rate;
+ obj->av_context.frame_rate_base = frame_rate_base;
+}
+
+static void ms_AVencoder_rtp_callback (AVCodecContext *ctx, void *data, int size, int packet_number)
+{
+ MSAVEncoder *r = MS_AVENCODER(ctx->opaque);
+ MSQueue *outq = r->q_outputs[0];
+ MSMessage *outm;
+ guint32 *p = (guint32 *) data;
+ gint gob_num = (ntohl(*p) >> 10) & 0x1f;
+
+ /*g_message("ms_AVencoder_rtp_callback: packet %i, size %i, GOB number %i", packet_number, size, gob_num);*/
+ ms_trace("ms_AVencoder_rtp_callback: received %08x %08x", ntohl(p[0]), ntohl(p[1]));
+ /* set the H.263 Payload Header (RFC 2429) */
+ p[0] = ntohl( (0x04000000) | (ntohl(p[0]) & 0x0000ffff) ); /* P=1, V=0, PLEN=0 */
+ ms_trace("ms_AVencoder_rtp_callback: sending %08x %08x", ntohl(p[0]), ntohl(p[1]));
+ outm = ms_message_new(size);
+ memcpy(outm->data,data,size);
+ ms_queue_put(outq, outm);
+}
+
+void ms_AVencoder_process(MSAVEncoder *r)
+{
+ AVFrame orig;
+ AVFrame pict;
+ AVCodecContext *c=&r->av_context;
+ MSQueue *inq,*outq;
+ MSMessage *inm,*outm;
+ gint error;
+
+ inq=r->q_inputs[0];
+ outq=r->q_outputs[0];
+
+ /* get a picture from the input queue */
+ inm=ms_queue_get(inq);
+ g_return_if_fail(inm!=NULL);
+
+ /* allocate a new image */
+ if (r->yuv_buf==NULL){
+ gint bsize = avpicture_get_size(c->pix_fmt,c->width,c->height);
+ r->yuv_buf=ms_buffer_new(bsize);
+ r->yuv_buf->ref_count++;
+
+ r->comp_buf=ms_buffer_new(bsize/2);
+ r->comp_buf->ref_count++;
+ }
+ if (!r->av_opened || r->av_context.codec == NULL){
+ error=avcodec_open(c, r->av_codec);
+ ms_trace("image format is %i.",c->pix_fmt);
+ if (error!=0) {
+ g_warning("avcodec_open() failed: %i",error);
+ return;
+ }else r->av_opened=1;
+ }
+ outm=ms_message_alloc();
+ /* convert image if necessary */
+ if (r->input_pix_fmt!=c->pix_fmt){
+ ms_trace("Changing picture format.");
+ avpicture_fill((AVPicture*)&orig,inm->data,r->input_pix_fmt,c->width,c->height);
+ avpicture_fill((AVPicture*)&pict,r->yuv_buf->buffer,c->pix_fmt,c->width,c->height);
+ if (img_convert((AVPicture*)&pict,c->pix_fmt,(AVPicture*)&orig,r->input_pix_fmt,c->width,c->height) < 0) {
+ g_warning("img_convert failed");
+ return;
+ }
+ //if (pict.data[0]==NULL) g_error("img_convert failed.");
+ ms_message_set_buf(outm,r->yuv_buf);
+ }
+ else
+ {
+ avpicture_fill((AVPicture*)&pict,inm->data,c->pix_fmt,c->width,c->height);
+ ms_message_set_buf(outm,inm->buffer);
+ }
+ /* timestamp used by ffmpeg, unset here */
+ pict.pts=AV_NOPTS_VALUE;
+ error=avcodec_encode_video(c, r->comp_buf->buffer, r->comp_buf->size, &pict);
+ if (error<=0) ms_warning("ms_AVencoder_process: error %i.",error);
+ else ms_trace("ms_AVencoder_process: video encoding done");
+ if (r->q_outputs[1]!=NULL) ms_queue_put(r->q_outputs[1],outm);
+ else ms_message_destroy(outm);
+ ms_message_destroy(inm);
+}
+
+gint ms_AVencoder_set_format(MSAVEncoder *enc, gchar *fmt)
+{
+ gint format;
+ if (strcmp(fmt,"YUV420P")==0) format=PIX_FMT_YUV420P;
+ else if (strcmp(fmt,"YUV422")==0) format=PIX_FMT_YUV422;
+ else if (strcmp(fmt,"RGB24")==0) format=PIX_FMT_RGB24;
+ else if (strcmp(fmt,"BGR24")==0) format=PIX_FMT_BGR24;
+ else if (strcmp(fmt,"YUV422P")==0) format=PIX_FMT_YUV422P;
+ else if (strcmp(fmt,"YUV444P")==0) format=PIX_FMT_YUV444P;
+ else {
+ g_warning("ms_AVdecoder_set_format: unsupported format %s.",fmt);
+ return -1;
+ }
+ enc->input_pix_fmt=format;
+
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/msavencoder.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msavencoder.h
new file mode 100644
index 0000000..6fe5cad
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msbuffer.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msbuffer.c
new file mode 100644
index 0000000..4ca3c92
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msbuffer.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msbuffer.h
new file mode 100644
index 0000000..f96b35a
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mscodec.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mscodec.c
new file mode 100644
index 0000000..7677dbe
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mscodec.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mscodec.h
new file mode 100644
index 0000000..6c6847d
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mscopy.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mscopy.c
new file mode 100644
index 0000000..3040b2e
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mscopy.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mscopy.h
new file mode 100644
index 0000000..2b5749b
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msfdispatcher.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msfdispatcher.c
new file mode 100644
index 0000000..692bbb7
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msfdispatcher.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msfdispatcher.h
new file mode 100644
index 0000000..b1b457d
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msfifo.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msfifo.c
new file mode 100644
index 0000000..9897e08
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msfifo.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msfifo.h
new file mode 100644
index 0000000..fde1bec
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msfilter.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msfilter.c
new file mode 100644
index 0000000..c67e9f0
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msfilter.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msfilter.h
new file mode 100644
index 0000000..71ec81a
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msilbcdec.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msilbcdec.c
new file mode 100644
index 0000000..b2dfff9
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msilbcdec.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msilbcdec.h
new file mode 100644
index 0000000..c219aab
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msilbcenc.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msilbcenc.c
new file mode 100644
index 0000000..76d8b64
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msilbcenc.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msilbcenc.h
new file mode 100644
index 0000000..bd8f3bf
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msnosync.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msnosync.c
new file mode 100644
index 0000000..af5141c
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msnosync.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msnosync.h
new file mode 100644
index 0000000..eef52d4
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msossread.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msossread.c
new file mode 100644
index 0000000..2e7b032
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msossread.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msossread.h
new file mode 100644
index 0000000..89d5a40
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msosswrite.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msosswrite.c
new file mode 100644
index 0000000..13a0dfe
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msosswrite.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msosswrite.h
new file mode 100644
index 0000000..d477534
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msqdispatcher.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msqdispatcher.c
new file mode 100644
index 0000000..6bd073b
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msqdispatcher.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msqdispatcher.h
new file mode 100644
index 0000000..3b0c566
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msqueue.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msqueue.c
new file mode 100644
index 0000000..4636895
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msqueue.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msqueue.h
new file mode 100644
index 0000000..73ab8d8
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msread.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msread.c
new file mode 100644
index 0000000..6f0ec99
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msread.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msread.h
new file mode 100644
index 0000000..93177f3
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msringplayer.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msringplayer.c
new file mode 100644
index 0000000..fb2006e
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msringplayer.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msringplayer.h
new file mode 100644
index 0000000..1f5e67d
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msrtprecv.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msrtprecv.c
new file mode 100644
index 0000000..9b82e93
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msrtprecv.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msrtprecv.h
new file mode 100644
index 0000000..8c2c2ed
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msrtpsend.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msrtpsend.c
new file mode 100644
index 0000000..5e781ff
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msrtpsend.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msrtpsend.h
new file mode 100644
index 0000000..b70f4e5
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mssdlout.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mssdlout.c
new file mode 100644
index 0000000..8e49cd3
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/mssdlout.c
@@ -0,0 +1,303 @@
+/***************************************************************************
+ * mssdlout.c
+ *
+ * Mon Jul 11 16:17:59 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
+*/
+
+#include "mssdlout.h"
+
+MSSdlOutClass *ms_sdl_out_class=NULL;
+
+void ms_sdl_out_init(MSSdlOut *obj){
+ ms_filter_init(MS_FILTER(obj));
+ obj->width=VIDEO_SIZE_CIF_W;
+ obj->height=VIDEO_SIZE_CIF_H;
+ obj->format="RGB24";
+ obj->use_yuv=FALSE;
+ obj->oldinm1=NULL;
+ MS_FILTER(obj)->inqueues=obj->input;
+}
+
+void ms_sdl_out_set_format(MSSdlOut *obj, const char *fmt){
+ obj->format=fmt;
+ if (strcmp(fmt,"YUV420P")==0) obj->use_yuv=TRUE;
+ else obj->use_yuv=FALSE;
+}
+
+void ms_sdl_uninit_sdl(MSSdlOut *obj){
+ if (obj->overlay!=NULL){
+ SDL_FreeYUVOverlay(obj->overlay);
+ obj->overlay=NULL;
+ }
+ if (obj->screen!=NULL){
+ SDL_FreeSurface(obj->screen);
+ obj->screen=NULL;
+ }
+
+}
+
+void ms_sdl_out_uninit(MSSdlOut *obj){
+ ms_sdl_uninit_sdl(obj);
+}
+
+void ms_sdl_out_destroy(MSSdlOut *obj){
+ ms_sdl_out_uninit(obj);
+ if (obj->oldinm1!=NULL) ms_message_destroy(obj->oldinm1);
+ g_free(obj);
+}
+
+void ms_sdl_init_sdl(MSSdlOut *obj){
+ if (strcmp(obj->format,"RGB24")==0){
+ }else{
+ obj->use_yuv=TRUE;
+ }
+ obj->screen = SDL_SetVideoMode(obj->width, obj->height, 0,SDL_HWSURFACE|SDL_ANYFORMAT);
+ if ( obj->screen == NULL ) {
+ g_warning("Couldn't set video mode: %s\n",
+ SDL_GetError());
+ return ;
+ }
+ if (obj->screen->flags & SDL_HWSURFACE) g_message("SDL surface created in hardware");
+ SDL_WM_SetCaption("Linphone Video", NULL);
+
+ if (obj->use_yuv){
+ g_message("Using yuv overlay.");
+ obj->overlay=SDL_CreateYUVOverlay(obj->width,obj->height,SDL_IYUV_OVERLAY,obj->screen);
+ if (obj->overlay==NULL){
+ g_warning("Couldn't create yuv overlay: %s\n",
+ SDL_GetError());
+ }else{
+ if (obj->overlay->hw_overlay) g_message("YUV overlay using hardware acceleration.");
+ }
+ }
+
+}
+
+static void resize_yuv_small(char *pict, int w, int h, int scale){
+ int i,j,id,jd;
+ int nh,nw;
+ char *smallpict;
+ int ysize,usize,ydsize,udsize;
+ int smallpict_sz;
+ char *dptr,*sptr;
+ nw=w/scale;
+ nh=h/scale;
+ ysize=w*h;
+ usize=ysize/4;
+ ydsize=nw*nh;
+ udsize=ydsize/4;
+ smallpict_sz=(ydsize*3)/2;
+ smallpict=(char*)alloca(smallpict_sz);
+ memset(smallpict,0,smallpict_sz);
+
+
+ dptr=smallpict;
+ sptr=pict;
+ for (j=0,jd=0;j<nh;j++,jd+=scale){
+ for (i=0,id=0;i<nw;i++,id+=scale){
+ dptr[(j*nw) + i]=sptr[(jd*w)+id];
+ }
+ }
+
+ nh=nh/2;
+ nw=nw/2;
+ w=w/2;
+ h=h/2;
+ dptr+=ydsize;
+ sptr+=ysize;
+ for (j=0,jd=0;j<nh;j++,jd+=scale){
+ for (i=0,id=0;i<nw;i++,id+=scale){
+ dptr[(j*nw) + i]=sptr[(jd*w)+id];
+ }
+ }
+ dptr+=udsize;
+ sptr+=usize;
+ for (j=0,jd=0;j<nh;j++,jd+=scale){
+ for (i=0,id=0;i<nw;i++,id+=scale){
+ dptr[(j*nw) + i]=sptr[(jd*w)+id];
+ }
+ }
+
+ memcpy(pict,smallpict,smallpict_sz);
+}
+
+static void fill_overlay_at_pos(SDL_Overlay *lay, MSMessage *m, int x, int y, int w, int h){
+ char *data=(char*)m->data;
+ int i,j;
+ int jlim,ilim;
+ int off;
+ char *dptr;
+
+ ilim=MIN(x+w,lay->w);
+ jlim=MIN(y+h,lay->h);
+ SDL_LockYUVOverlay(lay);
+ /* set Y */
+ dptr=lay->pixels[0];
+ for (j=y;j<jlim;j++){
+ off=j*lay->w;
+ for (i=x;i<ilim;i++){
+ dptr[off + i]=*data;
+ data++;
+ }
+ }
+ /*set U and V*/
+ ilim=ilim/2;
+ jlim=jlim/2;
+ dptr=lay->pixels[1];
+ for (j=y/2;j<jlim;j++){
+ off=j*(lay->w/2);
+ for (i=x/2;i<ilim;i++){
+ dptr[off + i]=*data;
+ data++;
+ }
+ }
+ dptr=lay->pixels[2];
+ for (j=y/2;j<jlim;j++){
+ off=j*(lay->w/2);
+ for (i=x/2;i<ilim;i++){
+ dptr[off + i]=*data;
+ data++;
+ }
+ }
+ SDL_UnlockYUVOverlay(lay);
+}
+
+static void fill_overlay(SDL_Overlay *lay, MSMessage *m){
+
+ int w2,h2;
+ char *data=(char*)m->data;
+ int ysize=lay->w*lay->h;
+ int usize;
+ w2=lay->w/2;
+ h2=lay->h/2;
+ usize=w2*h2;
+ SDL_LockYUVOverlay(lay);
+ memcpy(lay->pixels[0],data,ysize);
+ memcpy(lay->pixels[1],data+ysize,usize);
+ memcpy(lay->pixels[2],data+ysize+usize,usize);
+ SDL_UnlockYUVOverlay(lay);
+}
+
+#define SCALE_FACTOR 6
+
+void ms_sdl_out_process(MSSdlOut *obj){
+ MSQueue *q0=obj->input[0];
+ MSQueue *q1=obj->input[1];
+ MSMessage *inm0=NULL;
+ MSMessage *inm1=NULL;
+ int err;
+ SDL_Rect smallrect;
+ SDL_Rect rect;
+ rect.w=obj->width;
+ rect.h=obj->height;
+ rect.x=0;
+ rect.y=0;
+ smallrect.w=obj->width/SCALE_FACTOR;
+ smallrect.h=obj->height/SCALE_FACTOR;
+ smallrect.x=obj->width - smallrect.w ;
+ smallrect.y=obj->height -smallrect.h;
+
+ if (obj->screen==NULL){
+ ms_sdl_init_sdl(obj);
+ }
+
+ if (q0!=NULL)
+ inm0=ms_queue_get(q0);
+ if (q1!=NULL)
+ inm1=ms_queue_get(q1);
+
+ if (inm0!=NULL){
+ SDL_Surface *surf;
+ if (obj->use_yuv){
+
+ fill_overlay(obj->overlay,inm0);
+
+ }else {
+ surf=SDL_CreateRGBSurfaceFrom(inm0->data,obj->width,obj->height,24,obj->width*3,0,0,0,0);
+
+ err=SDL_BlitSurface(surf,NULL,obj->screen,NULL);
+ if (err<0) g_warning("Fail to blit surface: %s",SDL_GetError());
+ SDL_FreeSurface(surf);
+ }
+ ms_message_destroy(inm0);
+ }
+ if (inm1!=NULL){
+ /* this message is blitted on the right,bottom corner of the screen */
+ SDL_Surface *surf;
+
+ if (obj->use_yuv){
+ resize_yuv_small(inm1->data,rect.w,rect.h,SCALE_FACTOR);
+ fill_overlay_at_pos(obj->overlay,inm1,smallrect.x, smallrect.y, smallrect.w, smallrect.h);
+ }else {
+ surf=SDL_CreateRGBSurfaceFrom(inm1->data,obj->width,obj->height,24,obj->width*3,0,0,0,0);
+
+ err=SDL_BlitSurface(surf,NULL,obj->screen,&smallrect);
+ if (err<0) g_warning("Fail to blit surface: %s",SDL_GetError());
+ SDL_FreeSurface(surf);
+ }
+ if (obj->oldinm1!=NULL) {
+ ms_message_destroy(obj->oldinm1);
+ }
+ obj->oldinm1=inm1;
+
+ }else{
+ /* this is the case were we have only inm0, we have to redisplay inm1 */
+ if (obj->use_yuv){
+ if (obj->oldinm1!=NULL){
+ fill_overlay_at_pos(obj->overlay,obj->oldinm1,smallrect.x, smallrect.y, smallrect.w, smallrect.h);
+ }
+ }
+ }
+
+ if (obj->use_yuv) SDL_DisplayYUVOverlay(obj->overlay,&rect);
+ SDL_UpdateRect(obj->screen,0,0,obj->width,obj->height);
+
+}
+
+void ms_sdl_out_class_init(MSSdlOutClass *klass){
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_sdl_out_process;
+ MS_FILTER_CLASS(klass)->max_qinputs=2;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_sdl_out_destroy;
+ MS_FILTER_CLASS(klass)->name="MSSdlOut";
+ /* Initialize the SDL library */
+ if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
+ fprintf(stderr,
+ "Couldn't initialize SDL: %s\n", SDL_GetError());
+ return;
+ }
+ /* Clean up on exit */
+ atexit(SDL_Quit);
+}
+
+MSFilter * ms_sdl_out_new(void){
+ MSSdlOut *obj=g_new0(MSSdlOut,1);
+ if (ms_sdl_out_class==NULL){
+ ms_sdl_out_class=g_new0(MSSdlOutClass,1);
+ ms_sdl_out_class_init(ms_sdl_out_class);
+ }
+ MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_sdl_out_class);
+ ms_sdl_out_init(obj);
+ return MS_FILTER(obj);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/mssdlout.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mssdlout.h
new file mode 100644
index 0000000..fd6ec54
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mssoundread.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mssoundread.c
new file mode 100644
index 0000000..3803b01
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mssoundread.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mssoundread.h
new file mode 100644
index 0000000..7f2cab9
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mssoundwrite.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mssoundwrite.c
new file mode 100644
index 0000000..9c5879f
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mssoundwrite.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mssoundwrite.h
new file mode 100644
index 0000000..e6d7987
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msspeexdec.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msspeexdec.c
new file mode 100644
index 0000000..f3fd4d2
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msspeexdec.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msspeexdec.h
new file mode 100644
index 0000000..d4e745f
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msspeexenc.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msspeexenc.c
new file mode 100644
index 0000000..abf976e
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msspeexenc.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msspeexenc.h
new file mode 100644
index 0000000..41655b9
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mssync.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mssync.c
new file mode 100644
index 0000000..9e32a8a
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mssync.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mssync.h
new file mode 100644
index 0000000..012c068
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mstimer.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mstimer.c
new file mode 100644
index 0000000..1955ef2
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mstimer.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mstimer.h
new file mode 100644
index 0000000..5c7e8ed
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechdecoder.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechdecoder.c
new file mode 100644
index 0000000..aba6e06
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechdecoder.c
@@ -0,0 +1,152 @@
+/*
+ Copyright 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
+*/
+
+
+#include "mstruespeechdecoder.h"
+#include "mscodec.h"
+
+MSCodecInfo TrueSpeechinfo =
+{
+ {
+ "TrueSpeech codec",
+ 0,
+ MS_FILTER_AUDIO_CODEC,
+ ms_truespeechencoder_new,
+ "This is a proprietary codec by the DSP Group that is used in some "
+ "Windows applications. It has a good quality and bitrate. "
+ "It requires the Windows DLLs tssoft32.acm and "
+ "tsd32.dll to be available."
+ },
+ ms_truespeechencoder_new,
+ ms_truespeechdecoder_new,
+ 480,
+ 32,
+ 8536,
+ 8000,
+ 116,
+ "TSP0",
+ 1,
+ 1,
+};
+
+
+static MSTrueSpeechDecoderClass *ms_truespeechdecoder_class = 0;
+
+/* FOR INTERNAL USE*/
+void ms_truespeechdecoder_init(MSTrueSpeechDecoder *r);
+void ms_truespeechdecoder_class_init(MSTrueSpeechDecoderClass *klass);
+void ms_truespeechdecoder_destroy(MSTrueSpeechDecoder *obj);
+void ms_truespeechdecoder_process(MSTrueSpeechDecoder *r);
+
+MSFilter * ms_truespeechdecoder_new(void)
+{
+ MSTrueSpeechDecoder *r = 0;
+
+ if (!ms_truespeechdecoder_class)
+ {
+ ms_truespeechdecoder_class = g_new(MSTrueSpeechDecoderClass, 1);
+ ms_truespeechdecoder_class_init(ms_truespeechdecoder_class);
+ }
+
+ r = g_new(MSTrueSpeechDecoder, 1);
+ MS_FILTER(r)->klass = MS_FILTER_CLASS(ms_truespeechdecoder_class);
+ ms_truespeechdecoder_init(r);
+ return MS_FILTER(r);
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_truespeechdecoder_init(MSTrueSpeechDecoder *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos = r->f_inputs;
+ MS_FILTER(r)->outfifos = r->f_outputs;
+
+ WAVEFORMATEX* wf = ms_truespeechencoder_wf_create();
+
+ r->codec = win32codec_create(wf, 0);
+ free(wf);
+
+ MS_FILTER(r)->r_mingran = r->codec->min_insize;
+
+ MS_FILTER_CLASS(ms_truespeechdecoder_class)->r_maxgran =
+ r->codec->min_insize;
+ MS_FILTER_CLASS(ms_truespeechdecoder_class)->w_maxgran =
+ r->codec->min_outsize;
+
+ memset(r->f_inputs, 0, sizeof(MSFifo*) * MS_TRUESPEECH_CODEC_MAX_IN_OUT);
+ memset(r->f_outputs, 0, sizeof(MSFifo*) * MS_TRUESPEECH_CODEC_MAX_IN_OUT);
+}
+
+void ms_truespeechdecoder_class_init(MSTrueSpeechDecoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass), "TrueSpeechDecoder");
+ MS_FILTER_CLASS(klass)->max_finputs = MS_TRUESPEECH_CODEC_MAX_IN_OUT;
+ MS_FILTER_CLASS(klass)->max_foutputs = MS_TRUESPEECH_CODEC_MAX_IN_OUT;
+ MS_FILTER_CLASS(klass)->r_maxgran = 0; /* filled in by first instance */
+ MS_FILTER_CLASS(klass)->w_maxgran = 0; /* filled in by first instance */
+ MS_FILTER_CLASS(klass)->destroy = (MSFilterDestroyFunc)ms_truespeechdecoder_destroy;
+ MS_FILTER_CLASS(klass)->process = (MSFilterProcessFunc)ms_truespeechdecoder_process;
+ MS_FILTER_CLASS(klass)->info = MS_FILTER_INFO(&TrueSpeechinfo);
+ klass->driver = win32codec_create_driver(TRUESPEECH_DLL,
+ TRUESPEECH_FORMAT_TAG, 0);
+}
+
+void ms_truespeechdecoder_process(MSTrueSpeechDecoder *r)
+{
+ MSFifo *fi,*fo;
+ gint err1;
+ 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)
+ {
+ err1 = ms_fifo_get_read_ptr(fi, r->codec->min_insize, &s);
+ if (err1 > 0)
+ {
+ err1 = ms_fifo_get_write_ptr(fo, r->codec->min_outsize, &d);
+ if (d)
+ {
+ signed long n;
+ n = win32codec_convert(r->codec,
+ s, r->codec->min_insize,
+ d, r->codec->min_outsize);
+ }
+ }
+
+ }
+}
+
+
+
+void ms_truespeechdecoder_uninit(MSTrueSpeechDecoder *obj)
+{
+ win32codec_destroy(obj->codec);
+}
+
+void ms_truespeechdecoder_destroy(MSTrueSpeechDecoder *obj)
+{
+ ms_truespeechdecoder_uninit(obj);
+ g_free(obj);
+}
+
+
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechdecoder.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechdecoder.h
new file mode 100644
index 0000000..6247743
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechencoder.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechencoder.c
new file mode 100644
index 0000000..0b00c0c
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechencoder.c
@@ -0,0 +1,161 @@
+/*
+ Copyright 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
+*/
+
+
+#include "mstruespeechencoder.h"
+#include "mscodec.h"
+
+#define TRUESPEECH_CBSIZE 32
+
+extern MSCodecInfo TrueSpeechinfo;
+
+static MSTrueSpeechEncoderClass *ms_truespeechencoder_class = 0;
+
+/* FOR INTERNAL USE*/
+void ms_truespeechencoder_init(MSTrueSpeechEncoder *r);
+void ms_truespeechencoder_class_init(MSTrueSpeechEncoderClass *klass);
+void ms_truespeechencoder_destroy(MSTrueSpeechEncoder *obj);
+void ms_truespeechencoder_process(MSTrueSpeechEncoder *r);
+
+MSFilter * ms_truespeechencoder_new(void)
+{
+ MSTrueSpeechEncoder *r = 0;
+
+ if (!ms_truespeechencoder_class)
+ {
+ ms_truespeechencoder_class = g_new(MSTrueSpeechEncoderClass, 1);
+ ms_truespeechencoder_class_init(ms_truespeechencoder_class);
+ }
+
+ r = g_new(MSTrueSpeechEncoder, 1);
+ MS_FILTER(r)->klass = MS_FILTER_CLASS(ms_truespeechencoder_class);
+ ms_truespeechencoder_init(r);
+ return MS_FILTER(r);
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_truespeechencoder_init(MSTrueSpeechEncoder *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos = r->f_inputs;
+ MS_FILTER(r)->outfifos = r->f_outputs;
+
+ WAVEFORMATEX* wf = ms_truespeechencoder_wf_create();
+
+ r->codec = win32codec_create(wf, 1);
+ free(wf);
+
+ MS_FILTER(r)->r_mingran = r->codec->min_insize;
+
+ MS_FILTER_CLASS(ms_truespeechencoder_class)->r_maxgran =
+ r->codec->min_insize;
+ MS_FILTER_CLASS(ms_truespeechencoder_class)->w_maxgran =
+ r->codec->min_outsize;
+
+ memset(r->f_inputs, 0, sizeof(MSFifo*) * MS_TRUESPEECH_CODEC_MAX_IN_OUT);
+ memset(r->f_outputs, 0, sizeof(MSFifo*) * MS_TRUESPEECH_CODEC_MAX_IN_OUT);
+}
+
+void ms_truespeechencoder_class_init(MSTrueSpeechEncoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass), "TrueSpeechEncoder");
+ MS_FILTER_CLASS(klass)->max_finputs = MS_TRUESPEECH_CODEC_MAX_IN_OUT;
+ MS_FILTER_CLASS(klass)->max_foutputs = MS_TRUESPEECH_CODEC_MAX_IN_OUT;
+ MS_FILTER_CLASS(klass)->r_maxgran = 0; /* filled in by first instance */
+ MS_FILTER_CLASS(klass)->w_maxgran = 0; /* filled in by first instance */
+ MS_FILTER_CLASS(klass)->destroy = (MSFilterDestroyFunc)ms_truespeechencoder_destroy;
+ MS_FILTER_CLASS(klass)->process = (MSFilterProcessFunc)ms_truespeechencoder_process;
+ MS_FILTER_CLASS(klass)->info = MS_FILTER_INFO(&TrueSpeechinfo);
+ klass->driver = win32codec_create_driver(TRUESPEECH_DLL,
+ TRUESPEECH_FORMAT_TAG, 1);
+}
+
+void ms_truespeechencoder_process(MSTrueSpeechEncoder *r)
+{
+ MSFifo *fi,*fo;
+ int err1;
+ 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)
+ {
+ err1 = ms_fifo_get_read_ptr(fi, r->codec->min_insize, &s);
+ if (err1 > 0)
+ {
+ err1 = ms_fifo_get_write_ptr(fo, r->codec->min_outsize, &d);
+ if (d)
+ {
+ signed long n;
+
+ n = win32codec_convert(r->codec,
+ s, r->codec->min_insize,
+ d, r->codec->min_outsize);
+ }
+ }
+
+ }
+}
+
+
+
+void ms_truespeechencoder_uninit(MSTrueSpeechEncoder *obj)
+{
+ win32codec_destroy(obj->codec);
+}
+
+void ms_truespeechencoder_destroy(MSTrueSpeechEncoder *obj)
+{
+ ms_truespeechencoder_uninit(obj);
+ g_free(obj);
+}
+
+
+WAVEFORMATEX* ms_truespeechencoder_wf_create()
+{
+ WAVEFORMATEX* ts_wf = 0;
+ long* iptr = 0;
+
+ ts_wf = malloc(sizeof(WAVEFORMATEX) + TRUESPEECH_CBSIZE);
+ if (!ts_wf)
+ {
+ return 0;
+ }
+
+ memset(ts_wf, 0, sizeof(*ts_wf) + TRUESPEECH_CBSIZE);
+
+ ts_wf->wFormatTag = TRUESPEECH_FORMAT_TAG;
+ ts_wf->nChannels = 1;
+ ts_wf->nSamplesPerSec = 8000;
+ ts_wf->wBitsPerSample = 1;
+ ts_wf->nBlockAlign = 32;
+ ts_wf->nAvgBytesPerSec = 1067;
+ ts_wf->cbSize = TRUESPEECH_CBSIZE;
+
+ /* write extra data needed by TrueSpeech codec found
+ from examining a TrueSpeech .wav file header
+ */
+ iptr = (long*)(ts_wf + 1);
+ *iptr = 0x00f00001;
+
+ return ts_wf;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechencoder.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mstruespeechencoder.h
new file mode 100644
index 0000000..04e40bb
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msutils.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msutils.h
new file mode 100644
index 0000000..012b87d
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msv4l.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msv4l.c
new file mode 100644
index 0000000..4534d1f
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/msv4l.c
@@ -0,0 +1,530 @@
+/*
+ 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 "msv4l.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+
+char *v4l_palette_string[17]={
+ "none",
+ "GREY", /* Linear greyscale */
+ "HI240", /* High 240 cube (BT848) */
+ "RGB565", /* 565 16 bit RGB */
+ "RGB24", /* 24bit RGB */
+ "RGB32", /* 32bit RGB */
+ "RGB555", /* 555 15bit RGB */
+ "YUV422", /* YUV422 capture */
+ "YUYV",
+ "UYVY", /* The great thing about standards is ... */
+ "YUV420",
+ "YUV411", /* YUV411 capture */
+ "RAW", /* RAW capture (BT848) */
+ "YUV422P", /* YUV 4:2:2 Planar */
+ "YUV411P", /* YUV 4:1:1 Planar */
+ "YUV420P", /* YUV 4:2:0 Planar */
+ "YUV410P", /* YUV 4:1:0 Planar */
+};
+
+#define V4L_PALETTE_TO_STRING(pal) v4l_palette_string[(pal)]
+
+MSFilterInfo v4l_info=
+{
+ "Video4Linux",
+ 0,
+ MS_FILTER_VIDEO_IO,
+ ms_v4l_new,
+ NULL
+};
+
+
+static MSV4lClass *ms_v4l_class=NULL;
+
+MSFilter * ms_v4l_new()
+{
+ MSV4l *obj;
+ obj=g_malloc0(sizeof(MSV4l));
+ if (ms_v4l_class==NULL)
+ {
+ ms_v4l_class=g_malloc0(sizeof(MSV4lClass));
+ ms_v4l_class_init(ms_v4l_class);
+ }
+ MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_v4l_class);
+ ms_v4l_init(obj);
+ return MS_FILTER(obj);
+}
+
+void ms_v4l_init(MSV4l *obj)
+{
+ ms_video_source_init(MS_VIDEO_SOURCE(obj));
+ /* initialize the static buffer */
+ obj->use_mmap=0;
+ obj->fd=-1;
+ obj->device = g_strdup("/dev/video0");
+ obj->count=0;
+ obj->allocdbuf=NULL;
+ obj->grab_image=FALSE;
+ obj->image_grabbed=NULL;
+ obj->cond=g_cond_new();
+ obj->stopcond=g_cond_new();
+ obj->v4lthread=NULL;
+ obj->thread_exited=FALSE;
+ obj->frame=0;
+ MS_VIDEO_SOURCE(obj)->format="RGB24"; /*default value */
+ MS_VIDEO_SOURCE(obj)->width = VIDEO_SIZE_CIF_W; /*default value */
+ MS_VIDEO_SOURCE(obj)->height = VIDEO_SIZE_CIF_H; /*default value */
+}
+
+void ms_v4l_class_init(MSV4lClass *klass)
+{
+ ms_video_source_class_init(MS_VIDEO_SOURCE_CLASS(klass));
+ MS_VIDEO_SOURCE_CLASS(klass)->start=(void (*)(MSVideoSource *))ms_v4l_start;
+ MS_VIDEO_SOURCE_CLASS(klass)->stop=(void (*)(MSVideoSource *))ms_v4l_stop;
+ MS_VIDEO_SOURCE_CLASS(klass)->set_device=(int (*)(MSVideoSource*,const gchar*))ms_v4l_set_device;
+ MS_FILTER_CLASS(klass)->process=(void (*)(MSFilter *))v4l_process;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_v4l_destroy;
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"msv4l");
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&v4l_info;
+}
+
+void *v4l_thread(MSV4l *obj);
+
+void ms_v4l_start(MSV4l *obj)
+{
+ int err;
+ ms_filter_lock(MS_FILTER(obj));
+ obj->fd=open(obj->device,O_RDONLY);
+ if (obj->fd<0)
+ {
+ g_warning("MSV4l: cannot open video device: %s.",strerror(errno));
+ }else{
+ err=v4l_configure(obj);
+ if (err<0)
+ {
+ g_warning("MSV4l: could not get configuration of video device");
+ }
+ }
+ obj->thread_exited=FALSE;
+ obj->v4lthread=g_thread_create((GThreadFunc)v4l_thread,(gpointer)obj,FALSE,NULL);
+ while(!obj->thread_run) g_cond_wait(obj->cond,MS_FILTER(obj)->lock);
+ ms_filter_unlock(MS_FILTER(obj));
+}
+
+void ms_v4l_stop(MSV4l *obj)
+{
+ ms_filter_lock(MS_FILTER(obj));
+ obj->thread_run=FALSE;
+ obj->grab_image=FALSE;
+ g_cond_signal(obj->cond);
+ if (obj->fd>0)
+ {
+ close(obj->fd);
+ obj->fd=-1;
+ if (!obj->use_mmap){
+ if (obj->allocdbuf!=NULL) ms_buffer_destroy(obj->allocdbuf);
+ else obj->allocdbuf=NULL;
+ }else
+ {
+ munmap(obj->mmapdbuf,obj->vmbuf.size);
+ obj->mmapdbuf=NULL;
+ }
+ obj->image_grabbed=NULL;
+ }
+ while(!obj->thread_exited) g_cond_wait(obj->stopcond,MS_FILTER(obj)->lock);
+ obj->v4lthread=NULL;
+ ms_filter_unlock(MS_FILTER(obj));
+}
+
+gint ms_v4l_get_width(MSV4l *v4l)
+{
+ return v4l->win.width;
+}
+
+gint ms_v4l_get_height(MSV4l *v4l)
+{
+ return v4l->win.height;
+}
+
+int ms_v4l_set_device(MSV4l *obj, const gchar *device)
+{
+ if (obj->device!=NULL) g_free(obj->device);
+ obj->device=g_strdup(device);
+ return 0;
+}
+
+void ms_v4l_set_size(MSV4l *obj, gint width, gint height)
+{
+ gint err;
+ gboolean restart = FALSE;
+
+ if (obj->fd == -1)
+ {
+ obj->fd = open(obj->device, O_RDONLY);
+ if (obj->fd < 0)
+ {
+ g_warning("MSV4l: cannot open video device: %s.",strerror(errno));
+ return;
+ }
+ } else
+ restart = TRUE;
+
+ ms_filter_lock(MS_FILTER(obj));
+ err = ioctl(obj->fd, VIDIOCGCAP, &obj->cap);
+ if (err != 0)
+ {
+ g_warning("MSV4l: cannot get device capabilities: %s.",strerror(errno));
+ return;
+ }
+ if (width <= obj->cap.maxwidth && width >= obj->cap.minwidth &&
+ height <= obj->cap.maxheight && height >= obj->cap.minheight)
+ {
+ MS_VIDEO_SOURCE(obj)->width = width;
+ MS_VIDEO_SOURCE(obj)->height = height;
+ }
+ ms_filter_unlock(MS_FILTER(obj));
+
+ if (restart)
+ {
+ ms_v4l_stop(obj);
+ ms_v4l_start(obj);
+ }
+
+}
+
+int v4l_configure(MSV4l *f)
+{
+ gint err;
+ gint i;
+ struct video_channel *chan=&f->channel;
+ struct video_window *win=&f->win;
+ struct video_picture *pict=&f->pict;
+ struct video_mmap *vmap=&f->vmap;
+ struct video_mbuf *vmbuf=&f->vmbuf;
+ struct video_capture *vcap=&f->vcap;
+ int found=0;
+
+ err=ioctl(f->fd,VIDIOCGCAP,&f->cap);
+ if (err!=0)
+ {
+ g_warning("MSV4l: cannot get device capabilities: %s.",strerror(errno));
+ return -1;
+ }
+ MS_VIDEO_SOURCE(f)->dev_name=f->cap.name;
+
+ for (i=0;i<f->cap.channels;i++)
+ {
+ chan->channel=i;
+ err=ioctl(f->fd,VIDIOCGCHAN,chan);
+ if (err==0)
+ {
+ g_message("Getting video channel %s",chan->name);
+ switch(chan->type){
+ case VIDEO_TYPE_TV:
+ g_message("Channel is a TV.");
+ break;
+ case VIDEO_TYPE_CAMERA:
+ g_message("Channel is a camera");
+ break;
+ default:
+ g_warning("unknown video channel type.");
+ }
+ found=1;
+ break; /* find the first channel */
+ }
+ }
+ if (found) g_message("A valid video channel was found.");
+ /* select this channel */
+ ioctl(f->fd,VIDIOCSCHAN,chan);
+
+ /* set/get the resolution */
+ err = -1;
+ /*
+ if (f->cap.type & VID_TYPE_SUBCAPTURE) {
+ vcap->x = vcap->y = 0;
+ vcap->width = MS_VIDEO_SOURCE(f)->width;
+ vcap->height = MS_VIDEO_SOURCE(f)->height;
+ err = ioctl(f->fd, VIDIOCSCAPTURE, vcap);
+ if (err > -1) {
+ f->width = MS_VIDEO_SOURCE(f)->width;
+ f->height = MS_VIDEO_SOURCE(f)->height;
+ }
+ }
+ */
+ if (err < 0) {
+ win->x = win->y = 0;
+ win->width = MS_VIDEO_SOURCE(f)->width;
+ win->height = MS_VIDEO_SOURCE(f)->height;
+ win->clipcount = win->flags = 0;
+ win->clips = NULL;
+ err=ioctl(f->fd,VIDIOCSWIN,win);
+ if (err < 0) {
+ g_warning("Could not set video window properties: %s",strerror(errno));
+
+ err=ioctl(f->fd,VIDIOCGWIN,win);
+ if (err < 0) {
+ g_warning("Could not set video window properties: %s",strerror(errno));
+ return -1;
+ }
+ f->width = win->width;
+ f->height = win->height;
+ }
+ else {
+ f->width = MS_VIDEO_SOURCE(f)->width;
+ f->height = MS_VIDEO_SOURCE(f)->height;
+ }
+ }
+
+ /* get picture properties */
+ err=ioctl(f->fd,VIDIOCGPICT,pict);
+ if (err<0){
+ g_warning("Could not get picture properties: %s",strerror(errno));
+ return -1;
+ }
+ g_message("Picture properties: depth=%i, palette=%i.",pict->depth, pict->palette);
+ f->bsize=(pict->depth/8) * f->height * f->width;
+
+ /* try to get mmap properties */
+ err=ioctl(f->fd,VIDIOCGMBUF,vmbuf);
+ if (err<0){
+ g_warning("Could not get mmap properties: %s",strerror(errno));
+ f->use_mmap=0;
+ }else
+ {
+ if (vmbuf->size>0){
+ f->use_mmap=1;
+ /* do the mmap */
+ f->mmapdbuf=mmap((void*)f,vmbuf->size,PROT_READ,MAP_PRIVATE,f->fd,0);
+ if (f->mmapdbuf==(void*)-1) {
+ g_warning("Could not mmap. Using read instead.");
+ f->use_mmap=0;
+ f->mmapdbuf=NULL;
+ }else {
+ /* initialize the mediastreamer buffers */
+ gint i;
+ g_message("Using %i-frames mmap'd buffer.",vmbuf->frames);
+ for(i=0;i<vmbuf->frames;i++){
+ f->img[i].buffer=f->mmapdbuf+vmbuf->offsets[i];
+ f->img[i].size=f->bsize;
+ f->img[i].ref_count=1;
+ }
+ f->frame=0;
+ }
+ } else g_warning("This device cannot support mmap.");
+ }
+
+ /* initialize the video map structure */
+ vmap->width=win->width;
+ vmap->height=win->height;
+ vmap->format=pict->palette;
+ vmap->frame=0;
+
+ MS_VIDEO_SOURCE(f)->format=V4L_PALETTE_TO_STRING(pict->palette);
+ return 0;
+}
+
+#define BPP 3
+static inline
+void crop( guchar *src, gint s_width, gint s_height, guchar *dest, gint d_width, gint d_height)
+{
+ register int i;
+ register int stride = d_width*BPP;
+ register guchar *s = src, *d = dest;
+ s += ((s_height - d_height)/2 * s_width * BPP) + ((s_width - d_width)/2 * BPP);
+ for (i = 0; i < d_height; i++, d += stride, s += s_width * BPP)
+ memcpy( d, s, stride);
+}
+
+MSBuffer * v4l_grab_image_mmap(MSV4l *obj){
+ struct video_mmap *vmap=&obj->vmap;
+ struct video_mbuf *vmbuf=&obj->vmbuf;
+ int err;
+ int syncframe;
+ int jitter=vmbuf->frames-1;
+ obj->query_frame=(obj->frame) % vmbuf->frames;
+ ms_trace("v4l_mmap_process: query_frame=%i",
+ obj->query_frame);
+ vmap->frame=obj->query_frame;
+ err=ioctl(obj->fd,VIDIOCMCAPTURE,vmap);
+ if (err<0) {
+ g_warning("v4l_mmap_process: error in VIDIOCMCAPTURE: %s.",strerror(errno));
+ return NULL;
+ }
+ syncframe=(obj->frame-jitter);
+ obj->frame++;
+ if (syncframe>=0){
+ syncframe=syncframe%vmbuf->frames;
+ err=ioctl(obj->fd,VIDIOCSYNC,&syncframe);
+ if (err<0) {
+ g_warning("v4l_mmap_process: error in VIDIOCSYNC: %s.",strerror(errno));
+ return NULL;
+ }
+ }else {
+ return NULL;
+ }
+ /* not particularly efficient - hope for a capture source that
+ provides subcapture or setting window */
+
+ if (obj->width != MS_VIDEO_SOURCE(obj)->width || obj->height != MS_VIDEO_SOURCE(obj)->height){
+ guchar tmp[obj->bsize];
+ crop((guchar*) obj->img[syncframe].buffer, obj->width, obj->height, tmp,
+ MS_VIDEO_SOURCE(obj)->width, MS_VIDEO_SOURCE(obj)->height);
+ memcpy(obj->img[syncframe].buffer, tmp, MS_VIDEO_SOURCE(obj)->width *
+ MS_VIDEO_SOURCE(obj)->height * obj->pict.depth/8);
+ }
+ return &obj->img[syncframe];
+}
+
+MSBuffer *v4l_grab_image_read(MSV4l *obj){
+ int err;
+ if (obj->allocdbuf==NULL){
+ obj->allocdbuf=ms_buffer_new(obj->bsize);
+ obj->allocdbuf->ref_count++;
+ }
+ if (obj->width != MS_VIDEO_SOURCE(obj)->width || obj->height != MS_VIDEO_SOURCE(obj)->height)
+ {
+ guchar tmp[obj->bsize];
+ err=read(obj->fd,tmp,obj->bsize);
+ if (err>0)
+ crop(tmp, obj->width, obj->height, obj->allocdbuf->buffer, MS_VIDEO_SOURCE(obj)->width, MS_VIDEO_SOURCE(obj)->height);
+ else {
+ g_warning("MSV4l: Fail to read(): %s",strerror(errno));
+ return NULL;
+ }
+ }
+ else
+ {
+ err=read(obj->fd,obj->allocdbuf->buffer,obj->bsize);
+ if (err<0){
+ g_warning("MSV4l: Fail to read(): %s",strerror(errno));
+ return NULL;
+ }
+ }
+ return obj->allocdbuf;
+}
+
+
+MSBuffer * v4l_make_mire(MSV4l *obj){
+ gchar *data;
+ int i,j,line,pos;
+ int patternw=obj->parent.width/6;
+ int patternh=obj->parent.height/6;
+ int red,green=0,blue=0;
+ if (obj->allocdbuf==NULL){
+ obj->allocdbuf=ms_buffer_new(obj->parent.width*obj->parent.height*3);
+ obj->allocdbuf->ref_count++;
+ }
+ data=obj->allocdbuf->buffer;
+ for (i=0;i<obj->parent.height;++i){
+ line=i*obj->parent.width*3;
+ if ( ((i+obj->count)/patternh) & 0x1) red=255;
+ else red= 0;
+ for (j=0;j<obj->parent.width;++j){
+ int tmp;
+ pos=line+(j*3);
+
+ if ( ((j+obj->count)/patternw) & 0x1) blue=255;
+ else blue= 0;
+
+ data[pos]=red;
+ data[pos+1]=green;
+ data[pos+2]=blue;
+ }
+ }
+ obj->count++;
+ usleep(60000);
+ return obj->allocdbuf;
+}
+
+
+void *v4l_thread(MSV4l *obj){
+ GMutex *mutex=MS_FILTER(obj)->lock;
+ g_mutex_lock(mutex);
+ obj->thread_run=TRUE;
+ g_cond_signal(obj->cond);
+ while(obj->thread_run){
+ g_cond_wait(obj->cond,mutex);
+ if (obj->grab_image){
+ MSBuffer *grabbed;
+ g_mutex_unlock(mutex);
+ if (obj->fd>0){
+ if (obj->use_mmap){
+ grabbed=v4l_grab_image_mmap(obj);
+ }else{
+ grabbed=v4l_grab_image_read(obj);
+ }
+ }else grabbed=v4l_make_mire(obj);
+ g_mutex_lock(mutex);
+ if (grabbed){
+ obj->image_grabbed=grabbed;
+ obj->grab_image=FALSE;
+ }
+ }
+ }
+ g_cond_signal(obj->stopcond);
+ obj->thread_exited=TRUE;
+ g_mutex_unlock(mutex);
+ return NULL;
+}
+
+
+
+
+void v4l_process(MSV4l * obj)
+{
+ GMutex *mutex=MS_FILTER(obj)->lock;
+ g_mutex_lock(mutex);
+ if (obj->image_grabbed!=NULL){
+ MSMessage *m=ms_message_alloc();
+ ms_message_set_buf(m,obj->image_grabbed);
+ ms_queue_put(MS_FILTER(obj)->outqueues[0],m);
+ obj->image_grabbed=NULL;
+ }else{
+ obj->grab_image=TRUE;
+ g_cond_signal(obj->cond);
+ }
+ g_mutex_unlock(mutex);
+}
+
+void ms_v4l_uninit(MSV4l *obj)
+{
+ if (obj->device!=NULL) {
+ g_free(obj->device);
+ obj->device=NULL;
+ }
+ if (obj->v4lthread!=NULL) ms_v4l_stop(obj);
+ if (obj->allocdbuf!=NULL) {
+ ms_buffer_destroy(obj->allocdbuf);
+ obj->allocdbuf=NULL;
+ }
+ g_cond_free(obj->cond);
+ g_cond_free(obj->stopcond);
+ ms_filter_uninit(MS_FILTER(obj));
+}
+
+void ms_v4l_destroy(MSV4l *obj)
+{
+ ms_v4l_uninit(obj);
+ g_free(obj);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/msv4l.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msv4l.h
new file mode 100644
index 0000000..e19ac9e
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/msvideosource.c b/third_party/libjingle/source/talk/third_party/mediastreamer/msvideosource.c
new file mode 100644
index 0000000..b3a9f12
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/msvideosource.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 "msvideosource.h"
+#include "mediastream.h"
+#include "msv4l.h"
+#ifdef HAVE_LIBDC1394
+#include "msdc1394.h"
+#endif
+
+/* register all statically linked codecs */
+void ms_video_source_register_all()
+{
+ ms_filter_register(&v4l_info);
+#ifdef HAVE_LIBDC1394
+ ms_filter_register(MS_FILTER_INFO(&dc1394_info));
+#endif
+}
+
+void ms_video_source_class_init(MSVideoSourceClass *klass)
+{
+ /* init base class first*/
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ /* then init videosource specific things*/
+ MS_FILTER_CLASS(klass)->max_qoutputs=MSVIDEOSOURCE_MAX_OUTPUTS;
+ ms_filter_class_set_attr(MS_FILTER_CLASS(klass),FILTER_IS_SOURCE|FILTER_HAS_QUEUES);
+}
+
+void ms_video_source_init(MSVideoSource *obj)
+{
+ ms_filter_init(MS_FILTER(obj));
+ MS_FILTER(obj)->outqueues=obj->outputs;
+ obj->width = VIDEO_SIZE_CIF_W;
+ obj->height = VIDEO_SIZE_CIF_H;
+}
+
+void ms_video_source_start(MSVideoSource *f)
+{
+ MS_VIDEO_SOURCE_CLASS(MS_FILTER(f)->klass)->start(f);
+}
+
+void ms_video_source_stop(MSVideoSource *f)
+{
+ MS_VIDEO_SOURCE_CLASS(MS_FILTER(f)->klass)->stop(f);
+}
+
+int ms_video_source_set_device(MSVideoSource *f, const gchar *device)
+{
+ return MS_VIDEO_SOURCE_CLASS(MS_FILTER(f)->klass)->set_device(f,device);
+}
+
+gchar* ms_video_source_get_device_name(MSVideoSource *f)
+{
+ return f->dev_name;
+}
+
+void ms_video_source_set_size(MSVideoSource *f, gint width, gint height)
+{
+ if (MS_VIDEO_SOURCE_CLASS(MS_FILTER(f)->klass)->set_size)
+ MS_VIDEO_SOURCE_CLASS(MS_FILTER(f)->klass)->set_size(f, width, height);
+}
+
+void ms_video_source_set_frame_rate(MSVideoSource *f, gint frame_rate, gint frame_rate_base)
+{
+ if (MS_VIDEO_SOURCE_CLASS(MS_FILTER(f)->klass)->set_frame_rate)
+ MS_VIDEO_SOURCE_CLASS(MS_FILTER(f)->klass)->set_frame_rate(f, frame_rate, frame_rate_base);
+ else{
+ f->frame_rate=frame_rate;
+ f->frame_rate_base=frame_rate_base;
+ }
+}
+
+gchar* ms_video_source_get_format(MSVideoSource *f)
+{
+ return f->format;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/msvideosource.h b/third_party/libjingle/source/talk/third_party/mediastreamer/msvideosource.h
new file mode 100644
index 0000000..9a27f83
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mswrite.c b/third_party/libjingle/source/talk/third_party/mediastreamer/mswrite.c
new file mode 100644
index 0000000..178e294
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/mswrite.h b/third_party/libjingle/source/talk/third_party/mediastreamer/mswrite.h
new file mode 100644
index 0000000..cd766d1
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/osscard.c b/third_party/libjingle/source/talk/third_party/mediastreamer/osscard.c
new file mode 100644
index 0000000..636c579
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/osscard.h b/third_party/libjingle/source/talk/third_party/mediastreamer/osscard.h
new file mode 100644
index 0000000..30b96c2
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/recvrtp.c b/third_party/libjingle/source/talk/third_party/mediastreamer/recvrtp.c
new file mode 100644
index 0000000..eaad77f
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/recvrtp.c
@@ -0,0 +1,109 @@
+#include "msrtprecv.h"
+#include "ms.h"
+#include "mswrite.h"
+#include "msosswrite.h"
+#include "msMUlawdec.h"
+#include "mstimer.h"
+#include "msfdispatcher.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static int cond=1;
+
+void usage(){
+ printf(
+ "\nUsage: \ntest_rtprecv <output_file> <address> <port>\n"
+ "<output_file> is a file to log the flow\n"
+ "<address> is a local ip address to listen\n"
+ "<port> is a local udp port to listen\n"
+ );
+
+}
+
+void stop_handler(int signum){
+ cond=0;
+}
+
+int main(int argc, char *argv[]){
+ MSFilter *play,*dec,*rec;
+ MSSync *timer;
+ RtpSession *rtps;
+ int port;
+
+ if (argc < 4){
+ usage();
+ exit(0);
+ }
+ port = atoi(argv[3]);
+
+ printf("#################################\n"
+ "Test Program\n"
+ "Receiving RTP flow with oRTP\n"
+ "#################################\n"
+ );
+ /*create the rtp session */
+ ortp_init();
+ ortp_set_debug_file("oRTP",NULL);
+ rtps=rtp_session_new(RTP_SESSION_RECVONLY);
+ rtp_session_set_local_addr(rtps,argv[2],port);
+ rtp_session_set_scheduling_mode(rtps,0);
+ rtp_session_set_blocking_mode(rtps,0);
+
+ printf(
+ "##########################################################################\n"
+ "Inicialized to listen to the %s (local address) in the port %d\n"
+ "##########################################################################\n", argv[2], port);
+ ms_init();
+ signal(SIGINT,stop_handler);
+
+ play=ms_rtp_recv_new();
+ rec=ms_oss_write_new();
+ ms_sound_write_set_device(MS_SOUND_WRITE(rec),0);
+ dec=ms_MULAWdecoder_new();
+ timer=ms_timer_new();
+
+ ms_rtp_recv_set_session(MS_RTP_RECV(play),rtps);
+
+ ms_filter_add_link(play,dec);
+ ms_filter_add_link(dec,rec);
+ ms_sync_attach(timer,play);
+ printf(
+ "############\n"
+ "gran=%i\n"
+ "############\n",MS_SYNC(timer)->samples_per_tick);
+
+ ms_start(timer);
+ ms_sound_write_start(MS_SOUND_WRITE(rec));
+ while(cond)
+ {
+ sleep(1);
+ }
+
+ printf(
+ "#################################\n"
+ "stoping sync...\n"
+ "#################################\n");
+ ms_stop(timer);
+ ms_sound_write_stop(MS_SOUND_WRITE(rec));
+ printf(
+ "#################################\n"
+ "unlinking filters...\n"
+ "#################################\n");
+ ms_filter_remove_links(play,dec);
+ ms_filter_remove_links(dec,rec);
+ printf( "#################################\n"
+ "destroying filters...\n"
+ "#################################\n");
+ ms_filter_destroy(play);
+ ms_filter_destroy(dec);
+ ms_filter_destroy(rec);
+
+ rtp_session_destroy(rtps);
+ ms_sync_destroy(timer);
+ ortp_global_stats_display();
+
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/ring_test.c b/third_party/libjingle/source/talk/third_party/mediastreamer/ring_test.c
new file mode 100644
index 0000000..46afcfb
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/ring_test.c
@@ -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
+*/
+
+#include "ms.h"
+
+#include "msringplayer.h"
+#include "msosswrite.h"
+#include "msossread.h"
+#include "mscopy.h"
+#include "mstimer.h"
+#include <unistd.h>
+
+
+#define READFILE "../share/ring.wav"
+#define WRITEFILE "/tmp/mediaout"
+
+int main()
+{
+ MSFilter *play,*copy,*rec;
+ MSSync *timer;
+ int i=0;
+ SndCard *card;
+ ms_init();
+
+ card=snd_card_manager_get_card(snd_card_manager,1);
+ play=ms_ring_player_new(READFILE,2);
+ //play=ms_oss_read_new(0);
+ rec=snd_card_create_write_filter(card);
+ copy=ms_copy_new();
+ timer=ms_timer_new();
+
+ ms_filter_add_link(play,copy);
+ ms_filter_add_link(copy,rec);
+ ms_sync_attach(timer,play);
+
+ ms_start(timer);
+
+ while(1)
+ {
+ ms_sound_write_set_level(MS_SOUND_WRITE(rec),i);
+ i+=10;
+ sleep(2);
+ if (i>100) i=0;
+ }
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/rtpspeex.c b/third_party/libjingle/source/talk/third_party/mediastreamer/rtpspeex.c
new file mode 100644
index 0000000..4ae057d
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/rtpspeex.c
@@ -0,0 +1,38 @@
+
+#include "mediastream.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int cond=1;
+
+void stop_handler(int signum)
+{
+ cond--;
+ if (cond<0) exit(-1);
+}
+
+int main(int argc, char * argv[])
+{
+ AudioStream *audio;
+
+ ortp_init();
+ rtp_profile_set_payload(&av_profile, 110, &speex_wb);
+ rtp_profile_set_payload(&av_profile,102,&payload_type_ilbc);
+
+ ms_init();
+ ms_speex_codec_init();
+ ms_ilbc_codec_init();
+
+ signal(SIGINT, stop_handler);
+
+ audio = audio_stream_start(&av_profile, 2000, "127.0.0.1", 2000, 110, 250);
+
+ for (;;) sleep(1);
+
+ audio_stream_stop(audio);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/sendrtp.c b/third_party/libjingle/source/talk/third_party/mediastreamer/sendrtp.c
new file mode 100644
index 0000000..924b549
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/sendrtp.c
@@ -0,0 +1,110 @@
+#include "msrtpsend.h"
+#include "ms.h"
+#include "msread.h"
+#include "msossread.h"
+#include "msMUlawenc.h"
+#include "mstimer.h"
+#include "msfdispatcher.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static int cond=1;
+
+void usage(){
+ printf(
+ "\nUsage: \ntest_rtpsend <output_file> <address> <port>\n"
+ "<output_file> is a file to log the flow\n"
+ "<address> is a remote ip address to listen\n"
+ "<port> is a remote udp port to listen\n"
+ );
+
+}
+
+void stop_handler(int signum){
+ cond=0;
+}
+
+int main(int argc, char *argv[]){
+ MSFilter *sender,*enc,*mic;
+ MSSync *timer;
+ RtpSession *rtps;
+ int port;
+
+ if (argc < 4){
+ usage();
+ exit(0);
+ }
+ port = atoi(argv[3]);
+
+ printf("#################################\n"
+ "Test Program\n"
+ "Sending RTP flow with oRTP\n"
+ "#################################\n"
+ );
+ /*create the rtp session */
+ ortp_init();
+ ortp_set_debug_file("oRTP",NULL);
+ rtps=rtp_session_new(RTP_SESSION_SENDONLY);
+ rtp_session_set_remote_addr(rtps,argv[2],port);
+ rtp_session_set_scheduling_mode(rtps,0);
+ rtp_session_set_blocking_mode(rtps,0);
+
+ printf(
+ "##########################################################################\n"
+ "Inicialized to write on the %s (remote address) in the port %d\n"
+ "##########################################################################\n", argv[2], port);
+ ms_init();
+ signal(SIGINT,stop_handler);
+
+ sender=(MSFilter*)ms_rtp_send_new();
+ mic=(MSFilter*)ms_oss_read_new();
+ ms_sound_read_set_device(MS_SOUND_READ(mic),0);
+ enc=(MSFilter*)ms_MULAWencoder_new();
+
+ timer=ms_timer_new();
+
+ ms_rtp_send_set_session(MS_RTP_SEND(sender),rtps);
+
+ ms_filter_add_link(mic,enc);
+ ms_filter_add_link(enc,sender);
+ ms_sync_attach(timer,mic);
+ printf(
+ "############\n"
+ "gran=%i\n"
+ "############\n",MS_SYNC(timer)->samples_per_tick);
+
+ ms_start(timer);
+ ms_sound_read_start(MS_SOUND_READ(mic));
+ while(cond)
+ {
+ sleep(1);
+ }
+
+ printf(
+ "#################################\n"
+ "stoping sync...\n"
+ "#################################\n");
+ ms_stop(timer);
+ ms_sound_read_stop(MS_SOUND_READ(mic));
+ printf(
+ "#################################\n"
+ "unlinking filters...\n"
+ "#################################\n");
+ ms_filter_remove_links(enc,sender);
+ ms_filter_remove_links(mic,enc);
+ printf( "#################################\n"
+ "destroying filters...\n"
+ "#################################\n");
+ ms_filter_destroy(sender);
+ ms_filter_destroy(enc);
+ ms_filter_destroy(mic);
+
+ rtp_session_destroy(rtps);
+ ms_sync_destroy(timer);
+ ortp_global_stats_display();
+
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/sndcard.c b/third_party/libjingle/source/talk/third_party/mediastreamer/sndcard.c
new file mode 100644
index 0000000..ada3e6d
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/sndcard.c
@@ -0,0 +1,204 @@
+/*
+ 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_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/third_party/libjingle/source/talk/third_party/mediastreamer/sndcard.h b/third_party/libjingle/source/talk/third_party/mediastreamer/sndcard.h
new file mode 100644
index 0000000..d84757f
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/third_party/mediastreamer/test.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test.c
new file mode 100644
index 0000000..a24c3f4
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test.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 copy of the GNU Lesser General Public
+ 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 "ms.h"
+
+#include "msringplayer.h"
+#include "msosswrite.h"
+#include "msossread.h"
+#include "mscopy.h"
+#include "mstimer.h"
+#include <unistd.h>
+#include <signal.h>
+
+#define READFILE "../share/rings/orig.wav"
+#define WRITEFILE "/tmp/mediaout"
+
+static int cond=1;
+
+void stop_handler(int signum)
+{
+ cond=0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ MSFilter *play,*copy,*rec;
+ MSSync *timer;
+ int i=0;
+ int tmp;
+ char *ring;
+
+ ms_init();
+
+ if (argc>1){
+ ring=argv[1];
+ }else ring= READFILE;
+
+ play=ms_ring_player_new(ring,2);
+ //play=ms_oss_read_new(0);
+ rec=snd_card_create_write_filter(snd_card_manager_get_card(snd_card_manager,1));
+ copy=ms_copy_new();
+
+ ms_filter_get_property(play,MS_FILTER_PROPERTY_FREQ,&tmp);
+ g_message("Playing at rate %i.",tmp);
+ ms_filter_set_property(rec,MS_FILTER_PROPERTY_FREQ,&tmp);
+ ms_filter_get_property(play,MS_FILTER_PROPERTY_CHANNELS,&tmp);
+ g_message("Playing with %i channels",tmp);
+ ms_filter_set_property(rec,MS_FILTER_PROPERTY_CHANNELS,&tmp);
+
+ timer=ms_timer_new();
+ ms_sync_start(timer);
+
+ ms_filter_add_link(play,copy);
+ ms_filter_add_link(copy,rec);
+ ms_sync_attach(timer,play);
+
+
+ while(cond)
+ {
+ sleep(1);
+ }
+ ms_sync_detach(timer,play);
+ ms_sync_stop(timer);
+ ms_sync_destroy(timer);
+
+ ms_filter_remove_links(play,copy);
+ ms_filter_remove_links(copy,rec);
+ ms_filter_destroy(play);
+ ms_filter_destroy(copy);
+ ms_filter_destroy(rec);
+
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_alaw.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_alaw.c
new file mode 100644
index 0000000..243ceca
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_alaw.c
@@ -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
+*/
+
+#include "ms.h"
+
+#include "msosswrite.h"
+#include "msossread.h"
+#include "mscopy.h"
+#include "mstimer.h"
+#include "msAlawdec.h"
+#include "msAlawenc.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static int cond=1;
+
+void stop_handler(int signum)
+{
+ cond=0;
+}
+
+int main()
+{
+ MSFilter *play,*enc,*dec,*rec;
+ MSSync *timer;
+
+ ms_init();
+ signal(SIGINT,stop_handler);
+
+ play=ms_oss_read_new();
+ rec=ms_oss_write_new();
+
+ ms_sound_read_set_device(MS_SOUND_READ(play),0);
+ ms_sound_write_set_device(MS_SOUND_WRITE(rec),0);
+
+ enc=ms_ALAWencoder_new();
+ dec=ms_ALAWdecoder_new();
+ timer=ms_timer_new();
+
+ ms_filter_add_link(play,enc);
+ ms_filter_add_link(enc,dec);
+ ms_filter_add_link(dec,rec);
+ ms_sync_attach(timer,play);
+
+ ms_start(timer);
+
+ ms_sound_read_start(MS_SOUND_READ(play));
+ ms_sound_write_start(MS_SOUND_WRITE(rec));
+ while(cond)
+ {
+ sleep(1);
+ }
+
+ ms_sound_read_stop(MS_SOUND_READ(play));
+ ms_sound_write_stop(MS_SOUND_WRITE(rec));
+
+ printf("stoping sync...\n");
+ ms_stop(timer);
+ printf("unlinking filters...\n");
+ ms_filter_remove_links(play,enc);
+ ms_filter_remove_links(enc,dec);
+ ms_filter_remove_links(dec,rec);
+ printf("destroying filters...\n");
+ ms_filter_destroy(play);
+ ms_filter_destroy(enc);
+ ms_filter_destroy(dec);
+ ms_filter_destroy(rec);
+ ms_sync_destroy(timer);
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_gsm.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_gsm.c
new file mode 100644
index 0000000..924b549
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_gsm.c
@@ -0,0 +1,110 @@
+#include "msrtpsend.h"
+#include "ms.h"
+#include "msread.h"
+#include "msossread.h"
+#include "msMUlawenc.h"
+#include "mstimer.h"
+#include "msfdispatcher.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static int cond=1;
+
+void usage(){
+ printf(
+ "\nUsage: \ntest_rtpsend <output_file> <address> <port>\n"
+ "<output_file> is a file to log the flow\n"
+ "<address> is a remote ip address to listen\n"
+ "<port> is a remote udp port to listen\n"
+ );
+
+}
+
+void stop_handler(int signum){
+ cond=0;
+}
+
+int main(int argc, char *argv[]){
+ MSFilter *sender,*enc,*mic;
+ MSSync *timer;
+ RtpSession *rtps;
+ int port;
+
+ if (argc < 4){
+ usage();
+ exit(0);
+ }
+ port = atoi(argv[3]);
+
+ printf("#################################\n"
+ "Test Program\n"
+ "Sending RTP flow with oRTP\n"
+ "#################################\n"
+ );
+ /*create the rtp session */
+ ortp_init();
+ ortp_set_debug_file("oRTP",NULL);
+ rtps=rtp_session_new(RTP_SESSION_SENDONLY);
+ rtp_session_set_remote_addr(rtps,argv[2],port);
+ rtp_session_set_scheduling_mode(rtps,0);
+ rtp_session_set_blocking_mode(rtps,0);
+
+ printf(
+ "##########################################################################\n"
+ "Inicialized to write on the %s (remote address) in the port %d\n"
+ "##########################################################################\n", argv[2], port);
+ ms_init();
+ signal(SIGINT,stop_handler);
+
+ sender=(MSFilter*)ms_rtp_send_new();
+ mic=(MSFilter*)ms_oss_read_new();
+ ms_sound_read_set_device(MS_SOUND_READ(mic),0);
+ enc=(MSFilter*)ms_MULAWencoder_new();
+
+ timer=ms_timer_new();
+
+ ms_rtp_send_set_session(MS_RTP_SEND(sender),rtps);
+
+ ms_filter_add_link(mic,enc);
+ ms_filter_add_link(enc,sender);
+ ms_sync_attach(timer,mic);
+ printf(
+ "############\n"
+ "gran=%i\n"
+ "############\n",MS_SYNC(timer)->samples_per_tick);
+
+ ms_start(timer);
+ ms_sound_read_start(MS_SOUND_READ(mic));
+ while(cond)
+ {
+ sleep(1);
+ }
+
+ printf(
+ "#################################\n"
+ "stoping sync...\n"
+ "#################################\n");
+ ms_stop(timer);
+ ms_sound_read_stop(MS_SOUND_READ(mic));
+ printf(
+ "#################################\n"
+ "unlinking filters...\n"
+ "#################################\n");
+ ms_filter_remove_links(enc,sender);
+ ms_filter_remove_links(mic,enc);
+ printf( "#################################\n"
+ "destroying filters...\n"
+ "#################################\n");
+ ms_filter_destroy(sender);
+ ms_filter_destroy(enc);
+ ms_filter_destroy(mic);
+
+ rtp_session_destroy(rtps);
+ ms_sync_destroy(timer);
+ ortp_global_stats_display();
+
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_lpc10.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_lpc10.c
new file mode 100644
index 0000000..eaad77f
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_lpc10.c
@@ -0,0 +1,109 @@
+#include "msrtprecv.h"
+#include "ms.h"
+#include "mswrite.h"
+#include "msosswrite.h"
+#include "msMUlawdec.h"
+#include "mstimer.h"
+#include "msfdispatcher.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static int cond=1;
+
+void usage(){
+ printf(
+ "\nUsage: \ntest_rtprecv <output_file> <address> <port>\n"
+ "<output_file> is a file to log the flow\n"
+ "<address> is a local ip address to listen\n"
+ "<port> is a local udp port to listen\n"
+ );
+
+}
+
+void stop_handler(int signum){
+ cond=0;
+}
+
+int main(int argc, char *argv[]){
+ MSFilter *play,*dec,*rec;
+ MSSync *timer;
+ RtpSession *rtps;
+ int port;
+
+ if (argc < 4){
+ usage();
+ exit(0);
+ }
+ port = atoi(argv[3]);
+
+ printf("#################################\n"
+ "Test Program\n"
+ "Receiving RTP flow with oRTP\n"
+ "#################################\n"
+ );
+ /*create the rtp session */
+ ortp_init();
+ ortp_set_debug_file("oRTP",NULL);
+ rtps=rtp_session_new(RTP_SESSION_RECVONLY);
+ rtp_session_set_local_addr(rtps,argv[2],port);
+ rtp_session_set_scheduling_mode(rtps,0);
+ rtp_session_set_blocking_mode(rtps,0);
+
+ printf(
+ "##########################################################################\n"
+ "Inicialized to listen to the %s (local address) in the port %d\n"
+ "##########################################################################\n", argv[2], port);
+ ms_init();
+ signal(SIGINT,stop_handler);
+
+ play=ms_rtp_recv_new();
+ rec=ms_oss_write_new();
+ ms_sound_write_set_device(MS_SOUND_WRITE(rec),0);
+ dec=ms_MULAWdecoder_new();
+ timer=ms_timer_new();
+
+ ms_rtp_recv_set_session(MS_RTP_RECV(play),rtps);
+
+ ms_filter_add_link(play,dec);
+ ms_filter_add_link(dec,rec);
+ ms_sync_attach(timer,play);
+ printf(
+ "############\n"
+ "gran=%i\n"
+ "############\n",MS_SYNC(timer)->samples_per_tick);
+
+ ms_start(timer);
+ ms_sound_write_start(MS_SOUND_WRITE(rec));
+ while(cond)
+ {
+ sleep(1);
+ }
+
+ printf(
+ "#################################\n"
+ "stoping sync...\n"
+ "#################################\n");
+ ms_stop(timer);
+ ms_sound_write_stop(MS_SOUND_WRITE(rec));
+ printf(
+ "#################################\n"
+ "unlinking filters...\n"
+ "#################################\n");
+ ms_filter_remove_links(play,dec);
+ ms_filter_remove_links(dec,rec);
+ printf( "#################################\n"
+ "destroying filters...\n"
+ "#################################\n");
+ ms_filter_destroy(play);
+ ms_filter_destroy(dec);
+ ms_filter_destroy(rec);
+
+ rtp_session_destroy(rtps);
+ ms_sync_destroy(timer);
+ ortp_global_stats_display();
+
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_mulaw.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_mulaw.c
new file mode 100644
index 0000000..9962e86
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_mulaw.c
@@ -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
+*/
+
+#include "ms.h"
+
+#include "msosswrite.h"
+#include "msossread.h"
+#include "mscopy.h"
+#include "msnosync.h"
+#include "mstimer.h"
+#include "msMUlawdec.h"
+#include "msMUlawenc.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static int cond=1;
+
+void stop_handler(int signum)
+{
+ cond=0;
+}
+
+int main()
+{
+ MSFilter *play,*enc,*dec,*rec;
+ MSSync *timer;
+
+ ms_init();
+ signal(SIGINT,stop_handler);
+
+ play=ms_oss_read_new();
+ rec=ms_oss_write_new();
+ ms_sound_read_set_device(MS_SOUND_READ(play),0);
+ ms_sound_write_set_device(MS_SOUND_WRITE(rec),0);
+
+ enc=ms_MULAWencoder_new();
+ dec=ms_MULAWdecoder_new();
+ timer=ms_timer_new();
+
+ ms_filter_add_link(play,enc);
+ ms_filter_add_link(enc,dec);
+ ms_filter_add_link(dec,rec);
+ ms_sync_attach(timer,play);
+
+ ms_start(timer);
+ ms_sound_read_start(MS_SOUND_READ(play));
+ ms_sound_write_start(MS_SOUND_WRITE(rec));
+ while(cond)
+ {
+ sleep(1);
+ }
+ ms_sound_read_stop(MS_SOUND_READ(play));
+ ms_sound_write_stop(MS_SOUND_WRITE(rec));
+ printf("stoping sync...\n");
+ ms_stop(timer);
+ printf("unlinking filters...\n");
+ ms_filter_remove_links(play,enc);
+ ms_filter_remove_links(enc,dec);
+ ms_filter_remove_links(dec,rec);
+ printf("destroying filters...\n");
+ ms_filter_destroy(play);
+ ms_filter_destroy(enc);
+ ms_filter_destroy(dec);
+ ms_filter_destroy(rec);
+ ms_sync_destroy(timer);
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_rtprecv.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_rtprecv.c
new file mode 100644
index 0000000..b8d6796
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_rtprecv.c
@@ -0,0 +1,100 @@
+ /*
+ 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"
+#include "ms.h"
+#include "mswrite.h"
+#include "msosswrite.h"
+#include "msMUlawdec.h"
+#include "mstimer.h"
+#include "msfdispatcher.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static int cond=1;
+
+void stop_handler(int signum)
+{
+ cond=0;
+}
+
+int main()
+{
+ MSFilter *play,*dec,*rec,*filerec,*dis;
+ MSSync *timer;
+ RtpSession *rtps;
+
+ /*create the rtp session */
+ ortp_init();
+ ortp_set_debug_file("oRTP",NULL);
+ rtps=rtp_session_new(RTP_SESSION_RECVONLY);
+ rtp_session_set_local_addr(rtps,"0.0.0.0",8000);
+ rtp_session_set_scheduling_mode(rtps,0);
+ rtp_session_set_blocking_mode(rtps,0);
+
+ ms_init();
+ signal(SIGINT,stop_handler);
+
+ play=ms_rtp_recv_new();
+ rec=ms_oss_write_new();
+ ms_sound_write_set_device(MS_SOUND_WRITE(rec),0);
+ dec=ms_MULAWdecoder_new();
+ filerec=ms_write_new("/tmp/rtpstream");
+ dis=ms_fdispatcher_new();
+ timer=ms_timer_new();
+
+ ms_rtp_recv_set_session(MS_RTP_RECV(play),rtps);
+
+ ms_filter_add_link(play,dec);
+ ms_filter_add_link(dec,dis);
+ ms_filter_add_link(dis,rec);
+ ms_filter_add_link(dis,filerec);
+ ms_sync_attach(timer,play);
+ printf("gran=%i\n",MS_SYNC(timer)->samples_per_tick);
+
+ ms_start(timer);
+ ms_sound_write_start(MS_SOUND_WRITE(rec));
+ while(cond)
+ {
+ sleep(1);
+ }
+
+ printf("stoping sync...\n");
+ ms_stop(timer);
+ ms_sound_write_stop(MS_SOUND_WRITE(rec));
+ printf("unlinking filters...\n");
+ ms_filter_remove_links(play,dec);
+ ms_filter_remove_links(dec,rec);
+ printf("destroying filters...\n");
+ ms_filter_destroy(play);
+ ms_filter_destroy(dec);
+ ms_filter_destroy(rec);
+ ms_filter_destroy(dis);
+ ms_filter_destroy(filerec);
+
+ rtp_session_destroy(rtps);
+ ms_sync_destroy(timer);
+ ortp_global_stats_display();
+
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_speex.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_speex.c
new file mode 100644
index 0000000..4ae057d
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_speex.c
@@ -0,0 +1,38 @@
+
+#include "mediastream.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int cond=1;
+
+void stop_handler(int signum)
+{
+ cond--;
+ if (cond<0) exit(-1);
+}
+
+int main(int argc, char * argv[])
+{
+ AudioStream *audio;
+
+ ortp_init();
+ rtp_profile_set_payload(&av_profile, 110, &speex_wb);
+ rtp_profile_set_payload(&av_profile,102,&payload_type_ilbc);
+
+ ms_init();
+ ms_speex_codec_init();
+ ms_ilbc_codec_init();
+
+ signal(SIGINT, stop_handler);
+
+ audio = audio_stream_start(&av_profile, 2000, "127.0.0.1", 2000, 110, 250);
+
+ for (;;) sleep(1);
+
+ audio_stream_stop(audio);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_truespeech.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_truespeech.c
new file mode 100644
index 0000000..4e734fa
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_truespeech.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 copy of the GNU Lesser General Public
+ 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 "ms.h"
+
+#include "msosswrite.h"
+#include "msossread.h"
+#include "mscopy.h"
+#include "mstimer.h"
+#include "mstruespeechdecoder.h"
+#include "mstruespeechencoder.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static int cond=1;
+
+void stop_handler(int signum)
+{
+ cond=0;
+}
+
+
+int main()
+{
+ MSFilter *play,*enc,*dec,*rec;
+ MSSync *timer;
+
+ ms_init();
+ signal(SIGINT,stop_handler);
+
+ play=ms_oss_read_new();
+ rec=ms_oss_write_new();
+
+ ms_sound_read_set_device(MS_SOUND_READ(play),0);
+ ms_sound_write_set_device(MS_SOUND_WRITE(rec),0);
+
+ enc=ms_truespeechencoder_new();
+ dec=ms_truespeechdecoder_new();
+ timer=ms_timer_new();
+
+ ms_filter_add_link(play,enc);
+ ms_filter_add_link(enc,dec);
+ ms_filter_add_link(dec,rec);
+ ms_sync_attach(timer,play);
+
+ ms_start(timer);
+
+ ms_sound_read_start(MS_SOUND_READ(play));
+ ms_sound_write_start(MS_SOUND_WRITE(rec));
+ while(cond)
+ {
+ sleep(1);
+ }
+
+ ms_sound_read_stop(MS_SOUND_READ(play));
+ ms_sound_write_stop(MS_SOUND_WRITE(rec));
+
+ printf("stoping sync...\n");
+ ms_stop(timer);
+ printf("unlinking filters...\n");
+ ms_filter_remove_links(play,enc);
+ ms_filter_remove_links(enc,dec);
+ ms_filter_remove_links(dec,rec);
+ printf("destroying filters...\n");
+ ms_filter_destroy(play);
+ ms_filter_destroy(dec);
+ ms_filter_destroy(enc);
+ ms_filter_destroy(rec);
+ ms_sync_destroy(timer);
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_v4l.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_v4l.c
new file mode 100644
index 0000000..ffb72d3
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_v4l.c
@@ -0,0 +1,32 @@
+/*
+ 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"
+
+
+int main()
+{
+ VideoStream *v;
+ ms_init();
+ v=video_preview_start("Video4Linux","/dev/video0");
+ sleep(5);
+ video_preview_stop(v);
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/test_videostream.c b/third_party/libjingle/source/talk/third_party/mediastreamer/test_videostream.c
new file mode 100644
index 0000000..0e68900
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/test_videostream.c
@@ -0,0 +1,44 @@
+/*
+ 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"
+#include <signal.h>
+
+static gboolean cond=TRUE;
+
+static void stop_handler(int signum){
+ cond=FALSE;
+}
+
+int main()
+{
+ VideoStream *v;
+ ortp_init();
+ ms_init();
+ signal(SIGINT,stop_handler);
+ v=video_stream_start(&av_profile,6000,"127.0.0.1",6000, 34, 60, TRUE, "Video4Linux","/dev/video0");
+ while(cond) {
+ ortp_global_stats_display();
+ sleep(1);
+ }
+ video_stream_stop(v);
+ ortp_exit();
+ return 0;
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/videostream.c b/third_party/libjingle/source/talk/third_party/mediastreamer/videostream.c
new file mode 100644
index 0000000..44d2495
--- /dev/null
+++ b/third_party/libjingle/source/talk/third_party/mediastreamer/videostream.c
@@ -0,0 +1,258 @@
+/*
+ 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"
+#include "msvideosource.h"
+#include "msavdecoder.h"
+#include "msavencoder.h"
+#include "msnosync.h"
+#include "mssdlout.h"
+
+#define USE_SDL
+
+extern void create_duplex_rtpsession(RtpProfile *profile, int locport,char *remip,int remport,
+ int payload,int jitt_comp,
+ RtpSession **recvsend);
+
+#define MAX_RTP_SIZE 5000
+
+/* this code is not part of the library itself, it is part of the mediastream program */
+void
+video_stream_free (VideoStream * stream)
+{
+ RtpSession *recvs, *sends;
+ if (stream->rtprecv != NULL)
+ {
+ recvs = ms_rtp_recv_get_session (MS_RTP_RECV (stream->rtprecv));
+ if (recvs != NULL)
+ {
+ rtp_session_destroy (recvs);
+ }
+ ms_filter_destroy (stream->rtprecv);
+ }
+ if (stream->rtpsend != NULL)
+ {
+ sends = ms_rtp_send_get_session (MS_RTP_SEND (stream->rtpsend));
+ if (sends != NULL && sends!=recvs)
+ {
+ rtp_session_destroy (sends);
+ }
+ ms_filter_destroy (stream->rtpsend);
+ }
+ if (stream->source != NULL)
+ ms_filter_destroy (stream->source);
+ if (stream->output != NULL)
+ ms_filter_destroy (stream->output);
+ if (stream->decoder != NULL)
+ ms_filter_destroy (stream->decoder);
+ if (stream->encoder != NULL)
+ ms_filter_destroy (stream->encoder);
+ if (stream->timer != NULL)
+ ms_sync_destroy (stream->timer);
+ g_free (stream);
+}
+
+
+VideoStream *
+video_stream_start (RtpProfile *profile, int locport, char *remip, int remport,
+ int payload, int jitt_comp, gboolean show_local,
+ const gchar *source, const gchar *device)
+{
+ VideoStream *stream = g_new0 (VideoStream, 1);
+ RtpSession *rtps, *rtpr;
+ PayloadType *pt;
+ gchar *format;
+ gint width = VIDEO_SIZE_CIF_W;
+ gint height = VIDEO_SIZE_CIF_H;
+ gfloat fps;
+
+ create_duplex_rtpsession(profile,locport,remip,remport,payload,jitt_comp,&rtpr);
+ rtp_session_enable_adaptive_jitter_compensation(rtpr,FALSE);
+ rtps=rtpr;
+ ms_trace("sending video to %s:%i", remip4, remport);
+
+ /* creates two rtp filters to recv send streams (remote part) */
+ rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE);
+ stream->rtpsend = ms_rtp_send_new ();
+ if (remport>0) 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);
+
+ pt=rtp_profile_get_payload(profile,payload);
+ if (pt==NULL){
+ g_error("videostream.c: undefined payload type.");
+ return NULL;
+ }
+ ms_trace("videostream.c: getting codecs for %s", pt->mime_type);
+
+ /* creates the filters */
+ stream->source = ms_filter_new_with_name(source);
+ if (stream->source == NULL){
+ g_error("videostream.c: failed to create video source %s.", source);
+ return NULL;
+ }
+
+#ifdef USE_SDL
+ stream->output=ms_sdl_out_new();
+#else
+ stream->output = MS_FILTER(ms_video_output_new ());
+#endif
+ stream->encoder=ms_encoder_new_with_string_id(pt->mime_type);
+ g_message("Video encoder created: %x",stream->encoder);
+ 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...*/
+ video_stream_free(stream);
+ g_error("videostream.c: No codecs available for payload %i.",payload);
+ return NULL;
+ }
+
+ /* configure the filters */
+ ms_video_source_set_device(MS_VIDEO_SOURCE(stream->source), device);
+ ms_video_source_set_size(MS_VIDEO_SOURCE(stream->source), width, height);
+ ms_video_source_set_frame_rate(MS_VIDEO_SOURCE(stream->source), 8, 1);
+ fps = MS_VIDEO_SOURCE(stream->source)->frame_rate / MS_VIDEO_SOURCE(stream->source)->frame_rate_base;
+ format = ms_video_source_get_format(MS_VIDEO_SOURCE(stream->source));
+
+ ms_AVencoder_set_format (MS_AVENCODER (stream->encoder), format);
+ ms_AVencoder_set_width(MS_AVENCODER(stream->encoder), width);
+ ms_AVencoder_set_height(MS_AVENCODER(stream->encoder), height);
+ /* bitrate is based upon 30fps? adjust by our possibly lower framerate */
+ ms_AVencoder_set_bit_rate(MS_AVENCODER(stream->encoder), pt->normal_bitrate * 30 / fps );
+ ms_AVdecoder_set_format (MS_AVDECODER (stream->decoder), "YUV420P");
+ ms_AVdecoder_set_width(MS_AVDECODER(stream->decoder), width);
+ ms_AVdecoder_set_height(MS_AVDECODER(stream->decoder), height);
+#ifdef USE_SDL
+ /* we suppose our decoder and pin1 of encoder always outputs YUV420P */
+ ms_sdl_out_set_format(MS_SDL_OUT(stream->output),"YUV420P");
+#else
+ ms_video_output_set_size (MS_VIDEO_OUTPUT (stream->output), width, height);
+#endif
+
+ /* and then connect all */
+ ms_filter_add_link (stream->source, 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->output);
+ if (show_local)
+ ms_filter_add_link(stream->encoder,stream->output);
+
+ /* create the synchronisation source */
+ stream->timer = ms_timer_new();
+ ms_sync_attach (stream->timer, stream->source);
+ ms_sync_attach (stream->timer, stream->rtprecv);
+
+ /* and start */
+ ms_video_source_start(MS_VIDEO_SOURCE(stream->source));
+ ms_start (stream->timer);
+ stream->show_local=show_local;
+ return stream;
+}
+
+
+
+void
+video_stream_stop (VideoStream * stream)
+{
+
+ ms_stop (stream->timer);
+ ms_video_source_stop (MS_VIDEO_SOURCE(stream->source));
+ ms_sync_detach (stream->timer, stream->source);
+ ms_sync_detach (stream->timer, stream->rtprecv);
+
+ ms_filter_remove_links(stream->source,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->output);
+ if (stream->show_local) {
+ ms_filter_remove_links (stream->encoder,
+ stream->output);
+ }
+ video_stream_free (stream);
+}
+
+
+void video_stream_set_rtcp_information(VideoStream *st, const char *cname){
+ if (st->send_session!=NULL){
+ rtp_session_set_source_description(st->send_session,cname,NULL,NULL,NULL,NULL,"linphone-" LINPHONE_VERSION,
+ "This is free software (GPL) !");
+ }
+}
+
+
+
+VideoStream * video_preview_start(const gchar *source, const gchar *device){
+ VideoStream *stream = g_new0 (VideoStream, 1);
+ gchar *format;
+ gint width = VIDEO_SIZE_CIF_W;
+ gint height = VIDEO_SIZE_CIF_H;
+
+ /* creates the filters */
+ stream->source = ms_filter_new_with_name(source);
+ if (stream->source == NULL){
+ g_error("videostream.c: failed to create video source %s.", source);
+ return NULL;
+ }
+#ifdef USE_SDL
+ stream->output=ms_sdl_out_new();
+#else
+ stream->output = ms_video_output_new ();
+#endif
+ /* configure the filters */
+ ms_video_source_set_device(MS_VIDEO_SOURCE(stream->source), device);
+ ms_video_source_set_size(MS_VIDEO_SOURCE(stream->source), width, height);
+ ms_video_source_set_frame_rate(MS_VIDEO_SOURCE(stream->source), 8, 1);
+ format = ms_video_source_get_format(MS_VIDEO_SOURCE(stream->source));
+
+#ifdef USE_SDL
+ ms_sdl_out_set_format(MS_SDL_OUT(stream->output),format);
+#else
+ ms_video_output_set_format(MS_VIDEO_OUTPUT(stream->output),format);
+ ms_video_output_set_size (MS_VIDEO_OUTPUT (stream->output), width, height);
+ ms_video_output_set_title(MS_VIDEO_OUTPUT(stream->output),"Linphone Video");
+#endif
+ /* and then connect all */
+ ms_filter_add_link (stream->source, stream->output);
+ /* create the synchronisation source */
+ stream->timer = ms_timer_new();
+ ms_sync_attach (stream->timer, stream->source);
+
+ /* and start */
+ ms_video_source_start(MS_VIDEO_SOURCE(stream->source));
+ ms_start (stream->timer);
+
+ return stream;
+}
+
+void video_preview_stop(VideoStream *stream){
+ ms_stop (stream->timer);
+ ms_video_source_stop (MS_VIDEO_SOURCE(stream->source));
+ ms_sync_detach (stream->timer, stream->source);
+ ms_filter_remove_links(stream->source,stream->output);
+ video_stream_free(stream);
+}
diff --git a/third_party/libjingle/source/talk/third_party/mediastreamer/waveheader.h b/third_party/libjingle/source/talk/third_party/mediastreamer/waveheader.h
new file mode 100644
index 0000000..6768d8f
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/xmllite/qname.cc b/third_party/libjingle/source/talk/xmllite/qname.cc
new file mode 100644
index 0000000..7486524
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/qname.cc
@@ -0,0 +1,162 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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"
+
+namespace buzz {
+
+static int QName_Hash(const std::string & ns, const char * local) {
+ int result = static_cast<int>(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/third_party/libjingle/source/talk/xmllite/qname.h b/third_party/libjingle/source/talk/xmllite/qname.h
new file mode 100644
index 0000000..3e64726
--- /dev/null
+++ b/third_party/libjingle/source/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) :
+ namespace_(ns),
+ localPart_(local),
+ refcount_(1) {}
+
+ 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/third_party/libjingle/source/talk/xmllite/xmlbuilder.cc b/third_party/libjingle/source/talk/xmllite/xmlbuilder.cc
new file mode 100644
index 0000000..b02dfe0
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/xmlbuilder.cc
@@ -0,0 +1,152 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <set>
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlbuilder.h"
+#ifdef EXPAT_RELATIVE_PATH
+#include "lib/expat.h"
+#else
+#include "third_party/expat/v2_0_1/Source/lib/expat.h"
+#endif // EXPAT_RELATIVE_PATH
+
+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/third_party/libjingle/source/talk/xmllite/xmlbuilder.h b/third_party/libjingle/source/talk/xmllite/xmlbuilder.h
new file mode 100644
index 0000000..d375985
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/xmlbuilder.h
@@ -0,0 +1,78 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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 <vector>
+#include "talk/base/scoped_ptr.h"
+#include "talk/xmllite/xmlparser.h"
+
+#ifdef EXPAT_RELATIVE_PATH
+#include "lib/expat.h"
+#else
+#include "third_party/expat/v2_0_1/Source/lib/expat.h"
+#endif // EXPAT_RELATIVE_PATH
+
+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_;
+ talk_base::scoped_ptr<XmlElement> pelRoot_;
+ talk_base::scoped_ptr<std::vector<XmlElement*> > pvParents_;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmllite/xmlconstants.cc b/third_party/libjingle/source/talk/xmllite/xmlconstants.cc
new file mode 100644
index 0000000..503f832
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/xmllite/xmlconstants.h b/third_party/libjingle/source/talk/xmllite/xmlconstants.h
new file mode 100644
index 0000000..8514d6f
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/xmllite/xmlelement.cc b/third_party/libjingle/source/talk/xmllite/xmlelement.cc
new file mode 100644
index 0000000..3ec085c
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/xmlelement.cc
@@ -0,0 +1,520 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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"
+
+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),
+ cdata_(false) {
+}
+
+XmlElement::XmlElement(const XmlElement & elt) :
+ XmlChild(),
+ name_(elt.name_),
+ pFirstAttr_(NULL),
+ pLastAttr_(NULL),
+ pFirstChild_(NULL),
+ pLastChild_(NULL),
+ cdata_(false) {
+
+ // 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;
+
+ cdata_ = elt.cdata_;
+}
+
+XmlElement::XmlElement(const QName & name, bool useDefaultNs) :
+ name_(name),
+ pFirstAttr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL),
+ pLastAttr_(pFirstAttr_),
+ pFirstChild_(NULL),
+ pLastChild_(NULL),
+ cdata_(false) {
+}
+
+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;
+}
+
+XmlElement* XmlElement::FindOrAddNamedChild(const QName& name) {
+ XmlElement* child = FirstNamed(name);
+ if (!child) {
+ child = new XmlElement(name);
+ AddElement(child);
+ }
+
+ return child;
+}
+
+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::AddCDATAText(const char * buf, int len) {
+ cdata_ = true;
+ AddParsedText(buf, 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::ClearAttributes() {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; ) {
+ XmlAttr * pToDelete = pattr;
+ pattr = pattr->pNextAttr_;
+ delete pToDelete;
+ }
+ pFirstAttr_ = pLastAttr_ = NULL;
+}
+
+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/third_party/libjingle/source/talk/xmllite/xmlelement.h b/third_party/libjingle/source/talk/xmllite/xmlelement.h
new file mode 100644
index 0000000..38e31a9
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/xmlelement.h
@@ -0,0 +1,239 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_; }
+ void SetName(const QName& name) { name_ = 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); }
+
+ // Finds the first element named 'name'. If that element can't be found then
+ // adds one and returns it.
+ XmlElement* FindOrAddNamedChild(const QName& 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);
+ // Note: CDATA is not supported by XMPP, therefore using this function will
+ // generate non-XMPP compatible XML.
+ void AddCDATAText(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 ClearAttributes();
+ void ClearChildren();
+
+ static XmlElement * ForStr(const std::string & str);
+ std::string Str() const;
+
+ void Print(std::ostream * pout, std::string xmlns[], int xmlnsCount) const;
+
+ bool IsCDATA() const { return cdata_; }
+
+protected:
+ virtual bool IsTextImpl() const;
+ virtual XmlElement * AsElementImpl() const;
+ virtual XmlText * AsTextImpl() const;
+
+private:
+ QName name_;
+ XmlAttr * pFirstAttr_;
+ XmlAttr * pLastAttr_;
+ XmlChild * pFirstChild_;
+ XmlChild * pLastChild_;
+ bool cdata_;
+};
+
+}
+#endif
diff --git a/third_party/libjingle/source/talk/xmllite/xmlnsstack.cc b/third_party/libjingle/source/talk/xmllite/xmlnsstack.cc
new file mode 100644
index 0000000..18e1607
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/xmlnsstack.cc
@@ -0,0 +1,204 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/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/third_party/libjingle/source/talk/xmllite/xmlnsstack.h b/third_party/libjingle/source/talk/xmllite/xmlnsstack.h
new file mode 100644
index 0000000..c7e9f89
--- /dev/null
+++ b/third_party/libjingle/source/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 <vector>
+#include "talk/base/scoped_ptr.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:
+
+ talk_base::scoped_ptr<std::vector<std::string> > pxmlnsStack_;
+ talk_base::scoped_ptr<std::vector<size_t> > pxmlnsDepthStack_;
+};
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmllite/xmlparser.cc b/third_party/libjingle/source/talk/xmllite/xmlparser.cc
new file mode 100644
index 0000000..568bce1
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/xmlparser.cc
@@ -0,0 +1,292 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/xmllite/xmlparser.h"
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlnsstack.h"
+#ifdef EXPAT_RELATIVE_PATH
+#include "lib/expat.h"
+#else
+#include "third_party/expat/v2_0_1/Source/lib/expat.h"
+#endif // EXPAT_RELATIVE_PATH
+
+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));
+ }
+ }
+ }
+ context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+ XML_GetCurrentColumnNumber(expat_),
+ XML_GetCurrentByteIndex(expat_));
+ pxph_->StartElement(&context_, name, atts);
+}
+
+void
+XmlParser::ExpatEndElement(const char *name) {
+ if (context_.RaisedError() != XML_ERROR_NONE)
+ return;
+ context_.EndElement();
+ context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+ XML_GetCurrentColumnNumber(expat_),
+ XML_GetCurrentByteIndex(expat_));
+ pxph_->EndElement(&context_, name);
+}
+
+void
+XmlParser::ExpatCharacterData(const char *text, int len) {
+ if (context_.RaisedError() != XML_ERROR_NONE)
+ return;
+ context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+ XML_GetCurrentColumnNumber(expat_),
+ XML_GetCurrentByteIndex(expat_));
+ 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_.SetPosition(XML_GetCurrentLineNumber(expat_),
+ XML_GetCurrentColumnNumber(expat_),
+ XML_GetCurrentByteIndex(expat_));
+ 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),
+ line_number_(0),
+ column_number_(0),
+ byte_index_(0) {
+}
+
+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;
+}
+
+void
+XmlParser::ParseContext::SetPosition(int line, int column,
+ long byte_index) {
+ line_number_ = line;
+ column_number_ = column;
+ byte_index_ = byte_index;
+}
+
+void
+XmlParser::ParseContext::GetPosition(unsigned long * line,
+ unsigned long * column,
+ unsigned long * byte_index) {
+ if (line != NULL) {
+ *line = static_cast<unsigned long>(line_number_);
+ }
+
+ if (column != NULL) {
+ *column = static_cast<unsigned long>(column_number_);
+ }
+
+ if (byte_index != NULL) {
+ *byte_index = static_cast<unsigned long>(byte_index_);
+ }
+}
+
+XmlParser::ParseContext::~ParseContext() {
+}
+
+}
diff --git a/third_party/libjingle/source/talk/xmllite/xmlparser.h b/third_party/libjingle/source/talk/xmllite/xmlparser.h
new file mode 100644
index 0000000..3e85e35
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/xmlparser.h
@@ -0,0 +1,121 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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"
+#ifdef EXPAT_RELATIVE_PATH
+#include "lib/expat.h"
+#else
+#include "third_party/expat/v2_0_1/Source/lib/expat.h"
+#endif // EXPAT_RELATIVE_PATH
+
+struct XML_ParserStruct;
+typedef struct XML_ParserStruct* XML_Parser;
+
+namespace buzz {
+
+class XmlParseHandler;
+class XmlParseContext;
+class XmlParser;
+
+class XmlParseContext {
+public:
+ virtual ~XmlParseContext() {}
+ virtual QName ResolveQName(const char * qname, bool isAttr) = 0;
+ virtual void RaiseError(XML_Error err) = 0;
+ virtual void GetPosition(unsigned long * line, unsigned long * column,
+ unsigned long * byte_index) = 0;
+};
+
+class XmlParseHandler {
+public:
+ virtual ~XmlParseHandler() {}
+ 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; }
+ virtual void GetPosition(unsigned long * line, unsigned long * column,
+ unsigned long * byte_index);
+ XML_Error RaisedError() { return raised_; }
+ void Reset();
+
+ void StartElement();
+ void EndElement();
+ void StartNamespace(const char * prefix, const char * ns);
+ void SetPosition(int line, int column, long byte_index);
+
+ private:
+ const XmlParser * parser_;
+ XmlnsStack xmlnsstack_;
+ XML_Error raised_;
+ XML_Size line_number_;
+ XML_Size column_number_;
+ XML_Index byte_index_;
+ };
+
+ ParseContext context_;
+ XML_Parser expat_;
+ XmlParseHandler * pxph_;
+ bool sentError_;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmllite/xmlprinter.cc b/third_party/libjingle/source/talk/xmllite/xmlprinter.cc
new file mode 100644
index 0000000..b05898f
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmllite/xmlprinter.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.
+ */
+
+#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);
+ void PrintCDATAText(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(pattr->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()) {
+ if (element->IsCDATA()) {
+ PrintCDATAText(pchild->AsText()->Text());
+ } else {
+ 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;
+ }
+}
+
+void
+XmlPrinterImpl::PrintCDATAText(const std::string & text) {
+ *pout_ << "<![CDATA[" << text << "]]>";
+}
+
+}
diff --git a/third_party/libjingle/source/talk/xmllite/xmlprinter.h b/third_party/libjingle/source/talk/xmllite/xmlprinter.h
new file mode 100644
index 0000000..96900d0
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/xmpp/asyncsocket.h b/third_party/libjingle/source/talk/xmpp/asyncsocket.h
new file mode 100644
index 0000000..e4bce7f
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/asyncsocket.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 _ASYNCSOCKET_H_
+#define _ASYNCSOCKET_H_
+
+#include "talk/base/sigslot.h"
+
+namespace talk_base {
+ 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 int GetError() = 0; // winsock error code
+
+ virtual bool Connect(const talk_base::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/third_party/libjingle/source/talk/xmpp/constants.cc b/third_party/libjingle/source/talk/xmpp/constants.cc
new file mode 100644
index 0000000..ce4ed4f
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/constants.cc
@@ -0,0 +1,481 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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::ns_avatar_hash() {
+ static const std::string ns_avatar_hash_("google:avatar");
+ return ns_avatar_hash_;
+}
+
+const std::string & Constants::ns_vcard_update() {
+ static const std::string ns_vcard_update_("vcard-temp:x:update");
+ return ns_vcard_update_;
+}
+
+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_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_TALK_GOOGLE_COM("talk.google.com");
+const std::string STR_TALKX_L_GOOGLE_COM("talkx.l.google.com");
+
+const std::string STR_X("x");
+
+#ifdef FEATURE_ENABLE_VOICEMAIL
+const std::string STR_VOICEMAIL("voicemail");
+const std::string STR_OUTGOINGVOICEMAIL("outgoingvoicemail");
+#endif
+
+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(true, NS_VCARD, "vCard");
+const QName QN_VCARD_FN(true, NS_VCARD, "FN");
+const QName QN_VCARD_PHOTO(true, NS_VCARD, "PHOTO");
+const QName QN_VCARD_PHOTO_BINVAL(true, NS_VCARD, "BINVAL");
+const QName QN_VCARD_AVATAR_HASH(true, NS_AVATAR_HASH, "hash");
+const QName QN_VCARD_AVATAR_HASH_MODIFIED(true, NS_AVATAR_HASH, "modified");
+
+const buzz::QName QN_NAME(true, STR_EMPTY, "name");
+const buzz::QName QN_AFFILIATION(true, STR_EMPTY, "affiliation");
+const buzz::QName QN_ROLE(true, STR_EMPTY, "role");
+
+#if defined(FEATURE_ENABLE_PSTN)
+const QName QN_VCARD_TEL(true, NS_VCARD, "TEL");
+const QName QN_VCARD_VOICE(true, NS_VCARD, "VOICE");
+const QName QN_VCARD_HOME(true, NS_VCARD, "HOME");
+const QName QN_VCARD_WORK(true, NS_VCARD, "WORK");
+const QName QN_VCARD_CELL(true, NS_VCARD, "CELL");
+const QName QN_VCARD_NUMBER(true, NS_VCARD, "NUMBER");
+#endif
+
+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_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_TITLE1(true, STR_EMPTY, "title1");
+const QName QN_TITLE2(true, STR_EMPTY, "title2");
+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");
+const std::string STR_SHOW_OFFLINE("offline");
+
+// 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");
+
+// Google Invite
+const std::string NS_GOOGLE_INVITE("google:subscribe");
+const QName QN_INVITATION(true, NS_GOOGLE_INVITE, "invitation");
+const QName QN_INVITE_NAME(true, NS_GOOGLE_INVITE, "name");
+const QName QN_INVITE_SUBJECT(true, NS_GOOGLE_INVITE, "subject");
+const QName QN_INVITE_MESSAGE(true, NS_GOOGLE_INVITE, "body");
+
+// 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");
+
+const std::string NS_MUC_USER("http://jabber.org/protocol/muc#user");
+const QName QN_MUC_USER_CONTINUE(true, NS_MUC_USER, "continue");
+const QName QN_MUC_USER_X(true, NS_MUC_USER, "x");
+const QName QN_MUC_USER_ITEM(true, NS_MUC_USER, "item");
+const QName QN_MUC_USER_STATUS(true, NS_MUC_USER, "status");
+
+// 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 0153
+const std::string kNSVCard("vcard-temp:x:update");
+const QName kQnVCardX(true, kNSVCard, "x");
+const QName kQnVCardPhoto(true, kNSVCard, "photo");
+
+// JEP 0172 User Nickname
+const std::string kNSNickname("http://jabber.org/protocol/nick");
+const QName kQnNickname(true, kNSNickname, "nick");
+
+
+// JEP 0085 chat state
+const std::string NS_CHATSTATE("http://jabber.org/protocol/chatstates");
+const QName QN_CS_ACTIVE(true, NS_CHATSTATE, "active");
+const QName QN_CS_COMPOSING(true, NS_CHATSTATE, "composing");
+const QName QN_CS_PAUSED(true, NS_CHATSTATE, "paused");
+const QName QN_CS_INACTIVE(true, NS_CHATSTATE, "inactive");
+const QName QN_CS_GONE(true, NS_CHATSTATE, "gone");
+
+// JEP 0091 Delayed Delivery
+const std::string kNSDelay("jabber:x:delay");
+const QName kQnDelayX(true, kNSDelay, "x");
+const QName kQnStamp(true, STR_EMPTY, "stamp");
+
+// Google time stamping (higher resolution)
+const std::string kNSTimestamp("google:timestamp");
+const QName kQnTime(true, kNSTimestamp, "time");
+const QName kQnMilliseconds(true, STR_EMPTY, "ms");
+
+
+// Event tracking
+#ifdef FEATURE_ENABLE_TRACKING
+const std::string NS_GOOGLE_EVENT_TRACKING("google:client-usability-testing");
+const QName QN_EVENT_TRACKING(true, NS_GOOGLE_EVENT_TRACKING, "usage-stats");
+const QName QN_EVENT_TRACKING_BRANDID(true, NS_GOOGLE_EVENT_TRACKING, "bid");
+const QName QN_EVENT_TRACKING_EVENT(true, NS_GOOGLE_EVENT_TRACKING, "event");
+const QName QN_EVENT_TRACKING_VARIABLE_KEY(true, STR_EMPTY, "key");
+const QName QN_EVENT_TRACKING_VARIABLE_VALUE(true, STR_EMPTY, "value");
+const QName QN_EVENT_TRACKING_VARIABLE_TIME(true, STR_EMPTY, "time");
+const QName QN_EVENT_TRACKING_EVENT_GROUP(true,
+ NS_GOOGLE_EVENT_TRACKING, "events");
+#endif
+
+
+// Jingle Info
+const std::string NS_JINGLE_INFO("google:jingleinfo");
+const QName QN_JINGLE_INFO_QUERY(true, NS_JINGLE_INFO, "query");
+const QName QN_JINGLE_INFO_STUN(true, NS_JINGLE_INFO, "stun");
+const QName QN_JINGLE_INFO_RELAY(true, NS_JINGLE_INFO, "relay");
+const QName QN_JINGLE_INFO_SERVER(true, NS_JINGLE_INFO, "server");
+const QName QN_JINGLE_INFO_TOKEN(true, NS_JINGLE_INFO, "token");
+const QName QN_JINGLE_INFO_HOST(true, STR_EMPTY, "host");
+const QName QN_JINGLE_INFO_TCP(true, STR_EMPTY, "tcp");
+const QName QN_JINGLE_INFO_UDP(true, STR_EMPTY, "udp");
+const QName QN_JINGLE_INFO_TCPSSL(true, STR_EMPTY, "tcpssl");
+
+// Call Performance Logging
+const std::string NS_GOOGLE_CALLPERF_STATS("google:call-perf-stats");
+const QName QN_CALLPERF_STATS(true, NS_GOOGLE_CALLPERF_STATS, "callPerfStats");
+const QName QN_CALLPERF_SESSIONID(true, STR_EMPTY, "sessionId");
+const QName QN_CALLPERF_LOCALUSER(true, STR_EMPTY, "localUser");
+const QName QN_CALLPERF_REMOTEUSER(true, STR_EMPTY, "remoteUser");
+const QName QN_CALLPERF_STARTTIME(true, STR_EMPTY, "startTime");
+const QName QN_CALLPERF_CALL_LENGTH(true, STR_EMPTY, "callLength");
+const QName QN_CALLPERF_DATAPOINT(true, NS_GOOGLE_CALLPERF_STATS, "dataPoint");
+const QName QN_CALLPERF_DATAPOINT_TIME(true, STR_EMPTY, "timeStamp");
+const QName QN_CALLPERF_DATAPOINT_FRACTION_LOST(true, STR_EMPTY, "fraction_lost");
+const QName QN_CALLPERF_DATAPOINT_CUM_LOST(true, STR_EMPTY, "cum_lost");
+const QName QN_CALLPERF_DATAPOINT_EXT_MAX(true, STR_EMPTY, "ext_max");
+const QName QN_CALLPERF_DATAPOINT_JITTER(true, STR_EMPTY, "jitter");
+const QName QN_CALLPERF_DATAPOINT_RTT(true, STR_EMPTY, "RTT");
+const QName QN_CALLPERF_DATAPOINT_BYTES_R(true, STR_EMPTY, "bytesReceived");
+const QName QN_CALLPERF_DATAPOINT_PACKETS_R(true, STR_EMPTY, "packetsReceived");
+const QName QN_CALLPERF_DATAPOINT_BYTES_S(true, STR_EMPTY, "bytesSent");
+const QName QN_CALLPERF_DATAPOINT_PACKETS_S(true, STR_EMPTY, "packetsSent");
+const QName QN_CALLPERF_CONNECTION(true, NS_GOOGLE_CALLPERF_STATS, "connection");
+const QName QN_CALLPERF_CONNECTION_LOCAL_ADDRESS(true, STR_EMPTY, "localAddress");
+const QName QN_CALLPERF_CONNECTION_REMOTE_ADDRESS(true, STR_EMPTY, "remoteAddress");
+
+// Muc invites.
+const QName QN_MUC_USER_INVITE(true, NS_MUC_USER, "invite");
+
+// Multiway audio/video.
+const std::string NS_GOOGLE_MUC_USER("google:muc#user");
+const QName QN_GOOGLE_MUC_USER_AVAILABLE_MEDIA(true, NS_GOOGLE_MUC_USER, "available-media");
+const QName QN_GOOGLE_MUC_USER_ENTRY(true, NS_GOOGLE_MUC_USER, "entry");
+const QName QN_GOOGLE_MUC_USER_MEDIA(true, NS_GOOGLE_MUC_USER, "media");
+const QName QN_GOOGLE_MUC_USER_TYPE(true, NS_GOOGLE_MUC_USER, "type");
+const QName QN_GOOGLE_MUC_USER_SRC_ID(true, NS_GOOGLE_MUC_USER, "src-id");
+const QName QN_GOOGLE_MUC_USER_STATUS(true, NS_GOOGLE_MUC_USER, "status");
+const std::string NS_JINGLE("google:jingle");
+const QName QN_JINGLE_SRC_ID(true, NS_JINGLE, "src-id");
+const QName QN_LABEL(true, STR_EMPTY, "label");
+
+// Call terminate reasons
+const std::string STR_TERMINATE_CALL_ENDED("call-ended");
+const std::string STR_TERMINATE_RECIPIENT_UNAVAILABLE("recipient-unavailable");
+const std::string STR_TERMINATE_RECIPIENT_BUSY("recipient-busy");
+const std::string STR_TERMINATE_INSUFFICIENT_FUNDS("insufficient-funds");
+const std::string STR_TERMINATE_NUMBER_MALFORMED("number-malformed");
+const std::string STR_TERMINATE_NUMBER_DISALLOWED("number-disallowed");
+const std::string STR_TERMINATE_PROTOCOL_ERROR("protocol-error");
+const std::string STR_TERMINATE_INTERNAL_SERVER_ERROR("internal-server-error");
+const std::string STR_TERMINATE_UNKNOWN_ERROR("unknown-error");
+
+
+}
diff --git a/third_party/libjingle/source/talk/xmpp/constants.h b/third_party/libjingle/source/talk/xmpp/constants.h
new file mode 100644
index 0000000..f07dde0
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/constants.h
@@ -0,0 +1,449 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_XMPP_CONSTANTS_H_
+#define TALK_XMPP_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 NS_AVATAR_HASH Constants::ns_avatar_hash()
+#define NS_VCARD_UPDATE Constants::ns_vcard_update()
+#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 & ns_avatar_hash();
+ static const std::string & ns_vcard_update();
+
+ 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_TALK_GOOGLE_COM;
+extern const std::string STR_TALKX_L_GOOGLE_COM;
+
+#ifdef FEATURE_ENABLE_VOICEMAIL
+extern const std::string STR_VOICEMAIL;
+extern const std::string STR_OUTGOINGVOICEMAIL;
+#endif
+
+extern const std::string STR_UNAVAILABLE;
+
+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;
+extern const QName QN_VCARD_FN;
+extern const QName QN_VCARD_PHOTO;
+extern const QName QN_VCARD_PHOTO_BINVAL;
+extern const QName QN_VCARD_AVATAR_HASH;
+extern const QName QN_VCARD_AVATAR_HASH_MODIFIED;
+
+#if defined(FEATURE_ENABLE_PSTN)
+extern const QName QN_VCARD_TEL;
+extern const QName QN_VCARD_VOICE;
+extern const QName QN_VCARD_HOME;
+extern const QName QN_VCARD_WORK;
+extern const QName QN_VCARD_CELL;
+extern const QName QN_VCARD_NUMBER;
+#endif
+
+#if defined(FEATURE_ENABLE_RICHPROFILES)
+extern const QName QN_USER_PROFILE_QUERY;
+extern const QName QN_USER_PROFILE_URL;
+
+extern const QName QN_ATOM_FEED;
+extern const QName QN_ATOM_ENTRY;
+extern const QName QN_ATOM_TITLE;
+extern const QName QN_ATOM_ID;
+extern const QName QN_ATOM_MODIFIED;
+extern const QName QN_ATOM_IMAGE;
+extern const QName QN_ATOM_LINK;
+extern const QName QN_ATOM_HREF;
+#endif
+
+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_TITLE1;
+extern const QName QN_TITLE2;
+extern const QName QN_AFFILIATION;
+extern const QName QN_ROLE;
+
+
+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;
+extern const std::string STR_SHOW_OFFLINE;
+
+// 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;
+
+// Google Invite
+extern const std::string NS_GOOGLE_SUBSCRIBE;
+extern const QName QN_INVITATION;
+extern const QName QN_INVITE_NAME;
+extern const QName QN_INVITE_SUBJECT;
+extern const QName QN_INVITE_MESSAGE;
+
+
+// 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 0045
+extern const std::string NS_MUC;
+extern const QName QN_MUC_X;
+extern const QName QN_MUC_ITEM;
+extern const QName QN_MUC_AFFILIATION;
+extern const QName QN_MUC_ROLE;
+extern const std::string STR_AFFILIATION_NONE;
+extern const std::string STR_ROLE_PARTICIPANT;
+extern const std::string NS_MUC_USER;
+extern const QName QN_MUC_USER_CONTINUE;
+extern const QName QN_MUC_USER_X;
+extern const QName QN_MUC_USER_ITEM;
+extern const QName QN_MUC_USER_STATUS;
+
+
+// JEP 0115
+extern const std::string NS_CAPS;
+extern const QName QN_CAPS_C;
+extern const QName QN_VER;
+extern const QName QN_EXT;
+
+
+// Avatar - JEP 0153
+extern const std::string kNSVCard;
+extern const QName kQnVCardX;
+extern const QName kQnVCardPhoto;
+
+// JEP 0172 User Nickname
+extern const std::string kNSNickname;
+extern const QName kQnNickname;
+
+
+// JEP 0085 chat state
+extern const std::string NS_CHATSTATE;
+extern const QName QN_CS_ACTIVE;
+extern const QName QN_CS_COMPOSING;
+extern const QName QN_CS_PAUSED;
+extern const QName QN_CS_INACTIVE;
+extern const QName QN_CS_GONE;
+
+// JEP 0091 Delayed Delivery
+extern const std::string kNSDelay;
+extern const QName kQnDelayX;
+extern const QName kQnStamp;
+
+// Google time stamping (higher resolution)
+extern const std::string kNSTimestamp;
+extern const QName kQnTime;
+extern const QName kQnMilliseconds;
+
+
+extern const std::string NS_JINGLE_INFO;
+extern const QName QN_JINGLE_INFO_QUERY;
+extern const QName QN_JINGLE_INFO_STUN;
+extern const QName QN_JINGLE_INFO_RELAY;
+extern const QName QN_JINGLE_INFO_SERVER;
+extern const QName QN_JINGLE_INFO_TOKEN;
+extern const QName QN_JINGLE_INFO_HOST;
+extern const QName QN_JINGLE_INFO_TCP;
+extern const QName QN_JINGLE_INFO_UDP;
+extern const QName QN_JINGLE_INFO_TCPSSL;
+
+extern const std::string NS_GOOGLE_CALLPERF_STATS;
+extern const QName QN_CALLPERF_STATS;
+extern const QName QN_CALLPERF_SESSIONID;
+extern const QName QN_CALLPERF_LOCALUSER;
+extern const QName QN_CALLPERF_REMOTEUSER;
+extern const QName QN_CALLPERF_STARTTIME;
+extern const QName QN_CALLPERF_CALL_LENGTH;
+extern const QName QN_CALLPERF_DATAPOINT;
+extern const QName QN_CALLPERF_DATAPOINT_TIME;
+extern const QName QN_CALLPERF_DATAPOINT_FRACTION_LOST;
+extern const QName QN_CALLPERF_DATAPOINT_CUM_LOST;
+extern const QName QN_CALLPERF_DATAPOINT_EXT_MAX;
+extern const QName QN_CALLPERF_DATAPOINT_JITTER;
+extern const QName QN_CALLPERF_DATAPOINT_RTT;
+extern const QName QN_CALLPERF_DATAPOINT_BYTES_R;
+extern const QName QN_CALLPERF_DATAPOINT_PACKETS_R;
+extern const QName QN_CALLPERF_DATAPOINT_BYTES_S;
+extern const QName QN_CALLPERF_DATAPOINT_PACKETS_S;
+extern const QName QN_CALLPERF_CONNECTION;
+extern const QName QN_CALLPERF_CONNECTION_LOCAL_ADDRESS;
+extern const QName QN_CALLPERF_CONNECTION_REMOTE_ADDRESS;
+
+// Muc invites.
+extern const QName QN_MUC_USER_INVITE;
+
+// Multiway audio/video.
+extern const std::string NS_GOOGLE_MUC_USER;
+extern const QName QN_GOOGLE_MUC_USER_AVAILABLE_MEDIA;
+extern const QName QN_GOOGLE_MUC_USER_ENTRY;
+extern const QName QN_GOOGLE_MUC_USER_MEDIA;
+extern const QName QN_GOOGLE_MUC_USER_TYPE;
+extern const QName QN_GOOGLE_MUC_USER_SRC_ID;
+extern const QName QN_GOOGLE_MUC_USER_STATUS;
+extern const std::string NS_JINGLE;
+extern const QName QN_JINGLE_SRC_ID;
+extern const QName QN_LABEL;
+
+// Call terminate reasons
+extern const std::string STR_TERMINATE_CALL_ENDED;
+extern const std::string STR_TERMINATE_RECIPIENT_UNAVAILABLE;
+extern const std::string STR_TERMINATE_RECIPIENT_BUSY;
+extern const std::string STR_TERMINATE_INSUFFICIENT_FUNDS;
+extern const std::string STR_TERMINATE_NUMBER_MALFORMED;
+extern const std::string STR_TERMINATE_NUMBER_DISALLOWED;
+extern const std::string STR_TERMINATE_PROTOCOL_ERROR;
+extern const std::string STR_TERMINATE_INTERNAL_SERVER_ERROR;
+extern const std::string STR_TERMINATE_UNKNOWN_ERROR;
+
+} // namespace buzz
+
+#endif // TALK_XMPP_CONSTANTS_H_
diff --git a/third_party/libjingle/source/talk/xmpp/jid.cc b/third_party/libjingle/source/talk/xmpp/jid.cc
new file mode 100644
index 0000000..01a025f
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/jid.cc
@@ -0,0 +1,503 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/jid.h"
+
+#include <ctype.h>
+
+#include <algorithm>
+#include <string>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/xmpp/constants.h"
+
+namespace buzz {
+
+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 {
+ if (Compare(JID_EMPTY) == 0) {
+ LOG(LS_VERBOSE) << "Warning: Calling IsBare() on the empty jid";
+ return true;
+ }
+ 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;
+}
+
+uint32 Jid::ComputeLameHash() const {
+ uint32 hash = 0;
+ // Hash the node portion
+ {
+ const std::string &str = node();
+ for (int i = 0; i < static_cast<int>(str.size()); ++i) {
+ hash = ((hash << 2) + hash) + str[i];
+ }
+ }
+
+ // Hash the domain portion
+ {
+ const std::string &str = domain();
+ for (int i = 0; i < static_cast<int>(str.size()); ++i)
+ hash = ((hash << 2) + hash) + str[i];
+ }
+
+ // Hash the resource portion
+ {
+ const std::string &str = resource();
+ for (int i = 0; i < static_cast<int>(str.size()); ++i)
+ hash = ((hash << 2) + hash) + str[i];
+ }
+
+ return hash;
+}
+
+// --- 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;
+ unsigned 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;
+ unsigned 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;
+ unsigned 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/third_party/libjingle/source/talk/xmpp/jid.h b/third_party/libjingle/source/talk/xmpp/jid.h
new file mode 100644
index 0000000..6831bda
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/jid.h
@@ -0,0 +1,148 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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/base/basictypes.h"
+#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;
+
+ // A quick and dirty hash. Don't count on this producing a great
+ // distribution.
+ uint32 ComputeLameHash() 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/third_party/libjingle/source/talk/xmpp/plainsaslhandler.h b/third_party/libjingle/source/talk/xmpp/plainsaslhandler.h
new file mode 100644
index 0000000..e7d44b9
--- /dev/null
+++ b/third_party/libjingle/source/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 talk_base::CryptString & password,
+ bool allow_plain) : jid_(jid), password_(password),
+ allow_plain_(allow_plain) {}
+
+ 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) {
+
+ if (!encrypted && !allow_plain_) {
+ 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_;
+ talk_base::CryptString password_;
+ bool allow_plain_;
+};
+
+
+}
+
+#endif
+
diff --git a/third_party/libjingle/source/talk/xmpp/prexmppauth.h b/third_party/libjingle/source/talk/xmpp/prexmppauth.h
new file mode 100644
index 0000000..dce5e0b
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/prexmppauth.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 _PREXMPPAUTH_H_
+#define _PREXMPPAUTH_H_
+
+#include "talk/base/cryptstring.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/saslhandler.h"
+
+namespace talk_base {
+ 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 talk_base::SocketAddress & server,
+ const talk_base::CryptString & pass,
+ const std::string & auth_cookie) = 0;
+
+ sigslot::signal0<> SignalAuthDone;
+
+ virtual bool IsAuthDone() const = 0;
+ virtual bool IsAuthorized() const = 0;
+ virtual bool HadError() const = 0;
+ virtual int GetError() const = 0;
+ virtual CaptchaChallenge GetCaptchaChallenge() const = 0;
+ virtual std::string GetAuthCookie() const = 0;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmpp/ratelimitmanager.cc b/third_party/libjingle/source/talk/xmpp/ratelimitmanager.cc
new file mode 100644
index 0000000..14667a7
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/ratelimitmanager.cc
@@ -0,0 +1,80 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <list>
+#include <string>
+
+#include "talk/xmpp/ratelimitmanager.h"
+
+namespace buzz {
+
+RateLimitManager::RateLimit* RateLimitManager::GetRateLimit(
+ const std::string event_name) {
+ RateLimitMap::iterator it = rate_limits_.find(event_name);
+ if (it != rate_limits_.end()) {
+ return it->second;
+ }
+ return NULL;
+}
+
+bool RateLimitManager::IsWithinRateLimit(const std::string event_name) {
+ RateLimit* current_rate = GetRateLimit(event_name);
+ if (current_rate) {
+ return current_rate->IsWithinRateLimit();
+ }
+ return true; // If no rate limit is set, then you must be under the limit
+}
+
+void RateLimitManager::UpdateRateLimit(const std::string event_name,
+ int max_count,
+ int per_x_seconds) {
+ RateLimit* current_rate = GetRateLimit(event_name);
+ if (!current_rate) {
+ current_rate = new RateLimit(max_count, per_x_seconds);
+ rate_limits_[event_name] = current_rate;
+ }
+ current_rate->UpdateRateLimit();
+}
+
+bool RateLimitManager::VerifyRateLimit(const std::string event_name,
+ int max_count,
+ int per_x_seconds) {
+ return VerifyRateLimit(event_name, max_count, per_x_seconds, false);
+}
+
+bool RateLimitManager::VerifyRateLimit(const std::string event_name,
+ int max_count,
+ int per_x_seconds,
+ bool always_update) {
+ bool within_rate_limit = IsWithinRateLimit(event_name);
+ if (within_rate_limit || always_update) {
+ UpdateRateLimit(event_name, max_count, per_x_seconds);
+ }
+ return within_rate_limit;
+}
+
+}
diff --git a/third_party/libjingle/source/talk/xmpp/ratelimitmanager.h b/third_party/libjingle/source/talk/xmpp/ratelimitmanager.h
new file mode 100644
index 0000000..1a7fc82
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/ratelimitmanager.h
@@ -0,0 +1,141 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RATELIMITMANAGER_H_
+#define _RATELIMITMANAGER_H_
+
+#include "talk/base/time.h"
+#include "talk/base/taskrunner.h"
+#include <map>
+
+namespace buzz {
+
+/////////////////////////////////////////////////////////////////////
+//
+// RATELIMITMANAGER
+//
+/////////////////////////////////////////////////////////////////////
+//
+// RateLimitManager imposes client-side rate limiting for xmpp tasks and
+// other events. It ensures that no more than i events with a given name
+// can occur within k seconds.
+//
+// A buffer tracks the previous max_count events. Before an event is allowed
+// to occur, it can check its rate limit with a call to VerifyRateLimit.
+// VerifyRateLimit will look up the i-th to last event and if more than
+// k seconds have passed since then, it will return true and update the
+// appropriate rate limits. Else, it will return false.
+//
+/////////////////////////////////////////////////////////////////////
+
+class RateLimitManager {
+ public:
+
+ RateLimitManager() { };
+ ~RateLimitManager() {
+ for (RateLimitMap::iterator it = rate_limits_.begin();
+ it != rate_limits_.end(); ++it) {
+ delete it->second;
+ }
+ };
+
+ // Checks if the event is under the defined rate limit and updates the
+ // rate limit if so. Returns true if it's under the rate limit.
+ bool VerifyRateLimit(const std::string event_name, int max_count,
+ int per_x_seconds);
+
+ // Checks if the event is under the defined rate limit and updates the
+ // rate limit if so *or* if always_update = true.
+ bool VerifyRateLimit(const std::string event_name, int max_count,
+ int per_x_seconds, bool always_update);
+
+ private:
+ class RateLimit {
+ public:
+ RateLimit(int max, int per_x_secs) : counter_(0), max_count_(max),
+ per_x_seconds_(per_x_secs) {
+ event_times_ = new uint32[max_count_];
+ for (int i = 0; i < max_count_; i++) {
+ event_times_[i] = 0;
+ }
+ }
+
+ ~RateLimit() {
+ if (event_times_) {
+ delete[] event_times_;
+ }
+ }
+
+ // True iff the current time >= to the next song allowed time
+ bool IsWithinRateLimit() {
+ return (talk_base::TimeSince(NextTimeAllowedForCounter()) >= 0);
+ }
+
+ // Updates time and counter for rate limit
+ void UpdateRateLimit() {
+ event_times_[counter_] = talk_base::Time();
+ counter_ = (counter_ + 1) % max_count_;
+ }
+
+ private:
+
+ // The time at which the i-th (where i = max_count) event occured
+ uint32 PreviousTimeAtCounter() {
+ return event_times_[counter_];
+ }
+
+ // The time that the next event is allowed to occur
+ uint32 NextTimeAllowedForCounter() {
+ return PreviousTimeAtCounter() + per_x_seconds_ * talk_base::kSecToMsec;
+ }
+
+ int counter_; // count modulo max_count of the current event
+ int max_count_; // max number of events that can occur within per_x_seconds
+ int per_x_seconds_; // interval size for rate limit
+ uint32* event_times_; // buffer of previous max_count event
+ };
+
+ typedef std::map<const std::string, RateLimit*> RateLimitMap;
+
+ // Maps from event name to its rate limit
+ RateLimitMap rate_limits_;
+
+ // Returns rate limit for event with specified name
+ RateLimit* GetRateLimit(const std::string event_name);
+
+ // True iff the current time >= to the next song allowed time
+ bool IsWithinRateLimit(const std::string event_name);
+
+ // Updates time and counter for rate limit
+ void UpdateRateLimit(const std::string event_name, int max_count,
+ int per_x_seconds);
+
+};
+
+}
+
+#endif //_RATELIMITMANAGER_H_
diff --git a/third_party/libjingle/source/talk/xmpp/saslcookiemechanism.h b/third_party/libjingle/source/talk/xmpp/saslcookiemechanism.h
new file mode 100644
index 0000000..92cff4d
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/saslcookiemechanism.h
@@ -0,0 +1,88 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/saslmechanism.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,
+ const std::string & token_service)
+ : mechanism_(mechanism),
+ username_(username),
+ cookie_(cookie),
+ token_service_(token_service) {}
+
+ SaslCookieMechanism(const std::string & mechanism,
+ const std::string & username,
+ const std::string & cookie)
+ : mechanism_(mechanism),
+ username_(username),
+ cookie_(cookie),
+ token_service_("") {}
+
+ 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_);
+ if (!token_service_.empty()) {
+ el->AddAttr(
+ QName(true, "http://www.google.com/talk/protocol/auth", "service"),
+ token_service_);
+ }
+
+ 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_;
+ std::string token_service_;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmpp/saslhandler.h b/third_party/libjingle/source/talk/xmpp/saslhandler.h
new file mode 100644
index 0000000..bead8aa
--- /dev/null
+++ b/third_party/libjingle/source/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>
+#include <vector>
+
+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/third_party/libjingle/source/talk/xmpp/saslmechanism.cc b/third_party/libjingle/source/talk/xmpp/saslmechanism.cc
new file mode 100644
index 0000000..2645ac0
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/saslmechanism.cc
@@ -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.
+ */
+
+#include "talk/base/base64.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/saslmechanism.h"
+
+using talk_base::Base64;
+
+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, Base64::DO_LAX);
+}
+
+std::string
+SaslMechanism::Base64EncodeFromArray(const char * plain, size_t length) {
+ std::string result;
+ Base64::EncodeFromArray(plain, length, &result);
+ return result;
+}
+
+}
diff --git a/third_party/libjingle/source/talk/xmpp/saslmechanism.h b/third_party/libjingle/source/talk/xmpp/saslmechanism.h
new file mode 100644
index 0000000..f2e5adc
--- /dev/null
+++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/xmpp/saslplainmechanism.h b/third_party/libjingle/source/talk/xmpp/saslplainmechanism.h
new file mode 100644
index 0000000..72532e6
--- /dev/null
+++ b/third_party/libjingle/source/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/base/cryptstring.h"
+#include "talk/xmpp/saslmechanism.h"
+
+namespace buzz {
+
+class SaslPlainMechanism : public SaslMechanism {
+
+public:
+ SaslPlainMechanism(const buzz::Jid user_jid, const talk_base::CryptString & 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");
+
+ talk_base::FormatCryptString credential;
+ credential.Append("\0", 1);
+ credential.Append(user_jid_.node());
+ credential.Append("\0", 1);
+ credential.Append(&password_);
+ el->AddText(Base64EncodeFromArray(credential.GetData(), credential.GetLength()));
+ return el;
+ }
+
+private:
+ Jid user_jid_;
+ talk_base::CryptString password_;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmpp/xmppclient.cc b/third_party/libjingle/source/talk/xmpp/xmppclient.cc
new file mode 100644
index 0000000..d772998
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppclient.cc
@@ -0,0 +1,407 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 {
+
+talk_base::TaskParent* XmppClient::GetParent(int code) {
+ if (code == XMPP_CLIENT_TASK_CODE)
+ return this;
+ else
+ return talk_base::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),
+ pre_engine_subcode_(0),
+ signal_closed_(false),
+ allow_plain_(false) {}
+
+ // the owner
+ XmppClient * const client_;
+
+ // the two main objects
+ talk_base::scoped_ptr<AsyncSocket> socket_;
+ talk_base::scoped_ptr<XmppEngine> engine_;
+ talk_base::scoped_ptr<PreXmppAuth> pre_auth_;
+ talk_base::CryptString pass_;
+ std::string auth_cookie_;
+ talk_base::SocketAddress server_;
+ std::string proxy_host_;
+ int proxy_port_;
+ XmppEngine::Error pre_engine_error_;
+ int pre_engine_subcode_;
+ CaptchaChallenge captcha_challenge_;
+ bool signal_closed_;
+ bool allow_plain_;
+
+ // 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, const std::string & lang, 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());
+ if (!settings.resource().empty()) {
+ d_->engine_->SetRequestedResource(settings.resource());
+ }
+ d_->engine_->SetUseTls(settings.use_tls());
+
+ //
+ // The talk.google.com server expects you to use "gmail.com" in the
+ // stream, and expects the domain certificate to be "gmail.com" as well.
+ // For all other servers, we leave the strings empty, which causes
+ // the jid's domain to be used. "foo@example.com" -> stream to="example.com"
+ // tls certificate for "example.com"
+ //
+ // This is only true when using Gaia auth, so let's say if there's no preauth,
+ // we should use the actual server name
+ std::string server_name = settings.server().IPAsString();
+ if ((server_name == buzz::STR_TALK_GOOGLE_COM ||
+ server_name == buzz::STR_TALKX_L_GOOGLE_COM) &&
+ pre_auth != NULL) {
+ d_->engine_->SetTlsServer(buzz::STR_GMAIL_COM, buzz::STR_GMAIL_COM);
+ }
+
+ // Set language
+ d_->engine_->SetLanguage(lang);
+
+ d_->engine_->SetUser(buzz::Jid(settings.user(), settings.host(), STR_EMPTY));
+
+ 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_->allow_plain_ = settings.allow_plain();
+ 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(int *subcode) {
+ if (subcode) {
+ *subcode = 0;
+ }
+ if (d_->engine_.get() == NULL)
+ return XmppEngine::ERROR_NONE;
+ if (d_->pre_engine_error_ != XmppEngine::ERROR_NONE) {
+ if (subcode) {
+ *subcode = d_->pre_engine_subcode_;
+ }
+ return d_->pre_engine_error_;
+ }
+ return d_->engine_->GetError(subcode);
+}
+
+const XmlElement *
+XmppClient::GetStreamError() {
+ if (d_->engine_.get() == NULL) {
+ return NULL;
+ }
+ return d_->engine_->GetStreamError();
+}
+
+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_;
+}
+
+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_->allow_plain_));
+ 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;
+ d_->pre_engine_subcode_ = d_->pre_auth_->GetError();
+ }
+ else {
+ d_->pre_engine_error_ = XmppEngine::ERROR_UNAUTHORIZED;
+ d_->pre_engine_subcode_ = 0;
+ 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();
+ d_->socket_.reset(NULL);
+ return XMPP_RETURN_OK;
+}
+
+XmppClient::XmppClient(TaskParent * parent)
+ : Task(parent),
+ delivering_signal_(false),
+ valid_(false) {
+ d_.reset(new Private(this));
+ valid_ = true;
+}
+
+XmppClient::~XmppClient() {
+ valid_ = false;
+}
+
+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() {
+ int code = socket_->GetError();
+ engine_->ConnectionClosed(code);
+}
+
+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/third_party/libjingle/source/talk/xmpp/xmppclient.h b/third_party/libjingle/source/talk/xmpp/xmppclient.h
new file mode 100644
index 0000000..9983794
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppclient.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 _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 talk_base::Task, public sigslot::has_slots<>
+{
+public:
+ explicit XmppClient(talk_base::TaskParent * parent);
+ ~XmppClient();
+
+ XmppReturnStatus Connect(const XmppClientSettings & settings,
+ const std::string & lang,
+ AsyncSocket * socket,
+ PreXmppAuth * preauth);
+
+ virtual talk_base::TaskParent* 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(int *subcode);
+
+ // When there is a <stream:error> stanza, return the stanza
+ // so that they can be handled.
+ const XmlElement *GetStreamError();
+
+ // 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;
+ talk_base::scoped_ptr<Private> d_;
+
+ bool delivering_signal_;
+ bool valid_;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmpp/xmppclientsettings.h b/third_party/libjingle/source/talk/xmpp/xmppclientsettings.h
new file mode 100644
index 0000000..4f821ab
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppclientsettings.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 _XMPPCLIENTSETTINGS_H_
+#define _XMPPCLIENTSETTINGS_H_
+
+#include "talk/p2p/base/port.h"
+#include "talk/base/cryptstring.h"
+
+namespace buzz {
+
+class XmppUserSettings {
+ public:
+ XmppUserSettings()
+ : use_tls_(false),
+ allow_plain_(false) {
+ }
+
+ void set_user(const std::string & user) { user_ = user; }
+ void set_host(const std::string & host) { host_ = host; }
+ void set_pass(const talk_base::CryptString & 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_allow_plain(bool f) { allow_plain_ = f; }
+ void set_token_service(const std::string & token_service) {
+ token_service_ = token_service;
+ }
+
+ const std::string & user() const { return user_; }
+ const std::string & host() const { return host_; }
+ const talk_base::CryptString & 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_; }
+ bool allow_plain() const { return allow_plain_; }
+ const std::string & token_service() const { return token_service_; }
+
+ private:
+ std::string user_;
+ std::string host_;
+ talk_base::CryptString pass_;
+ std::string auth_cookie_;
+ std::string resource_;
+ bool use_tls_;
+ bool allow_plain_;
+ std::string token_service_;
+};
+
+class XmppClientSettings : public XmppUserSettings {
+ public:
+ XmppClientSettings()
+ : protocol_(cricket::PROTO_TCP),
+ proxy_(talk_base::PROXY_NONE),
+ proxy_port_(80),
+ use_proxy_auth_(false) {
+ }
+
+ void set_server(const talk_base::SocketAddress & server) {
+ server_ = server;
+ }
+ void set_protocol(cricket::ProtocolType protocol) { protocol_ = protocol; }
+ void set_proxy(talk_base::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 talk_base::CryptString & pass) { proxy_pass_ = pass; }
+
+ const talk_base::SocketAddress & server() const { return server_; }
+ cricket::ProtocolType protocol() const { return protocol_; }
+ talk_base::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 talk_base::CryptString & proxy_pass() const { return proxy_pass_; }
+
+ private:
+ talk_base::SocketAddress server_;
+ cricket::ProtocolType protocol_;
+ talk_base::ProxyType proxy_;
+ std::string proxy_host_;
+ int proxy_port_;
+ bool use_proxy_auth_;
+ std::string proxy_user_;
+ talk_base::CryptString proxy_pass_;
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmpp/xmppengine.h b/third_party/libjingle/source/talk/xmpp/xmppengine.h
new file mode 100644
index 0000000..44ed83d
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppengine.h
@@ -0,0 +1,341 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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:
+ virtual ~XmppOutputHandler() {}
+
+ //! 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:
+ virtual ~XmppSessionHandler() {}
+ //! 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:
+ virtual ~XmppStanzaHandler() {}
+ //! 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:
+ virtual ~XmppIqHandler() {}
+ //! 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
+ ERROR_NETWORK_TIMEOUT, //!< Some sort of timeout (eg., we never got the roster)
+ ERROR_MISSING_USERNAME //!< User has a Google Account but no nickname
+ };
+
+ //! 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(int subcode) = 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 SetTlsServer(const std::string & proxy_hostname,
+ 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;
+
+ //! Sets language
+ virtual void SetLanguage(const std::string & lang) = 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(int *subcode) = 0;
+
+ //! The stream:error stanza, when the error is XmppEngine::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/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc b/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc
new file mode 100644
index 0000000..9918050
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc
@@ -0,0 +1,498 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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"
+
+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),
+ subcode_(0),
+ stream_error_(NULL),
+ raised_reset_(false),
+ output_handler_(NULL),
+ session_handler_(NULL),
+ iq_entries_(new IqEntryVector()),
+ sasl_handler_(NULL),
+ output_(new std::stringstream()) {
+ 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);
+
+ // TODO(jliaw): The return value of the xml parser is not checked.
+ stanzaParser_.Parse(bytes, len, false);
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::ConnectionClosed(int subcode) {
+ if (state_ != STATE_CLOSED) {
+ EnterExit ee(this);
+ // If told that connection closed and not already closed,
+ // then connection was unpexectedly dropped.
+ if (subcode) {
+ SignalError(ERROR_SOCKET, subcode);
+ } else {
+ SignalError(ERROR_CONNECTION_CLOSED, 0); // no subcode
+ }
+ }
+ 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::SetTlsServer(const std::string & tls_server_hostname,
+ const std::string & tls_server_domain) {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ tls_server_hostname_ = tls_server_hostname;
+ 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, 0);
+ }
+}
+
+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, 0);
+}
+
+void
+XmppEngineImpl::InternalSendStart(const std::string & to) {
+ std::string hostname = tls_server_hostname_;
+ if (hostname.empty()) {
+ hostname = to;
+ }
+
+ // If not language is specified, the spec says use *
+ std::string lang = lang_;
+ if (lang.length() == 0)
+ lang = "*";
+
+ // 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=\"" << hostname << "\" "
+ << "xml:lang=\"" << lang << "\" "
+ << "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, 0);
+ }
+}
+
+void
+XmppEngineImpl::SignalError(Error errorCode, int subCode) {
+ if (state_ != STATE_CLOSED) {
+ error_code_ = errorCode;
+ subcode_ = subCode;
+ 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/third_party/libjingle/source/talk/xmpp/xmppengineimpl.h b/third_party/libjingle/source/talk/xmpp/xmppengineimpl.h
new file mode 100644
index 0000000..e5d3dc6
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppengineimpl.h
@@ -0,0 +1,278 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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 <vector>
+#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(int subcode);
+
+ // 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 SetTlsServer(const std::string & proxy_hostname,
+ 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();
+
+ //! Sets language
+ virtual void SetLanguage(const std::string & lang) {
+ lang_ = lang;
+ }
+
+ // 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(int *subcode) {
+ if (subcode) {
+ *subcode = subcode_;
+ }
+ return error_code_;
+ }
+
+ //! The stream:error stanza, when the error is XmppEngine::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, int subCode);
+ 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 ~StanzaParseHandler() {}
+ 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_hostname_;
+ std::string tls_server_domain_;
+ talk_base::scoped_ptr<XmppLoginTask> login_task_;
+ std::string lang_;
+
+ int next_id_;
+ Jid bound_jid_;
+ State state_;
+ bool encrypted_;
+ Error error_code_;
+ int subcode_;
+ talk_base::scoped_ptr<XmlElement> stream_error_;
+ bool raised_reset_;
+ XmppOutputHandler* output_handler_;
+ XmppSessionHandler* session_handler_;
+
+ typedef std::vector<XmppStanzaHandler*> StanzaHandlerVector;
+ talk_base::scoped_ptr<StanzaHandlerVector> stanza_handlers_[HL_COUNT];
+
+ typedef std::vector<XmppIqEntry*> IqEntryVector;
+ talk_base::scoped_ptr<IqEntryVector> iq_entries_;
+
+ talk_base::scoped_ptr<SaslHandler> sasl_handler_;
+
+ talk_base::scoped_ptr<std::stringstream> output_;
+};
+
+}
+
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmpp/xmppengineimpl_iq.cc b/third_party/libjingle/source/talk/xmpp/xmppengineimpl_iq.cc
new file mode 100644
index 0000000..5834b90
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppengineimpl_iq.cc
@@ -0,0 +1,277 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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"
+
+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/third_party/libjingle/source/talk/xmpp/xmpplogintask.cc b/third_party/libjingle/source/talk/xmpp/xmpplogintask.cc
new file mode 100644
index 0000000..537818c
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmpplogintask.cc
@@ -0,0 +1,380 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 <iostream>
+#include <string>
+#include <vector>
+#include "talk/base/base64.h"
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/saslmechanism.h"
+#include "talk/xmpp/xmppengineimpl.h"
+#include "talk/xmpp/xmpplogintask.h"
+
+using talk_base::ConstantLabel;
+
+namespace buzz {
+
+#ifdef _DEBUG
+const ConstantLabel XmppLoginTask::LOGINTASK_STATES[] = {
+ KLABEL(LOGINSTATE_INIT),
+ KLABEL(LOGINSTATE_STREAMSTART_SENT),
+ KLABEL(LOGINSTATE_STARTED_XMPP),
+ KLABEL(LOGINSTATE_TLS_INIT),
+ KLABEL(LOGINSTATE_AUTH_INIT),
+ KLABEL(LOGINSTATE_BIND_INIT),
+ KLABEL(LOGINSTATE_TLS_REQUESTED),
+ KLABEL(LOGINSTATE_SASL_RUNNING),
+ KLABEL(LOGINSTATE_BIND_REQUESTED),
+ KLABEL(LOGINSTATE_SESSION_REQUESTED),
+ KLABEL(LOGINSTATE_DONE),
+ LASTLABEL
+};
+#endif // _DEBUG
+
+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;
+
+#if _DEBUG
+ LOG(LS_VERBOSE) << "XmppLoginTask::Advance - "
+ << talk_base::ErrorName(state_, LOGINTASK_STATES);
+#endif // _DEBUG
+
+ switch (state_) {
+
+ case LOGINSTATE_INIT: {
+ pctx_->RaiseReset();
+ pelFeatures_.reset(NULL);
+
+ // 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_->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);
+
+ // Use TLS if forced, or if available
+ if (pctx_->tls_needed_ || GetFeature(QN_TLS_STARTTLS) != NULL) {
+ 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);
+ }
+
+ // 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, 0);
+ 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/third_party/libjingle/source/talk/xmpp/xmpplogintask.h b/third_party/libjingle/source/talk/xmpp/xmpplogintask.h
new file mode 100644
index 0000000..a6507b5
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmpplogintask.h
@@ -0,0 +1,98 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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/base/logging.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/xmppengine.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_;
+ talk_base::scoped_ptr<XmlElement> pelFeatures_;
+ Jid fullJid_;
+ std::string streamId_;
+ talk_base::scoped_ptr<std::vector<XmlElement *> > pvecQueuedStanzas_;
+
+ talk_base::scoped_ptr<SaslMechanism> sasl_mech_;
+
+#ifdef _DEBUG
+ static const talk_base::ConstantLabel LOGINTASK_STATES[];
+#endif // _DEBUG
+};
+
+}
+
+#endif
diff --git a/third_party/libjingle/source/talk/xmpp/xmppstanzaparser.cc b/third_party/libjingle/source/talk/xmpp/xmppstanzaparser.cc
new file mode 100644
index 0000000..3aced15
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppstanzaparser.cc
@@ -0,0 +1,105 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/xmllite/xmlelement.h"
+#include "talk/base/common.h"
+#include "talk/xmpp/xmppstanzaparser.h"
+#include "talk/xmpp/constants.h"
+#ifdef EXPAT_RELATIVE_PATH
+#include "lib/expat.h"
+#else
+#include "third_party/expat/v2_0_1/Source/lib/expat.h"
+#endif
+
+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/third_party/libjingle/source/talk/xmpp/xmppstanzaparser.h b/third_party/libjingle/source/talk/xmpp/xmppstanzaparser.h
new file mode 100644
index 0000000..c6f8b08
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmppstanzaparser.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 _xmppstanzaparser_h_
+#define _xmppstanzaparser_h_
+
+#include "talk/xmllite/xmlparser.h"
+#include "talk/xmllite/xmlbuilder.h"
+
+
+namespace buzz {
+
+class XmlElement;
+
+class XmppStanzaParseHandler {
+public:
+ virtual ~XmppStanzaParseHandler() {}
+ 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/third_party/libjingle/source/talk/xmpp/xmpptask.cc b/third_party/libjingle/source/talk/xmpp/xmpptask.cc
new file mode 100644
index 0000000..bbd50e3
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmpptask.cc
@@ -0,0 +1,175 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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"
+#include "talk/xmpp/ratelimitmanager.h"
+
+namespace buzz {
+
+RateLimitManager task_rate_manager;
+
+XmppTask::XmppTask(TaskParent* parent, XmppEngine::HandlerLevel level)
+ : Task(parent), client_(NULL) {
+#ifdef _DEBUG
+ debug_force_timeout_ = false;
+#endif
+
+ XmppClient* client =
+ static_cast<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) {
+#ifdef _DEBUG
+ if (debug_force_timeout_)
+ return;
+#endif
+
+ 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)
+ return true;
+
+ // We address the server as "", check if we are doing so here.
+ if (to != JID_EMPTY)
+ return false;
+
+ // It is legal for the server to identify itself with "domain" or
+ // "myself@domain"
+ Jid me = client_->jid();
+ return (from == Jid(me.domain())) || (from == me.BareJid());
+}
+
+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;
+}
+
+bool XmppTask::VerifyTaskRateLimit(const std::string task_name, int max_count,
+ int per_x_seconds) {
+ return task_rate_manager.VerifyRateLimit(task_name, max_count,
+ per_x_seconds);
+}
+
+}
diff --git a/third_party/libjingle/source/talk/xmpp/xmpptask.h b/third_party/libjingle/source/talk/xmpp/xmpptask.h
new file mode 100644
index 0000000..84e2fe0
--- /dev/null
+++ b/third_party/libjingle/source/talk/xmpp/xmpptask.h
@@ -0,0 +1,130 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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 talk_base::Task,
+ public XmppStanzaHandler,
+ public sigslot::has_slots<>
+{
+ public:
+ XmppTask(talk_base::TaskParent* parent,
+ XmppEngine::HandlerLevel level = XmppEngine::HL_NONE);
+ virtual ~XmppTask();
+
+ virtual XmppClient* GetClient() const { return client_; }
+ std::string task_id() const { return id_; }
+ void set_task_id(std::string id) { id_ = id; }
+
+#ifdef _DEBUG
+ void set_debug_force_timeout(const bool f) { debug_force_timeout_ = f; }
+#endif
+
+ 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; }
+
+ virtual void QueueStanza(const XmlElement* stanza);
+ const XmlElement* NextStanza();
+
+ bool MatchResponseIq(const XmlElement* stanza, const Jid& to,
+ const std::string& task_id);
+
+ static bool MatchRequestIq(const XmlElement* stanza, const std::string& type,
+ const QName& qn);
+ static XmlElement *MakeIqResult(const XmlElement* query);
+ static XmlElement *MakeIq(const std::string& type,
+ const Jid& to, const std::string& task_id);
+
+ // Returns true if the task is under the specified rate limit and updates the
+ // rate limit accordingly
+ bool VerifyTaskRateLimit(const std::string task_name, int max_count,
+ int per_x_seconds);
+
+private:
+ void StopImpl();
+
+ XmppClient* client_;
+ std::deque<XmlElement*> stanza_queue_;
+ talk_base::scoped_ptr<XmlElement> next_stanza_;
+ std::string id_;
+
+#ifdef _DEBUG
+ bool debug_force_timeout_;
+#endif
+};
+
+}
+
+#endif