Adds trunk/talk folder of revision 359 from libjingles google code to
trunk/talk


git-svn-id: http://webrtc.googlecode.com/svn/trunk@4318 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/examples/chat/Info.plist b/talk/examples/chat/Info.plist
new file mode 100644
index 0000000..ecd083a
--- /dev/null
+++ b/talk/examples/chat/Info.plist
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleIdentifier</key>
+  <string>com.google.call</string>
+  <key>CFBundleName</key>
+  <string>chat</string>
+</dict>
+</plist>
+
diff --git a/talk/examples/chat/chat_main.cc b/talk/examples/chat/chat_main.cc
new file mode 100644
index 0000000..09a454e
--- /dev/null
+++ b/talk/examples/chat/chat_main.cc
@@ -0,0 +1,159 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 simple text chat application, largely copied from examples/call.
+//
+
+#include <iostream>
+
+#include "talk/base/logging.h"
+#include "talk/base/ssladapter.h"
+
+#ifdef OSX
+#include "talk/base/maccocoasocketserver.h"
+#elif defined(WIN32)
+#include "talk/base/win32socketserver.h"
+#else
+#include "talk/base/physicalsocketserver.h"
+#endif
+
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmppauth.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/xmpp/xmpppump.h"
+#include "talk/xmpp/xmppsocket.h"
+
+#include "talk/examples/chat/chatapp.h"
+#include "talk/examples/chat/consoletask.h"
+
+static const int kDefaultPort = 5222;
+
+int main(int argc, char* argv[]) {
+  // TODO(pmclean): Remove duplication of code with examples/call.
+  // Set up debugging.
+  bool debug = true;
+  if (debug) {
+    talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE);
+  }
+
+  // Set up the crypto subsystem.
+  talk_base::InitializeSSL();
+
+  // 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];
+    }
+  }
+
+  // ... else prompt for them
+  if (username.empty()) {
+    printf("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()) {
+    buzz::ConsoleTask::SetEcho(false);
+    printf("Password: ");
+    std::cin >> pass.password();
+    buzz::ConsoleTask::SetEcho(true);
+    printf("\n");
+  }
+
+  // OTP (this can be skipped)
+  std::string otp_token;
+  printf("OTP: ");
+  fflush(stdin);
+  std::getline(std::cin, otp_token);
+
+  // Setup the connection settings.
+  buzz::XmppClientSettings xcs;
+  xcs.set_user(jid.node());
+  xcs.set_resource("chat");
+  xcs.set_host(jid.domain());
+  bool allow_plain = false;
+  xcs.set_allow_plain(allow_plain);
+  xcs.set_use_tls(buzz::TLS_REQUIRED);
+  xcs.set_pass(talk_base::CryptString(pass));
+  if (!otp_token.empty() && *otp_token.c_str() != '\n') {
+    xcs.set_auth_token(buzz::AUTH_MECHANISM_OAUTH2, otp_token);
+  }
+
+  // Build the server spec
+  std::string host;
+  int port;
+
+  std::string server = "talk.google.com";
+  int colon = server.find(':');
+  if (colon == -1) {
+    host = server;
+    port = kDefaultPort;
+  } else {
+    host = server.substr(0, colon);
+    port = atoi(server.substr(colon + 1).c_str());
+  }
+  xcs.set_server(talk_base::SocketAddress(host, port));
+
+  talk_base::Thread* main_thread = talk_base::Thread::Current();
+#if WIN32
+  // Need to pump messages on our main thread on Windows.
+  talk_base::Win32Thread w32_thread;
+  talk_base::ThreadManager::Instance()->SetCurrentThread(&w32_thread);
+#elif defined(OSX)
+  talk_base::MacCocoaSocketServer ss;
+  talk_base::SocketServerScope ss_scope(&ss);
+#else
+  talk_base::PhysicalSocketServer ss;
+#endif
+
+  buzz::XmppPump* pump = new buzz::XmppPump();
+  ChatApp *client = new ChatApp(pump->client(), main_thread);
+
+  // Start pumping messages!
+  pump->DoLogin(xcs, new buzz::XmppSocket(buzz::TLS_REQUIRED), new XmppAuth());
+
+  main_thread->Run();
+  pump->DoDisconnect();
+
+  delete client;
+
+  return 0;
+}
diff --git a/talk/examples/chat/chatapp.cc b/talk/examples/chat/chatapp.cc
new file mode 100644
index 0000000..1b59910
--- /dev/null
+++ b/talk/examples/chat/chatapp.cc
@@ -0,0 +1,251 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/chat/chatapp.h"
+
+#include "talk/examples/chat/consoletask.h"
+#include "talk/examples/chat/textchatsendtask.h"
+#include "talk/examples/chat/textchatreceivetask.h"
+#include "talk/xmpp/presenceouttask.h"
+#include "talk/xmpp/presencereceivetask.h"
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+ChatApp::ChatApp(buzz::XmppClient* xmpp_client, talk_base::Thread* main_thread)
+  : xmpp_client_(xmpp_client),
+    presence_out_task_(NULL),
+    presence_receive_task_(NULL),
+    message_send_task_(NULL),
+    message_received_task_(NULL),
+    console_task_(new buzz::ConsoleTask(main_thread)),
+    ui_state_(STATE_BASE) {
+  xmpp_client_->SignalStateChange.connect(this, &ChatApp::OnStateChange);
+
+  console_task_->TextInputHandler.connect(this, &ChatApp::OnConsoleMessage);
+  console_task_->Start();
+}
+
+ChatApp::~ChatApp() {
+  if (presence_out_task_ != NULL) {
+    // Check out
+    BroadcastPresence(away);
+  }
+}
+
+void ChatApp::Quit() {
+  talk_base::Thread::Current()->Quit();
+}
+
+void ChatApp::OnXmppOpen() {
+  presence_out_task_.reset(new buzz::PresenceOutTask(xmpp_client_));
+  presence_receive_task_.reset(new buzz::PresenceReceiveTask(xmpp_client_));
+  presence_receive_task_->PresenceUpdate.connect(this,
+                                                 &ChatApp::OnPresenceUpdate);
+  message_send_task_.reset(new buzz::TextChatSendTask(xmpp_client_));
+  message_received_task_.reset(new buzz::TextChatReceiveTask(xmpp_client_));
+  message_received_task_->SignalTextChatReceived.connect(
+      this, &ChatApp::OnTextMessage);
+
+  presence_out_task_->Start();
+  presence_receive_task_->Start();
+  message_send_task_->Start();
+  message_received_task_->Start();
+}
+
+void ChatApp::BroadcastPresence(PresenceState state) {
+  buzz::PresenceStatus status;
+  status.set_jid(xmpp_client_->jid());
+  status.set_available(state == online);
+  status.set_show(state == online ? buzz::PresenceStatus::SHOW_ONLINE
+                                  : buzz::PresenceStatus::SHOW_AWAY);
+  presence_out_task_->Send(status);
+}
+
+// UI Stuff
+static const char* kMenuChoiceQuit = "0";
+static const char* kMenuChoiceRoster = "1";
+static const char* kMenuChoiceChat = "2";
+
+static const char* kUIStrings[3][2] = {
+  {kMenuChoiceQuit, "Quit"},
+  {kMenuChoiceRoster, "Roster"},
+  {kMenuChoiceChat, "Send"}};
+
+void ChatApp::PrintMenu() {
+  char buff[128];
+  int numMenuItems = sizeof(kUIStrings) / sizeof(kUIStrings[0]);
+  for (int index = 0; index < numMenuItems; ++index) {
+    snprintf(buff, sizeof(buff), "%s) %s\n", kUIStrings[index][0],
+                                             kUIStrings[index][1]);
+    console_task_->Print(buff);
+  }
+  console_task_->Print("choice:");
+}
+
+void ChatApp::PrintRoster() {
+  int index = 0;
+  for (RosterList::iterator iter = roster_list_.begin();
+     iter != roster_list_.end(); ++iter) {
+       const buzz::Jid& jid = iter->second.jid();
+       console_task_->Print(
+         "%d: (*) %s@%s [%s] \n",
+         index++,
+         jid.node().c_str(),
+         jid.domain().c_str(),
+         jid.resource().c_str());
+  }
+}
+
+void ChatApp::PromptJid() {
+  PrintRoster();
+  console_task_->Print("choice:");
+}
+
+void ChatApp::PromptChatMessage() {
+  console_task_->Print(":");
+}
+
+bool ChatApp::GetRosterItem(int index, buzz::PresenceStatus* status) {
+  int found_index = 0;
+  for (RosterList::iterator iter = roster_list_.begin();
+     iter != roster_list_.end() && found_index <= index; ++iter) {
+    if (found_index == index) {
+      *status = iter->second;
+      return true;
+    }
+    found_index++;
+  }
+
+  return false;
+}
+
+void ChatApp::HandleBaseInput(const std::string& message) {
+  if (message == kMenuChoiceQuit) {
+    Quit();
+  } else if (message == kMenuChoiceRoster) {
+    PrintRoster();
+  } else if (message == kMenuChoiceChat) {
+    ui_state_ = STATE_PROMPTJID;
+    PromptJid();
+  } else if (message == "") {
+    PrintMenu();
+  }
+}
+
+void ChatApp::HandleJidInput(const std::string& message) {
+  if (isdigit(message[0])) {
+    // It's an index-based roster choice.
+    int index = 0;
+    buzz::PresenceStatus status;
+    if (!talk_base::FromString(message, &index) ||
+        !GetRosterItem(index, &status)) {
+      // fail, so drop back
+      ui_state_ = STATE_BASE;
+      return;
+    }
+
+    chat_dest_jid_ = status.jid();
+  } else {
+    // It's an explicit address.
+    chat_dest_jid_ = buzz::Jid(message.c_str());
+  }
+  ui_state_ = STATE_CHATTING;
+  PromptChatMessage();
+}
+
+void ChatApp::HandleChatInput(const std::string& message) {
+  if (message == "") {
+    ui_state_ = STATE_BASE;
+    PrintMenu();
+  } else {
+    message_send_task_->Send(chat_dest_jid_, message);
+    PromptChatMessage();
+  }
+}
+
+// Connection state notifications
+void ChatApp::OnStateChange(buzz::XmppEngine::State state) {
+  switch (state) {
+  // Nonexistent state
+  case buzz::XmppEngine::STATE_NONE:
+    break;
+
+  // Nonexistent state
+  case buzz::XmppEngine::STATE_START:
+    break;
+
+  // Exchanging stream headers, authenticating and so on.
+  case buzz::XmppEngine::STATE_OPENING:
+    break;
+
+  // Authenticated and bound.
+  case buzz::XmppEngine::STATE_OPEN:
+    OnXmppOpen();
+    BroadcastPresence(online);
+    PrintMenu();
+    break;
+
+  // Session closed, possibly due to error.
+  case buzz::XmppEngine::STATE_CLOSED:
+    break;
+  }
+}
+
+// Presence Notifications
+void ChatApp::OnPresenceUpdate(const buzz::PresenceStatus& status) {
+  if (status.available()) {
+    roster_list_[status.jid().Str()] = status;
+  } else {
+    RosterList::iterator iter = roster_list_.find(status.jid().Str());
+    if (iter != roster_list_.end()) {
+      roster_list_.erase(iter);
+    }
+  }
+}
+
+// Text message handlers
+void ChatApp::OnTextMessage(const buzz::Jid& from, const buzz::Jid& to,
+                            const std::string& message) {
+  console_task_->Print("%s says: %s\n", from.node().c_str(), message.c_str());
+}
+
+void ChatApp::OnConsoleMessage(const std::string &message) {
+  switch (ui_state_) {
+    case STATE_BASE:
+      HandleBaseInput(message);
+      break;
+
+    case STATE_PROMPTJID:
+      HandleJidInput(message);
+      break;
+
+    case STATE_CHATTING:
+      HandleChatInput(message);
+      break;
+  }
+}
diff --git a/talk/examples/chat/chatapp.h b/talk/examples/chat/chatapp.h
new file mode 100644
index 0000000..cc032a6
--- /dev/null
+++ b/talk/examples/chat/chatapp.h
@@ -0,0 +1,171 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CHAT_CHATAPP_H_
+#define TALK_EXAMPLES_CHAT_CHATAPP_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/scoped_ptr.h"
+
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/xmppclient.h"
+
+namespace buzz {
+class XmppClient;
+class PresenceOutTask;
+class PresenceReceiveTask;
+class TextChatSendTask;
+class TextChatReceiveTask;
+class ConsoleTask;
+class PresenceStatus;
+}
+
+// This is an example chat app for libjingle, showing how to use xmpp tasks,
+// data, callbacks, etc.  It has a simple text-based UI for logging in,
+// sending and receiving messages, and printing the roster.
+class ChatApp: public sigslot::has_slots<> {
+ public:
+  // Arguments:
+  //   xmpp_client  Points to the XmppClient for the communication channel
+  //    (typically created by the XmppPump object).
+  //   main_thread  Wraps the application's main thread.  Subsidiary threads
+  //    for the various tasks will be forked off of this.
+  ChatApp(buzz::XmppClient* xmpp_client, talk_base::Thread* main_thread);
+
+  // Shuts down and releases all of the contained tasks/threads
+  ~ChatApp();
+
+  // Shuts down the current thread and quits
+  void Quit();
+
+ private:
+  //
+  // Initialization
+  //
+  // Called explicitly after the connection to the chat server is established.
+  void OnXmppOpen();
+
+  //
+  // UI Stuff
+  //
+  // Prints the app main menu on the console.
+  // Called when ui_state_ == STATE_BASE.
+  void PrintMenu();
+
+  // Prints a numbered list of the logged-in user's roster on the console.
+  void PrintRoster();
+
+  // Prints a prompt for the user to enter either the index from the
+  // roster list of the user they wish to chat with, or a fully-qualified
+  // (user@server.ext) jid.
+  // Called when when ui_state_ == STATE_PROMPTJID.
+  void PromptJid();
+
+  // Prints a prompt on the console for the user to enter a message to send.
+  // Called when when ui_state_ == STATE_CHATTING.
+  void PromptChatMessage();
+
+  // Sends our presence state to the chat server (and on to your roster list).
+  // Arguments:
+  //  state Specifies the presence state to show.
+  enum PresenceState {online, away};
+  void BroadcastPresence(PresenceState state);
+
+  // Returns the RosterItem associated with the specified index.
+  // Just a helper to select a roster item from a numbered list in the UI.
+  bool GetRosterItem(int index, buzz::PresenceStatus* status);
+
+  //
+  // Input Handling
+  //
+  // Receives input when ui_state_ == STATE_BASE.  Handles choices from the
+  // main menu.
+  void HandleBaseInput(const std::string& message);
+
+  // Receives input when ui_state_ == STATE_PROMPTJID.  Handles selection
+  // of a JID to chat to.
+  void HandleJidInput(const std::string& message);
+
+  // Receives input when ui_state_ == STATE_CHATTING.  Handles text messages.
+  void HandleChatInput(const std::string& message);
+
+  //
+  // signal/slot Callbacks
+  //
+  // Connected to the XmppClient::SignalStateChange slot.  Receives
+  // notifications of state changes of the connection.
+  void OnStateChange(buzz::XmppEngine::State state);
+
+  // Connected to the PresenceReceiveTask::PresenceUpdate slot.
+  // Receives status messages for the logged-in user's roster (i.e.
+  // an initial list from the server and people coming/going).
+  void OnPresenceUpdate(const buzz::PresenceStatus& status);
+
+  // Connected to the TextChatReceiveTask::SignalTextChatReceived slot.
+  // Called when we receive a text chat from someone else.
+  void OnTextMessage(const buzz::Jid& from, const buzz::Jid& to,
+                     const std::string& message);
+
+  // Receives text input from the console task.  This is where any input
+  // from the user comes in.
+  // Arguments:
+  //   message What the user typed.
+  void OnConsoleMessage(const std::string &message);
+
+  // The XmppClient object associated with this chat application instance.
+  buzz::XmppClient* xmpp_client_;
+
+  // We send presence information through this object.
+  talk_base::scoped_ptr<buzz::PresenceOutTask> presence_out_task_;
+
+  // We receive others presence information through this object.
+  talk_base::scoped_ptr<buzz::PresenceReceiveTask> presence_receive_task_;
+
+  // We send text messages though this object.
+  talk_base::scoped_ptr<buzz::TextChatSendTask> message_send_task_;
+
+  // We receive messages through this object.
+  talk_base::scoped_ptr<buzz::TextChatReceiveTask> message_received_task_;
+
+  // UI gets drawn and receives input through this task.
+  talk_base::scoped_ptr< buzz::ConsoleTask> console_task_;
+
+  // The list of JIDs for the people in the logged-in users roster.
+  // RosterList  roster_list_;
+  typedef std::map<std::string, buzz::PresenceStatus> RosterList;
+  RosterList roster_list_;
+
+  // The JID of the user currently being chatted with.
+  buzz::Jid chat_dest_jid_;
+
+  // UI State constants
+  enum UIState { STATE_BASE, STATE_PROMPTJID, STATE_CHATTING };
+  UIState ui_state_;
+};
+
+#endif  // TALK_EXAMPLES_CHAT_CHATAPP_H_
+
diff --git a/talk/examples/chat/consoletask.cc b/talk/examples/chat/consoletask.cc
new file mode 100644
index 0000000..2577c79
--- /dev/null
+++ b/talk/examples/chat/consoletask.cc
@@ -0,0 +1,177 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// TODO(pmclean): Perhaps this should be unified with examples/call/console.cc
+// and refactor to talk/base.
+#include "talk/examples/chat/consoletask.h"
+
+#define _CRT_SECURE_NO_DEPRECATE 1
+
+#include <stdarg.h>
+#ifdef POSIX
+#include <signal.h>
+#include <termios.h>
+#include <unistd.h>
+#endif  // POSIX
+#include <cassert>
+
+#include "talk/base/logging.h"
+
+#ifdef POSIX
+static void DoNothing(int unused) {}
+#endif
+
+namespace buzz {
+
+ConsoleTask::ConsoleTask(talk_base::Thread *thread) :
+  client_thread_(thread),
+  console_thread_(new talk_base::Thread()) {
+}
+
+ConsoleTask::~ConsoleTask() {
+  Stop();
+}
+
+void ConsoleTask::Start() {
+  if (!console_thread_) {
+    // 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 ConsoleTask::Stop() {
+  if (console_thread_ && 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 ConsoleTask::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  // MAC & LINUX
+  const int fd = fileno(stdin);
+  if (fd == -1) {
+    return;
+  }
+
+  struct termios tcflags;
+  if (tcgetattr(fd, &tcflags) == -1) {
+    return;
+  }
+
+  if (on) {
+    tcflags.c_lflag |= ECHO;
+  } else {
+    tcflags.c_lflag &= ~ECHO;
+  }
+
+  tcsetattr(fd, TCSANOW, &tcflags);
+#endif
+}
+
+void ConsoleTask::Print(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';
+  printf("%s", buf);
+  fflush(stdout);
+
+  va_end(ap);
+}
+
+void ConsoleTask::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 ConsoleTask::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);
+      // Trim off the .line-terminator to make processing easier.
+      std::string parsed_message =
+        data->data().substr(0, data->data().length() - 1);
+      TextInputHandler(parsed_message);
+      break;
+  }
+}
+
+}  // namespace buzz
diff --git a/talk/examples/chat/consoletask.h b/talk/examples/chat/consoletask.h
new file mode 100644
index 0000000..1d45b3a
--- /dev/null
+++ b/talk/examples/chat/consoletask.h
@@ -0,0 +1,92 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CHAT_CONSOLETASK_H_
+#define TALK_EXAMPLES_CHAT_CONSOLETASK_H_
+
+#include <cstdio>
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+
+namespace buzz {
+
+//
+// Provides properly threaded console I/O.
+//
+class ConsoleTask : public talk_base::MessageHandler {
+ public:
+  // Arguments:
+  // thread The main application thread.  Input messages get posted through
+  //   this.
+  explicit ConsoleTask(talk_base::Thread *thread);
+
+  // Shuts down the thread associated with this task.
+  ~ConsoleTask();
+
+  // Slot for text inputs handler.
+  sigslot::signal1<const std::string&> TextInputHandler;
+
+  // Starts reading lines from the console and passes them to the
+  //  TextInputHandler.
+  void Start();
+
+  // Stops reading lines and shuts down the thread.  Cannot be restarted.
+  void Stop();
+
+  // Thread messages (especialy text-input messages) come in through here.
+  virtual void OnMessage(talk_base::Message *msg);
+
+  // printf() style output to the console.
+  void Print(const char* format, ...);
+
+  // Turns on/off the echo of input characters on the console.
+  // Arguments:
+  //   on If true turns echo on, off otherwise.
+  static void SetEcho(bool on);
+
+ private:
+  /** Message IDs (for OnMessage()). */
+  enum {
+    MSG_START,
+    MSG_INPUT,
+  };
+
+  // Starts up polling for console input
+  void RunConsole();
+
+  // The main application thread
+  talk_base::Thread *client_thread_;
+
+  // The tread associated with this console object
+  talk_base::scoped_ptr<talk_base::Thread> console_thread_;
+};
+
+}  // namespace buzz
+
+#endif  // TALK_EXAMPLES_CHAT_CONSOLETASK_H_
+
diff --git a/talk/examples/chat/textchatreceivetask.cc b/talk/examples/chat/textchatreceivetask.cc
new file mode 100644
index 0000000..cbd019c
--- /dev/null
+++ b/talk/examples/chat/textchatreceivetask.cc
@@ -0,0 +1,66 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/chat/textchatreceivetask.h"
+
+#include "talk/xmpp/constants.h"
+
+namespace buzz {
+
+TextChatReceiveTask::TextChatReceiveTask(XmppTaskParentInterface* parent)
+  : XmppTask(parent, XmppEngine::HL_TYPE) {
+}
+
+TextChatReceiveTask::~TextChatReceiveTask() {
+  Stop();
+}
+
+bool TextChatReceiveTask::HandleStanza(const XmlElement* stanza) {
+  // Make sure that this stanza is a message
+  if (stanza->Name() != QN_MESSAGE) {
+    return false;
+  }
+
+  // see if there is any body
+  const XmlElement* message_body = stanza->FirstNamed(QN_BODY);
+  if (message_body == NULL) {
+    return false;
+  }
+
+  // Looks good, so send the message text along.
+  SignalTextChatReceived(Jid(stanza->Attr(QN_FROM)), Jid(stanza->Attr(QN_TO)),
+                         message_body->BodyText());
+
+  return true;
+}
+
+int TextChatReceiveTask::ProcessStart() {
+  // not queuing messages, so just block.
+  return STATE_BLOCKED;
+}
+
+}  // namespace buzz
diff --git a/talk/examples/chat/textchatreceivetask.h b/talk/examples/chat/textchatreceivetask.h
new file mode 100644
index 0000000..e277692
--- /dev/null
+++ b/talk/examples/chat/textchatreceivetask.h
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CHAT_TEXTCHATRECEIVETASK_H_
+#define TALK_EXAMPLES_CHAT_TEXTCHATRECEIVETASK_H_
+
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/xmpptask.h"
+
+namespace buzz {
+
+// A class to receive chat messages from the XMPP server.
+class TextChatReceiveTask : public XmppTask {
+ public:
+  // Arguments:
+  //   parent a reference to task interface associated withe the XMPP client.
+  explicit TextChatReceiveTask(XmppTaskParentInterface* parent);
+
+  // Shuts down the thread associated with this task.
+  virtual ~TextChatReceiveTask();
+
+  // Starts pulling queued status messages and dispatching them to the
+  // PresenceUpdate() callback.
+  virtual int ProcessStart();
+
+  // Slot for chat message callbacks
+  sigslot::signal3<const Jid&, const Jid&, const std::string&>
+      SignalTextChatReceived;
+
+ protected:
+  // Called by the XMPP client when chat stanzas arrive.  We pull out the
+  // interesting parts and send them to the SignalTextCharReceived() slot.
+  virtual bool HandleStanza(const XmlElement* stanza);
+};
+
+}  // namespace buzz
+
+#endif  // TALK_EXAMPLES_CHAT_TEXTCHATRECEIVETASK_H_
+
diff --git a/talk/examples/chat/textchatsendtask.cc b/talk/examples/chat/textchatsendtask.cc
new file mode 100644
index 0000000..ba14453
--- /dev/null
+++ b/talk/examples/chat/textchatsendtask.cc
@@ -0,0 +1,81 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/chat/textchatsendtask.h"
+
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmppclient.h"
+
+namespace buzz {
+TextChatSendTask::TextChatSendTask(XmppTaskParentInterface* parent)
+  : XmppTask(parent) {
+}
+
+TextChatSendTask::~TextChatSendTask() {
+  Stop();
+}
+
+XmppReturnStatus TextChatSendTask::Send(const Jid& to,
+                                        const std::string& textmessage) {
+  // Make sure we are actually connected.
+  if (GetState() != STATE_INIT && GetState() != STATE_START) {
+    return XMPP_RETURN_BADSTATE;
+  }
+
+  // Put together the chat stanza...
+  XmlElement* message_stanza = new XmlElement(QN_MESSAGE);
+
+  // ... and specify the required attributes...
+  message_stanza->AddAttr(QN_TO, to.Str());
+  message_stanza->AddAttr(QN_TYPE, "chat");
+  message_stanza->AddAttr(QN_LANG, "en");
+
+  // ... and fill out the body.
+  XmlElement* message_body = new XmlElement(QN_BODY);
+  message_body->AddText(textmessage);
+  message_stanza->AddElement(message_body);
+
+  // Now queue it up.
+  QueueStanza(message_stanza);
+
+  return XMPP_RETURN_OK;
+}
+
+int TextChatSendTask::ProcessStart() {
+  const XmlElement* stanza = NextStanza();
+  if (stanza == NULL) {
+    return STATE_BLOCKED;
+  }
+
+  if (SendStanza(stanza) != XMPP_RETURN_OK) {
+    return STATE_ERROR;
+  }
+
+  return STATE_START;
+}
+
+}  // namespace buzz
diff --git a/talk/examples/chat/textchatsendtask.h b/talk/examples/chat/textchatsendtask.h
new file mode 100644
index 0000000..9b18923
--- /dev/null
+++ b/talk/examples/chat/textchatsendtask.h
@@ -0,0 +1,56 @@
+/*
+ * libjingle
+ * Copyright 2004--2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_CHAT_TEXTCHATSENDTASK_H_
+#define TALK_EXAMPLES_CHAT_TEXTCHATSENDTASK_H_
+
+#include "talk/xmpp/xmpptask.h"
+
+namespace buzz {
+
+// A class to send chat messages to the XMPP server.
+class TextChatSendTask : public XmppTask {
+ public:
+  // Arguments:
+  //   parent a reference to task interface associated withe the XMPP client.
+  explicit TextChatSendTask(XmppTaskParentInterface* parent);
+
+  // Shuts down the thread associated with this task.
+  virtual ~TextChatSendTask();
+
+  // Forms the XMPP "chat" stanza with the specified receipient and message
+  // and queues it up.
+  XmppReturnStatus Send(const Jid& to, const std::string& message);
+
+  // Picks up any "chat" stanzas from our queue and sends them to the server.
+  virtual int ProcessStart();
+};
+
+}  // namespace buzz
+
+#endif  // TALK_EXAMPLES_CHAT_TEXTCHATSENDTASK_H_
+