| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * libjingle | |
| 3 * Copyright 2004--2005, Google Inc. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions are met: | |
| 7 * | |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | |
| 9 * this list of conditions and the following disclaimer. | |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 11 * this list of conditions and the following disclaimer in the documentation | |
| 12 * and/or other materials provided with the distribution. | |
| 13 * 3. The name of the author may not be used to endorse or promote products | |
| 14 * derived from this software without specific prior written permission. | |
| 15 * | |
| 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
| 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
| 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
| 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
| 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
| 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
| 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 26 */ | |
| 27 | |
| 28 #include <stdio.h> | |
| 29 #include <string.h> | |
| 30 #include <time.h> | |
| 31 | |
| 32 #include <iomanip> | |
| 33 #include <iostream> | |
| 34 #include <vector> | |
| 35 | |
| 36 #include "webrtc/base/flags.h" | |
| 37 #include "webrtc/base/logging.h" | |
| 38 #ifdef OSX | |
| 39 #include "webrtc/base/maccocoasocketserver.h" | |
| 40 #endif | |
| 41 #include "talk/examples/call/callclient.h" | |
| 42 #include "talk/examples/call/console.h" | |
| 43 #include "talk/examples/call/mediaenginefactory.h" | |
| 44 #include "talk/session/media/srtpfilter.h" | |
| 45 #include "webrtc/base/pathutils.h" | |
| 46 #include "webrtc/base/ssladapter.h" | |
| 47 #include "webrtc/base/stream.h" | |
| 48 #include "webrtc/base/win32socketserver.h" | |
| 49 #include "webrtc/libjingle/session/media/mediasessionclient.h" | |
| 50 #include "webrtc/libjingle/xmpp/xmppauth.h" | |
| 51 #include "webrtc/libjingle/xmpp/xmppclientsettings.h" | |
| 52 #include "webrtc/libjingle/xmpp/xmpppump.h" | |
| 53 #include "webrtc/libjingle/xmpp/xmppsocket.h" | |
| 54 #include "webrtc/p2p/base/constants.h" | |
| 55 | |
| 56 class DebugLog : public sigslot::has_slots<> { | |
| 57 public: | |
| 58 DebugLog() : | |
| 59 debug_input_buf_(NULL), debug_input_len_(0), debug_input_alloc_(0), | |
| 60 debug_output_buf_(NULL), debug_output_len_(0), debug_output_alloc_(0), | |
| 61 censor_password_(false) | |
| 62 {} | |
| 63 char * debug_input_buf_; | |
| 64 int debug_input_len_; | |
| 65 int debug_input_alloc_; | |
| 66 char * debug_output_buf_; | |
| 67 int debug_output_len_; | |
| 68 int debug_output_alloc_; | |
| 69 bool censor_password_; | |
| 70 | |
| 71 void Input(const char * data, int len) { | |
| 72 if (debug_input_len_ + len > debug_input_alloc_) { | |
| 73 char * old_buf = debug_input_buf_; | |
| 74 debug_input_alloc_ = 4096; | |
| 75 while (debug_input_alloc_ < debug_input_len_ + len) { | |
| 76 debug_input_alloc_ *= 2; | |
| 77 } | |
| 78 debug_input_buf_ = new char[debug_input_alloc_]; | |
| 79 memcpy(debug_input_buf_, old_buf, debug_input_len_); | |
| 80 delete[] old_buf; | |
| 81 } | |
| 82 memcpy(debug_input_buf_ + debug_input_len_, data, len); | |
| 83 debug_input_len_ += len; | |
| 84 DebugPrint(debug_input_buf_, &debug_input_len_, false); | |
| 85 } | |
| 86 | |
| 87 void Output(const char * data, int len) { | |
| 88 if (debug_output_len_ + len > debug_output_alloc_) { | |
| 89 char * old_buf = debug_output_buf_; | |
| 90 debug_output_alloc_ = 4096; | |
| 91 while (debug_output_alloc_ < debug_output_len_ + len) { | |
| 92 debug_output_alloc_ *= 2; | |
| 93 } | |
| 94 debug_output_buf_ = new char[debug_output_alloc_]; | |
| 95 memcpy(debug_output_buf_, old_buf, debug_output_len_); | |
| 96 delete[] old_buf; | |
| 97 } | |
| 98 memcpy(debug_output_buf_ + debug_output_len_, data, len); | |
| 99 debug_output_len_ += len; | |
| 100 DebugPrint(debug_output_buf_, &debug_output_len_, true); | |
| 101 } | |
| 102 | |
| 103 static bool IsAuthTag(const char * str, size_t len) { | |
| 104 if (str[0] == '<' && str[1] == 'a' && | |
| 105 str[2] == 'u' && | |
| 106 str[3] == 't' && | |
| 107 str[4] == 'h' && | |
| 108 str[5] <= ' ') { | |
| 109 std::string tag(str, len); | |
| 110 | |
| 111 if (tag.find("mechanism") != std::string::npos) | |
| 112 return true; | |
| 113 } | |
| 114 return false; | |
| 115 } | |
| 116 | |
| 117 void DebugPrint(char * buf, int * plen, bool output) { | |
| 118 int len = *plen; | |
| 119 if (len > 0) { | |
| 120 time_t tim = time(NULL); | |
| 121 struct tm * now = localtime(&tim); | |
| 122 char *time_string = asctime(now); | |
| 123 if (time_string) { | |
| 124 size_t time_len = strlen(time_string); | |
| 125 if (time_len > 0) { | |
| 126 time_string[time_len-1] = 0; // trim off terminating \n | |
| 127 } | |
| 128 } | |
| 129 LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>" : "RECV <<<<<<<<<<<<<<<<") | |
| 130 << " : " << time_string; | |
| 131 | |
| 132 bool indent; | |
| 133 int start = 0, nest = 3; | |
| 134 for (int i = 0; i < len; i += 1) { | |
| 135 if (buf[i] == '>') { | |
| 136 if ((i > 0) && (buf[i-1] == '/')) { | |
| 137 indent = false; | |
| 138 } else if ((start + 1 < len) && (buf[start + 1] == '/')) { | |
| 139 indent = false; | |
| 140 nest -= 2; | |
| 141 } else { | |
| 142 indent = true; | |
| 143 } | |
| 144 | |
| 145 // Output a tag | |
| 146 LOG(INFO) << std::setw(nest) << " " | |
| 147 << std::string(buf + start, i + 1 - start); | |
| 148 | |
| 149 if (indent) | |
| 150 nest += 2; | |
| 151 | |
| 152 // Note if it's a PLAIN auth tag | |
| 153 if (IsAuthTag(buf + start, i + 1 - start)) { | |
| 154 censor_password_ = true; | |
| 155 } | |
| 156 | |
| 157 // incr | |
| 158 start = i + 1; | |
| 159 } | |
| 160 | |
| 161 if (buf[i] == '<' && start < i) { | |
| 162 if (censor_password_) { | |
| 163 LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##"; | |
| 164 censor_password_ = false; | |
| 165 } else { | |
| 166 LOG(INFO) << std::setw(nest) << " " | |
| 167 << std::string(buf + start, i - start); | |
| 168 } | |
| 169 start = i; | |
| 170 } | |
| 171 } | |
| 172 len = len - start; | |
| 173 memcpy(buf, buf + start, len); | |
| 174 *plen = len; | |
| 175 } | |
| 176 } | |
| 177 }; | |
| 178 | |
| 179 static DebugLog debug_log_; | |
| 180 static const int DEFAULT_PORT = 5222; | |
| 181 | |
| 182 #ifdef ANDROID | |
| 183 static std::vector<cricket::AudioCodec> codecs; | |
| 184 static const cricket::AudioCodec ISAC(103, "ISAC", 40000, 16000, 1, 0); | |
| 185 | |
| 186 cricket::MediaEngineInterface *CreateAndroidMediaEngine() { | |
| 187 cricket::FakeMediaEngine *engine = new cricket::FakeMediaEngine(); | |
| 188 | |
| 189 codecs.push_back(ISAC); | |
| 190 engine->SetAudioCodecs(codecs); | |
| 191 return engine; | |
| 192 } | |
| 193 #endif | |
| 194 | |
| 195 // TODO: Move this into Console. | |
| 196 void Print(const char* chars) { | |
| 197 printf("%s", chars); | |
| 198 fflush(stdout); | |
| 199 } | |
| 200 | |
| 201 bool GetSecurePolicy(const std::string& in, cricket::SecurePolicy* out) { | |
| 202 if (in == "disable") { | |
| 203 *out = cricket::SEC_DISABLED; | |
| 204 } else if (in == "enable") { | |
| 205 *out = cricket::SEC_ENABLED; | |
| 206 } else if (in == "require") { | |
| 207 *out = cricket::SEC_REQUIRED; | |
| 208 } else { | |
| 209 return false; | |
| 210 } | |
| 211 return true; | |
| 212 } | |
| 213 | |
| 214 int main(int argc, char **argv) { | |
| 215 // This app has three threads. The main thread will run the XMPP client, | |
| 216 // which will print to the screen in its own thread. A second thread | |
| 217 // will get input from the console, parse it, and pass the appropriate | |
| 218 // message back to the XMPP client's thread. A third thread is used | |
| 219 // by MediaSessionClient as its worker thread. | |
| 220 | |
| 221 // define options | |
| 222 DEFINE_string(s, "talk.google.com", "The connection server to use."); | |
| 223 DEFINE_string(tls, "require", | |
| 224 "Select connection encryption: disable, enable, require."); | |
| 225 DEFINE_bool(allowplain, false, "Allow plain authentication."); | |
| 226 DEFINE_bool(testserver, false, "Use test server."); | |
| 227 DEFINE_string(oauth, "", "OAuth2 access token."); | |
| 228 DEFINE_bool(a, false, "Turn on auto accept for incoming calls."); | |
| 229 DEFINE_string(signaling, "hybrid", | |
| 230 "Initial signaling protocol to use: jingle, gingle, or hybrid."); | |
| 231 DEFINE_string(transport, "hybrid", | |
| 232 "Initial transport protocol to use: ice, gice, or hybrid."); | |
| 233 DEFINE_string(sdes, "enable", | |
| 234 "Select SDES media encryption: disable, enable, require."); | |
| 235 DEFINE_string(dtls, "disable", | |
| 236 "Select DTLS transport encryption: disable, enable, require."); | |
| 237 DEFINE_int(portallocator, 0, "Filter out unwanted connection types."); | |
| 238 DEFINE_string(pmuc, "groupchat.google.com", "The persistant muc domain."); | |
| 239 DEFINE_string(capsnode, "http://code.google.com/p/libjingle/call", | |
| 240 "Caps node: A URI identifying the app."); | |
| 241 DEFINE_string(capsver, "0.6", | |
| 242 "Caps ver: A string identifying the version of the app."); | |
| 243 DEFINE_string(voiceinput, NULL, "RTP dump file for voice input."); | |
| 244 DEFINE_string(voiceoutput, NULL, "RTP dump file for voice output."); | |
| 245 DEFINE_string(videoinput, NULL, "RTP dump file for video input."); | |
| 246 DEFINE_string(videooutput, NULL, "RTP dump file for video output."); | |
| 247 DEFINE_bool(render, true, "Renders the video."); | |
| 248 DEFINE_string(datachannel, "", | |
| 249 "Enable a data channel, and choose the type: rtp or sctp."); | |
| 250 DEFINE_bool(d, false, "Turn on debugging."); | |
| 251 DEFINE_bool(debugsrtp, false, "Enable debugging for srtp."); | |
| 252 DEFINE_bool(help, false, "Prints this message"); | |
| 253 DEFINE_bool(multisession, false, | |
| 254 "Enable support for multiple sessions in calls."); | |
| 255 DEFINE_bool(roster, false, | |
| 256 "Enable roster messages printed in console."); | |
| 257 | |
| 258 // parse options | |
| 259 rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true); | |
| 260 if (FLAG_help) { | |
| 261 rtc::FlagList::Print(NULL, false); | |
| 262 return 0; | |
| 263 } | |
| 264 | |
| 265 bool auto_accept = FLAG_a; | |
| 266 bool debug = FLAG_d; | |
| 267 std::string signaling = FLAG_signaling; | |
| 268 std::string transport = FLAG_transport; | |
| 269 bool test_server = FLAG_testserver; | |
| 270 bool allow_plain = FLAG_allowplain; | |
| 271 std::string tls = FLAG_tls; | |
| 272 std::string oauth_token = FLAG_oauth; | |
| 273 int32 portallocator_flags = FLAG_portallocator; | |
| 274 std::string pmuc_domain = FLAG_pmuc; | |
| 275 std::string server = FLAG_s; | |
| 276 std::string sdes = FLAG_sdes; | |
| 277 std::string dtls = FLAG_dtls; | |
| 278 std::string caps_node = FLAG_capsnode; | |
| 279 std::string caps_ver = FLAG_capsver; | |
| 280 bool debugsrtp = FLAG_debugsrtp; | |
| 281 bool render = FLAG_render; | |
| 282 std::string data_channel = FLAG_datachannel; | |
| 283 bool multisession_enabled = FLAG_multisession; | |
| 284 rtc::SSLIdentity* ssl_identity = NULL; | |
| 285 bool show_roster_messages = FLAG_roster; | |
| 286 | |
| 287 // Set up debugging. | |
| 288 if (debug) { | |
| 289 rtc::LogMessage::LogToDebug(rtc::LS_VERBOSE); | |
| 290 } | |
| 291 | |
| 292 if (debugsrtp) { | |
| 293 cricket::EnableSrtpDebugging(); | |
| 294 } | |
| 295 | |
| 296 // Set up the crypto subsystem. | |
| 297 rtc::InitializeSSL(); | |
| 298 | |
| 299 // Parse username and password, if present. | |
| 300 buzz::Jid jid; | |
| 301 std::string username; | |
| 302 rtc::InsecureCryptStringImpl pass; | |
| 303 if (argc > 1) { | |
| 304 username = argv[1]; | |
| 305 if (argc > 2) { | |
| 306 pass.password() = argv[2]; | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 if (username.empty()) { | |
| 311 Print("JID: "); | |
| 312 std::cin >> username; | |
| 313 } | |
| 314 if (username.find('@') == std::string::npos) { | |
| 315 username.append("@localhost"); | |
| 316 } | |
| 317 jid = buzz::Jid(username); | |
| 318 if (!jid.IsValid() || jid.node() == "") { | |
| 319 Print("Invalid JID. JIDs should be in the form user@domain\n"); | |
| 320 return 1; | |
| 321 } | |
| 322 if (pass.password().empty() && !test_server && oauth_token.empty()) { | |
| 323 Console::SetEcho(false); | |
| 324 Print("Password: "); | |
| 325 std::cin >> pass.password(); | |
| 326 Console::SetEcho(true); | |
| 327 Print("\n"); | |
| 328 } | |
| 329 | |
| 330 // Decide on the connection settings. | |
| 331 buzz::XmppClientSettings xcs; | |
| 332 xcs.set_user(jid.node()); | |
| 333 xcs.set_resource("call"); | |
| 334 xcs.set_host(jid.domain()); | |
| 335 xcs.set_allow_plain(allow_plain); | |
| 336 | |
| 337 if (tls == "disable") { | |
| 338 xcs.set_use_tls(buzz::TLS_DISABLED); | |
| 339 } else if (tls == "enable") { | |
| 340 xcs.set_use_tls(buzz::TLS_ENABLED); | |
| 341 } else if (tls == "require") { | |
| 342 xcs.set_use_tls(buzz::TLS_REQUIRED); | |
| 343 } else { | |
| 344 Print("Invalid TLS option, must be enable, disable, or require.\n"); | |
| 345 return 1; | |
| 346 } | |
| 347 | |
| 348 if (test_server) { | |
| 349 pass.password() = jid.node(); | |
| 350 xcs.set_allow_plain(true); | |
| 351 xcs.set_use_tls(buzz::TLS_DISABLED); | |
| 352 xcs.set_test_server_domain("google.com"); | |
| 353 } | |
| 354 xcs.set_pass(rtc::CryptString(pass)); | |
| 355 if (!oauth_token.empty()) { | |
| 356 xcs.set_auth_token(buzz::AUTH_MECHANISM_OAUTH2, oauth_token); | |
| 357 } | |
| 358 | |
| 359 std::string host; | |
| 360 int port; | |
| 361 | |
| 362 int colon = server.find(':'); | |
| 363 if (colon == -1) { | |
| 364 host = server; | |
| 365 port = DEFAULT_PORT; | |
| 366 } else { | |
| 367 host = server.substr(0, colon); | |
| 368 port = atoi(server.substr(colon + 1).c_str()); | |
| 369 } | |
| 370 | |
| 371 xcs.set_server(rtc::SocketAddress(host, port)); | |
| 372 | |
| 373 // Decide on the signaling and crypto settings. | |
| 374 cricket::SignalingProtocol signaling_protocol = cricket::PROTOCOL_HYBRID; | |
| 375 if (signaling == "jingle") { | |
| 376 signaling_protocol = cricket::PROTOCOL_JINGLE; | |
| 377 } else if (signaling == "gingle") { | |
| 378 signaling_protocol = cricket::PROTOCOL_GINGLE; | |
| 379 } else if (signaling == "hybrid") { | |
| 380 signaling_protocol = cricket::PROTOCOL_HYBRID; | |
| 381 } else { | |
| 382 Print("Invalid signaling protocol. Must be jingle, gingle, or hybrid.\n"); | |
| 383 return 1; | |
| 384 } | |
| 385 | |
| 386 cricket::TransportProtocol transport_protocol = cricket::ICEPROTO_HYBRID; | |
| 387 if (transport == "ice") { | |
| 388 transport_protocol = cricket::ICEPROTO_RFC5245; | |
| 389 } else if (transport == "gice") { | |
| 390 transport_protocol = cricket::ICEPROTO_GOOGLE; | |
| 391 } else if (transport == "hybrid") { | |
| 392 transport_protocol = cricket::ICEPROTO_HYBRID; | |
| 393 } else { | |
| 394 Print("Invalid transport protocol. Must be ice, gice, or hybrid.\n"); | |
| 395 return 1; | |
| 396 } | |
| 397 | |
| 398 cricket::DataChannelType data_channel_type = cricket::DCT_NONE; | |
| 399 if (data_channel == "rtp") { | |
| 400 data_channel_type = cricket::DCT_RTP; | |
| 401 } else if (data_channel == "sctp") { | |
| 402 data_channel_type = cricket::DCT_SCTP; | |
| 403 } else if (!data_channel.empty()) { | |
| 404 Print("Invalid data channel type. Must be rtp or sctp.\n"); | |
| 405 return 1; | |
| 406 } | |
| 407 | |
| 408 cricket::SecurePolicy sdes_policy, dtls_policy; | |
| 409 if (!GetSecurePolicy(sdes, &sdes_policy)) { | |
| 410 Print("Invalid SDES policy. Must be enable, disable, or require.\n"); | |
| 411 return 1; | |
| 412 } | |
| 413 if (!GetSecurePolicy(dtls, &dtls_policy)) { | |
| 414 Print("Invalid DTLS policy. Must be enable, disable, or require.\n"); | |
| 415 return 1; | |
| 416 } | |
| 417 if (dtls_policy != cricket::SEC_DISABLED) { | |
| 418 ssl_identity = rtc::SSLIdentity::Generate(jid.Str()); | |
| 419 if (!ssl_identity) { | |
| 420 Print("Failed to generate identity for DTLS.\n"); | |
| 421 return 1; | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 #ifdef ANDROID | |
| 426 MediaEngineFactory::SetCreateFunction(&CreateAndroidMediaEngine); | |
| 427 #endif | |
| 428 | |
| 429 #if WIN32 | |
| 430 // Need to pump messages on our main thread on Windows. | |
| 431 rtc::Win32Thread w32_thread; | |
| 432 rtc::ThreadManager::Instance()->SetCurrentThread(&w32_thread); | |
| 433 #endif | |
| 434 rtc::Thread* main_thread = rtc::Thread::Current(); | |
| 435 #ifdef OSX | |
| 436 rtc::MacCocoaSocketServer ss; | |
| 437 rtc::SocketServerScope ss_scope(&ss); | |
| 438 #endif | |
| 439 | |
| 440 buzz::XmppPump pump; | |
| 441 CallClient *client = new CallClient(pump.client(), caps_node, caps_ver); | |
| 442 | |
| 443 if (FLAG_voiceinput || FLAG_voiceoutput || | |
| 444 FLAG_videoinput || FLAG_videooutput) { | |
| 445 // If any dump file is specified, we use a FileMediaEngine. | |
| 446 cricket::MediaEngineInterface* engine = | |
| 447 MediaEngineFactory::CreateFileMediaEngine( | |
| 448 FLAG_voiceinput, FLAG_voiceoutput, | |
| 449 FLAG_videoinput, FLAG_videooutput); | |
| 450 client->SetMediaEngine(engine); | |
| 451 } | |
| 452 | |
| 453 Console *console = new Console(main_thread, client); | |
| 454 client->SetConsole(console); | |
| 455 client->SetAutoAccept(auto_accept); | |
| 456 client->SetPmucDomain(pmuc_domain); | |
| 457 client->SetPortAllocatorFlags(portallocator_flags); | |
| 458 client->SetAllowLocalIps(true); | |
| 459 client->SetSignalingProtocol(signaling_protocol); | |
| 460 client->SetTransportProtocol(transport_protocol); | |
| 461 client->SetSecurePolicy(sdes_policy, dtls_policy); | |
| 462 client->SetSslIdentity(ssl_identity); | |
| 463 client->SetRender(render); | |
| 464 client->SetDataChannelType(data_channel_type); | |
| 465 client->SetMultiSessionEnabled(multisession_enabled); | |
| 466 client->SetShowRosterMessages(show_roster_messages); | |
| 467 console->Start(); | |
| 468 | |
| 469 if (debug) { | |
| 470 pump.client()->SignalLogInput.connect(&debug_log_, &DebugLog::Input); | |
| 471 pump.client()->SignalLogOutput.connect(&debug_log_, &DebugLog::Output); | |
| 472 } | |
| 473 | |
| 474 Print(("Logging in to " + server + " as " + jid.Str() + "\n").c_str()); | |
| 475 pump.DoLogin(xcs, new buzz::XmppSocket(buzz::TLS_REQUIRED), new XmppAuth()); | |
| 476 main_thread->Run(); | |
| 477 pump.DoDisconnect(); | |
| 478 | |
| 479 console->Stop(); | |
| 480 delete console; | |
| 481 delete client; | |
| 482 | |
| 483 return 0; | |
| 484 } | |
| OLD | NEW |