1f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch/*
2f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * libjingle
3f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * Copyright 2004--2005, Google Inc.
4f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *
5f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * Redistribution and use in source and binary forms, with or without
6f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * modification, are permitted provided that the following conditions are met:
7f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *
8f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *  1. Redistributions of source code must retain the above copyright notice,
9f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *     this list of conditions and the following disclaimer.
10f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *  2. Redistributions in binary form must reproduce the above copyright notice,
11f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *     this list of conditions and the following disclaimer in the documentation
12f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *     and/or other materials provided with the distribution.
13f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *  3. The name of the author may not be used to endorse or promote products
14f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *     derived from this software without specific prior written permission.
15f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch *
16f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch */
27f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
28f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/callclient.h"
29f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
30f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include <string>
31f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
32dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/base/basicpacketsocketfactory.h"
33f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/helpers.h"
34dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/base/logging.h"
35f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/network.h"
36f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/socketaddress.h"
374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "talk/base/stringencode.h"
38dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/base/stringutils.h"
39dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/base/thread.h"
40f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/console.h"
41f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/presencepushtask.h"
42f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/presenceouttask.h"
43f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/mucinviterecvtask.h"
44f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/mucinvitesendtask.h"
45f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/friendinvitesendtask.h"
46f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/muc.h"
47f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/examples/call/voicemailjidrequester.h"
48dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/p2p/base/sessionmanager.h"
49dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/p2p/client/basicportallocator.h"
50dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/p2p/client/sessionmanagertask.h"
51dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/session/phone/devicemanager.h"
52dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/session/phone/mediaengine.h"
53dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/session/phone/mediasessionclient.h"
54dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "talk/xmpp/constants.h"
55f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
56f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
57f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochclass NullRenderer : public cricket::VideoRenderer {
58f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch public:
59f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  explicit NullRenderer(const char* s) : s_(s) {}
60f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch private:
61f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  bool SetSize(int width, int height, int reserved) {
62f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    LOG(LS_INFO) << "Video size for " << s_ << ": " << width << "x" << height;
63f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return true;
64f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
65f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  bool RenderFrame(const cricket::VideoFrame *frame) {
66f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return true;
67f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
68f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  const char* s_;
69f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch};
70f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
71f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochnamespace {
72f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
73f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst char* DescribeStatus(buzz::Status::Show show, const std::string& desc) {
74f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  switch (show) {
75f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::Status::SHOW_XA:      return desc.c_str();
76f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::Status::SHOW_ONLINE:  return "online";
77f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::Status::SHOW_AWAY:    return "away";
78f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::Status::SHOW_DND:     return "do not disturb";
79f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::Status::SHOW_CHAT:    return "ready to chat";
80f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  default:                         return "offline";
81f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
82f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
83f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochstd::string GetWord(const std::vector<std::string>& words,
854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                    size_t index, const std::string& def) {
864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (words.size() > index) {
874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return words[index];
884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return def;
904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochint GetInt(const std::vector<std::string>& words, size_t index, int def) {
944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  int val;
954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (words.size() > index && talk_base::FromString(words[index], &val)) {
964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return val;
974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return def;
994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
1004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
103f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}  // namespace
104f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
105f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst char* CALL_COMMANDS =
106f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"Available commands:\n"
107f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"\n"
108f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  hangup  Ends the call.\n"
109f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  mute    Stops sending voice.\n"
110f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  unmute  Re-starts sending voice.\n"
111f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  dtmf    Sends a DTMF tone.\n"
112f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  quit    Quits the application.\n"
113f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"";
114f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
115f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst char* RECEIVE_COMMANDS =
116f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"Available commands:\n"
117f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"\n"
1184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch"  accept [bw] Accepts the incoming call and switches to it.\n"
119f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  reject  Rejects the incoming call and stays with the current call.\n"
120f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  quit    Quits the application.\n"
121f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"";
122f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
123f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst char* CONSOLE_COMMANDS =
124f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"Available commands:\n"
125f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"\n"
126f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  roster              Prints the online friends from your roster.\n"
127f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  friend user         Request to add a user to your roster.\n"
1284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch"  call [jid] [bw]     Initiates a call to the user[/room] with the\n"
1294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch"                      given JID and with optional bandwidth.\n"
1304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch"  vcall [jid] [bw]    Initiates a video call to the user[/room] with\n"
1314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch"                      the given JID and with optional bandwidth.\n"
132f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  voicemail [jid]     Leave a voicemail for the user with the given JID.\n"
133f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  join [room]         Joins a multi-user-chat.\n"
134f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  invite user [room]  Invites a friend to a multi-user-chat.\n"
135f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  leave [room]        Leaves a multi-user-chat.\n"
136f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  getdevs             Prints the available media devices.\n"
137f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"  quit                Quits the application.\n"
138f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch"";
139f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
140f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::ParseLine(const std::string& line) {
141f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  std::vector<std::string> words;
142f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  int start = -1;
143f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  int state = 0;
144f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  for (int index = 0; index <= static_cast<int>(line.size()); ++index) {
145f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (state == 0) {
146f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      if (!isspace(line[index])) {
147f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        start = index;
148f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        state = 1;
149f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      }
150f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    } else {
1513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      ASSERT(state == 1);
1523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      ASSERT(start >= 0);
153f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      if (isspace(line[index])) {
154f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        std::string word(line, start, index - start);
155f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        words.push_back(word);
156f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        start = -1;
157f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        state = 0;
158f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      }
159f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
160f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
161f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
162f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // Global commands
1634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  const std::string& command = GetWord(words, 0, "");
1644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (command == "quit") {
165f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    Quit();
166f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (call_ && incoming_call_) {
1674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if (command == "accept") {
1684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      cricket::CallOptions options;
1694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      options.video_bandwidth = GetInt(words, 1, cricket::kAutoBandwidth);
1704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      Accept(options);
1714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "reject") {
172f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      Reject();
173f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    } else {
174f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      console_->Print(RECEIVE_COMMANDS);
175f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
176f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (call_) {
1774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if (command == "hangup") {
178731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // TODO: do more shutdown here, move to Terminate()
179f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      call_->Terminate();
180f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      call_ = NULL;
181f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      session_ = NULL;
182f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      console_->SetPrompt(NULL);
1834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "mute") {
184f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      call_->Mute(true);
1854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "unmute") {
186f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      call_->Mute(false);
1874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if ((command == "dtmf") && (words.size() == 2)) {
188731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      int ev = std::string("0123456789*#").find(words[1][0]);
189731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      call_->PressDTMF(ev);
190f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    } else {
191f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      console_->Print(CALL_COMMANDS);
192f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
193f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else {
1944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if (command == "roster") {
195f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      PrintRoster();
1964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "send") {
1974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      buzz::Jid jid(words[1]);
1984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if (jid.IsValid()) {
1994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        last_sent_to_ = words[1];
2004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        SendChat(words[1], words[2]);
2014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      } else if (!last_sent_to_.empty()) {
2024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        SendChat(last_sent_to_, words[1]);
2034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      } else {
2044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        console_->Printf(
2054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            "Invalid JID. JIDs should be in the form user@domain\n");
2064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      }
2074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if ((words.size() == 2) && (command == "friend")) {
208f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      InviteFriend(words[1]);
2094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "call") {
2104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      std::string to = GetWord(words, 1, "");
2114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      MakeCallTo(to, cricket::CallOptions());
2124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "vcall") {
2134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      std::string to = GetWord(words, 1, "");
2144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      int bandwidth = GetInt(words, 2, cricket::kAutoBandwidth);
2154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      cricket::CallOptions options;
2164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      options.is_video = true;
2174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      options.video_bandwidth = bandwidth;
2184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      MakeCallTo(to, options);
2194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "join") {
2204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      JoinMuc(GetWord(words, 1, ""));
2214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if ((words.size() >= 2) && (command == "invite")) {
2224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      InviteToMuc(words[1], GetWord(words, 2, ""));
2234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "leave") {
2244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      LeaveMuc(GetWord(words, 1, ""));
2254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "getdevs") {
226f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      GetDevices();
2274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if ((words.size() == 2) && (command == "setvol")) {
228f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      SetVolume(words[1]);
2294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else if (command == "voicemail") {
230f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      CallVoicemail((words.size() >= 2) ? words[1] : "");
231f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    } else {
232f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      console_->Print(CONSOLE_COMMANDS);
233f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
234f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
235f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
236f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
237f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen MurdochCallClient::CallClient(buzz::XmppClient* xmpp_client)
238dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    : xmpp_client_(xmpp_client),
239dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      media_engine_(NULL),
240dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      media_client_(NULL),
241dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      call_(NULL),
242dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      incoming_call_(false),
243dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      auto_accept_(false),
244dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      pmuc_domain_("groupchat.google.com"),
245dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      local_renderer_(NULL),
246dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      remote_renderer_(NULL),
247dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      roster_(new RosterMap),
248dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      portallocator_flags_(0),
249dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      allow_local_ips_(false),
250dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      initial_protocol_(cricket::PROTOCOL_HYBRID),
251dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      secure_policy_(cricket::SEC_DISABLED) {
252f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange);
253f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
254f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
255f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen MurdochCallClient::~CallClient() {
256f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  delete media_client_;
257f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  delete roster_;
258f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
259f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
260f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst std::string CallClient::strerror(buzz::XmppEngine::Error err) {
261f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  switch (err) {
262f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_NONE:
263f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "";
264f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_XML:
265f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "Malformed XML or encoding error";
266f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_STREAM:
267f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "XMPP stream error";
268f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_VERSION:
269f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "XMPP version error";
270f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_UNAUTHORIZED:
271f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "User is not authorized (Check your username and password)";
272f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_TLS:
273f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "TLS could not be negotiated";
274f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_AUTH:
275f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "Authentication could not be negotiated";
276f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_BIND:
277f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "Resource or session binding could not be negotiated";
278f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_CONNECTION_CLOSED:
279f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "Connection closed by output handler.";
280f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_DOCUMENT_CLOSED:
281f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "Closed by </stream:stream>";
282f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    case  buzz::XmppEngine::ERROR_SOCKET:
283f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "Socket error";
284f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    default:
285f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return "Unknown error";
286f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
287f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
288f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
289f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnCallDestroy(cricket::Call* call) {
290f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (call == call_) {
291f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (remote_renderer_) {
292f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      delete remote_renderer_;
293f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      remote_renderer_ = NULL;
294f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
295f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (local_renderer_) {
296f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      delete local_renderer_;
297f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      local_renderer_ = NULL;
298f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
299f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->SetPrompt(NULL);
300f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("call destroyed");
301f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    call_ = NULL;
302f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    session_ = NULL;
303f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
304f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
305f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
306f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnStateChange(buzz::XmppEngine::State state) {
307f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  switch (state) {
308f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::XmppEngine::STATE_START:
309f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("connecting...");
310f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    break;
311f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
312f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::XmppEngine::STATE_OPENING:
313f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("logging in...");
314f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    break;
315f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
316f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::XmppEngine::STATE_OPEN:
317f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("logged in...");
318f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    InitPhone();
319f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    InitPresence();
320f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    break;
321f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
322f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  case buzz::XmppEngine::STATE_CLOSED:
323f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    buzz::XmppEngine::Error error = xmpp_client_->GetError(NULL);
324f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("logged out..." + strerror(error));
325f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    Quit();
326f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
327f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
328f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
329f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::InitPhone() {
330f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  std::string client_unique = xmpp_client_->jid().Str();
331f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  talk_base::InitRandom(client_unique.c_str(), client_unique.size());
332f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
333f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  worker_thread_ = new talk_base::Thread();
334f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // The worker thread must be started here since initialization of
335f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // the ChannelManager will generate messages that need to be
336f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // dispatched by it.
337f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  worker_thread_->Start();
338f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
339dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // TODO: It looks like we are leaking many
340dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // objects. E.g. |network_manager_| and |socket_factory_| are never
341dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // deleted.
342dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
343f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  network_manager_ = new talk_base::NetworkManager();
344dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  socket_factory_ = new talk_base::BasicPacketSocketFactory(worker_thread_);
345f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
346731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // TODO: Decide if the relay address should be specified here.
347f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  talk_base::SocketAddress stun_addr("stun.l.google.com", 19302);
348dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  port_allocator_ = new cricket::BasicPortAllocator(
349dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      network_manager_, socket_factory_, stun_addr,
350dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      talk_base::SocketAddress(), talk_base::SocketAddress(),
351dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      talk_base::SocketAddress());
352f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
353f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (portallocator_flags_ != 0) {
354f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    port_allocator_->set_flags(portallocator_flags_);
355f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
356f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  session_manager_ = new cricket::SessionManager(
357f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      port_allocator_, worker_thread_);
358f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  session_manager_->SignalRequestSignaling.connect(
359f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      this, &CallClient::OnRequestSignaling);
3604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  session_manager_->SignalSessionCreate.connect(
3614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      this, &CallClient::OnSessionCreate);
362f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  session_manager_->OnSignalingReady();
363f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
364f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  session_manager_task_ =
365f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      new cricket::SessionManagerTask(xmpp_client_, session_manager_);
366f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  session_manager_task_->EnableOutgoingMessages();
367f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  session_manager_task_->Start();
368f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
369f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (!media_engine_) {
370dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    media_engine_ = cricket::MediaEngine::Create();
371f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
372f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
373f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_ = new cricket::MediaSessionClient(
374f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      xmpp_client_->jid(),
375f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      session_manager_,
376f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      media_engine_,
377dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      new cricket::DeviceManager());
378f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate);
379f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->SignalDevicesChange.connect(this,
380f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                                             &CallClient::OnDevicesChange);
381dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  media_client_->set_secure(secure_policy_);
382f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
383f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
384f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnRequestSignaling() {
385f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  session_manager_->OnSignalingReady();
386f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
387f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
3884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CallClient::OnSessionCreate(cricket::Session* session, bool initiate) {
3894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  session->set_allow_local_ips(allow_local_ips_);
3904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  session->set_current_protocol(initial_protocol_);
3914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
3924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
393f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnCallCreate(cricket::Call* call) {
394f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  call->SignalSessionState.connect(this, &CallClient::OnSessionState);
395f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (call->video()) {
396f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    local_renderer_ = new NullRenderer("local");
397f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    remote_renderer_ = new NullRenderer("remote");
398f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
399f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
400f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
401f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnSessionState(cricket::Call* call,
402f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                                cricket::BaseSession* session,
403f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                                cricket::BaseSession::State state) {
404f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (state == cricket::Session::STATE_RECEIVEDINITIATE) {
405f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    buzz::Jid jid(session->remote_name());
406f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Incoming call from '%s'", jid.Str().c_str());
407f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    call_ = call;
408f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    session_ = session;
409f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    incoming_call_ = true;
4104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    cricket::CallOptions options;
411f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (auto_accept_) {
4124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      Accept(options);
413f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
414f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (state == cricket::Session::STATE_SENTINITIATE) {
415f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("calling...");
416f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (state == cricket::Session::STATE_RECEIVEDACCEPT) {
417f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("call answered");
418f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (state == cricket::Session::STATE_RECEIVEDREJECT) {
419f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("call not answered");
420f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (state == cricket::Session::STATE_INPROGRESS) {
421f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("call in progress");
422f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) {
423f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Print("other side hung up");
424f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
425f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
426f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
427f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::InitPresence() {
428f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_push_ = new buzz::PresencePushTask(xmpp_client_, this);
429f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_push_->SignalStatusUpdate.connect(
430f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    this, &CallClient::OnStatusUpdate);
431f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_push_->SignalMucJoined.connect(this, &CallClient::OnMucJoined);
432f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_push_->SignalMucLeft.connect(this, &CallClient::OnMucLeft);
433f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_push_->SignalMucStatusUpdate.connect(
434f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    this, &CallClient::OnMucStatusUpdate);
435f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_push_->Start();
436f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
437f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_out_ = new buzz::PresenceOutTask(xmpp_client_);
438f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  RefreshStatus();
439f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_out_->Start();
440f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
441f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  muc_invite_recv_ = new buzz::MucInviteRecvTask(xmpp_client_);
442f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  muc_invite_recv_->SignalInviteReceived.connect(this,
443f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      &CallClient::OnMucInviteReceived);
444f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  muc_invite_recv_->Start();
445f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
446f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  muc_invite_send_ = new buzz::MucInviteSendTask(xmpp_client_);
447f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  muc_invite_send_->Start();
448f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
449f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  friend_invite_send_ = new buzz::FriendInviteSendTask(xmpp_client_);
450f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  friend_invite_send_->Start();
451f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
452f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
453f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::RefreshStatus() {
454f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  int media_caps = media_client_->GetCapabilities();
455f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_jid(xmpp_client_->jid());
456f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_available(true);
457f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_show(buzz::Status::SHOW_ONLINE);
458f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_priority(0);
459f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_know_capabilities(true);
460f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_pmuc_capability(true);
461f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_phone_capability(
462f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      (media_caps & cricket::MediaEngine::AUDIO_RECV) != 0);
463f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_video_capability(
464f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      (media_caps & cricket::MediaEngine::VIDEO_RECV) != 0);
465f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_camera_capability(
466f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      (media_caps & cricket::MediaEngine::VIDEO_SEND) != 0);
467f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_is_google_client(true);
468f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  my_status_.set_version("1.0.0.67");
469f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_out_->Send(my_status_);
470f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
471f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
472f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnStatusUpdate(const buzz::Status& status) {
473f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  RosterItem item;
474f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  item.jid = status.jid();
475f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  item.show = status.show();
476f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  item.status = status.status();
477f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
478f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  std::string key = item.jid.Str();
479f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
480f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (status.available() && status.phone_capability()) {
481f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch     console_->Printf("Adding to roster: %s", key.c_str());
482f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    (*roster_)[key] = item;
483f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else {
484f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Removing from roster: %s", key.c_str());
485f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    RosterMap::iterator iter = roster_->find(key);
486f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (iter != roster_->end())
487f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      roster_->erase(iter);
488f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
489f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
490f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
491f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::PrintRoster() {
492f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->SetPrompting(false);
493f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->Printf("Roster contains %d callable", roster_->size());
494f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  RosterMap::iterator iter = roster_->begin();
495f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  while (iter != roster_->end()) {
496f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("%s - %s",
497f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                     iter->second.jid.BareJid().Str().c_str(),
498f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                     DescribeStatus(iter->second.show, iter->second.status));
499f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    iter++;
500f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
501f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->SetPrompting(true);
502f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
503f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
5044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CallClient::SendChat(const std::string& to, const std::string msg) {
5054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  buzz::XmlElement* stanza = new buzz::XmlElement(buzz::QN_MESSAGE);
5064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  stanza->AddAttr(buzz::QN_TO, to);
5074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  stanza->AddAttr(buzz::QN_ID, talk_base::CreateRandomString(16));
5084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  stanza->AddAttr(buzz::QN_TYPE, "chat");
5094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  buzz::XmlElement* body = new buzz::XmlElement(buzz::QN_BODY);
5104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  body->SetBodyText(msg);
5114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  stanza->AddElement(body);
5124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
5134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  xmpp_client_->SendStanza(stanza);
5144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  delete stanza;
5154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
5164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
517f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::InviteFriend(const std::string& name) {
518f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Jid jid(name);
519f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (!jid.IsValid() || jid.node() == "") {
520f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Invalid JID. JIDs should be in the form user@domain\n");
521f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
522f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
523f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // Note: for some reason the Buzz backend does not forward our presence
524f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // subscription requests to the end user when that user is another call
525f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // client as opposed to a Smurf user. Thus, in that scenario, you must
526f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // run the friend command as the other user too to create the linkage
527f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // (and you won't be notified to do so).
528f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  friend_invite_send_->Send(jid);
529f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->Printf("Requesting to befriend %s.\n", name.c_str());
530f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
531f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
5324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CallClient::MakeCallTo(const std::string& name,
5334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                            const cricket::CallOptions& given_options) {
5344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Copy so we can change .is_muc.
5354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  cricket::CallOptions options = given_options;
5364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
537f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  bool found = false;
5384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  options.is_muc = false;
539f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Jid callto_jid(name);
540f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Jid found_jid;
541f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (name.length() == 0 && mucs_.size() > 0) {
542f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // if no name, and in a MUC, establish audio with the MUC
543f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    found_jid = mucs_.begin()->first;
544f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    found = true;
5454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    options.is_muc = true;
546731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (name[0] == '+') {
547731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // if the first character is a +, assume it's a phone number
548731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    found_jid = callto_jid;
549731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    found = true;
550f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (callto_jid.resource() == "voicemail") {
551f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // if the resource is /voicemail, allow that
552f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    found_jid = callto_jid;
553f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    found = true;
554f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else {
555f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // otherwise, it's a friend
556f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    for (RosterMap::iterator iter = roster_->begin();
557f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch         iter != roster_->end(); ++iter) {
558f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      if (iter->second.jid.BareEquals(callto_jid)) {
559f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        found = true;
560f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        found_jid = iter->second.jid;
561f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        break;
562f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      }
563f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
564f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
565f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (!found) {
566f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      if (mucs_.count(callto_jid) == 1 &&
567f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch          mucs_[callto_jid]->state() == buzz::Muc::MUC_JOINED) {
568f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        found = true;
569f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        found_jid = callto_jid;
5704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        options.is_muc = true;
571f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      }
572f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
573f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
574f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
575f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (found) {
5764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    console_->Printf("Found %s '%s'", options.is_muc ? "room" : "online friend",
577f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        found_jid.Str().c_str());
5784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    PlaceCall(found_jid, options);
579f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else {
580f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Could not find online friend '%s'", name.c_str());
581f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
582f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
583f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
5844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CallClient::PlaceCall(const buzz::Jid& jid,
5854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                           const cricket::CallOptions& options) {
586f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->SignalCallDestroy.connect(
587f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      this, &CallClient::OnCallDestroy);
588f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (!call_) {
5894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    call_ = media_client_->CreateCall();
590f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->SetPrompt(jid.Str().c_str());
5914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    session_ = call_->InitiateSession(jid, options);
5924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if (options.is_muc) {
593f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // If people in this room are already in a call, must add all their
594f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // streams.
595f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      buzz::Muc::MemberMap& members = mucs_[jid]->members();
596f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      for (buzz::Muc::MemberMap::iterator elem = members.begin();
597f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch           elem != members.end();
598f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch           ++elem) {
599f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        AddStream(elem->second.audio_src_id(), elem->second.video_src_id());
600f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      }
601f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
602f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
603f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->SetFocus(call_);
604f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (call_->video()) {
605f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    call_->SetLocalRenderer(local_renderer_);
606731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // TODO: Call this once for every different remote SSRC
607f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // once we get to testing multiway video.
608f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    call_->SetVideoRenderer(session_, 0, remote_renderer_);
609f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
610f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
611f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
612f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::CallVoicemail(const std::string& name) {
613f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Jid jid(name);
614f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (!jid.IsValid() || jid.node() == "") {
615f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Invalid JID. JIDs should be in the form user@domain\n");
616f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
617f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
618f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::VoicemailJidRequester *request =
619f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    new buzz::VoicemailJidRequester(xmpp_client_, jid, my_status_.jid());
620f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  request->SignalGotVoicemailJid.connect(this,
621f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                                         &CallClient::OnFoundVoicemailJid);
622f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  request->SignalVoicemailJidError.connect(this,
623f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                                           &CallClient::OnVoicemailJidError);
624f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  request->Start();
625f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
626f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
627f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnFoundVoicemailJid(const buzz::Jid& to,
628f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                                     const buzz::Jid& voicemail) {
629f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->Printf("Calling %s's voicemail.\n", to.Str().c_str());
6304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  PlaceCall(voicemail, cricket::CallOptions());
631f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
632f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
633f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnVoicemailJidError(const buzz::Jid& to) {
634f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->Printf("Unable to voicemail %s.\n", to.Str().c_str());
635f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
636f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
637f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::AddStream(uint32 audio_src_id, uint32 video_src_id) {
638f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (audio_src_id || video_src_id) {
639f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Adding stream (%u, %u)\n", audio_src_id, video_src_id);
640f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    call_->AddStream(session_, audio_src_id, video_src_id);
641f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
642f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
643f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
644f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::RemoveStream(uint32 audio_src_id, uint32 video_src_id) {
645f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (audio_src_id || video_src_id) {
646f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Removing stream (%u, %u)\n", audio_src_id, video_src_id);
647f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    call_->RemoveStream(session_, audio_src_id, video_src_id);
648f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
649f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
650f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
6514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CallClient::Accept(const cricket::CallOptions& options) {
6523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT(call_ && incoming_call_);
6533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT(call_->sessions().size() == 1);
6544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  call_->AcceptSession(call_->sessions()[0], options);
655f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->SetFocus(call_);
656f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (call_->video()) {
657f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    call_->SetLocalRenderer(local_renderer_);
658f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // The client never does an accept for multiway, so this must be 1:1,
659f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // so there's no SSRC.
660f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    call_->SetVideoRenderer(session_, 0, remote_renderer_);
661f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
662f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  incoming_call_ = false;
663f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
664f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
665f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::Reject() {
6663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT(call_ && incoming_call_);
667f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  call_->RejectSession(call_->sessions()[0]);
668f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  incoming_call_ = false;
669f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
670f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
671f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::Quit() {
672f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  talk_base::Thread::Current()->Quit();
673f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
674f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
675f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::JoinMuc(const std::string& room) {
676f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Jid room_jid;
677f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (room.length() > 0) {
678f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    room_jid = buzz::Jid(room);
679f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else {
680f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // generate a GUID of the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,
681f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // for an eventual JID of private-chat-<GUID>@groupchat.google.com
682f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    char guid[37], guid_room[256];
683f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    for (size_t i = 0; i < ARRAY_SIZE(guid) - 1;) {
684f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      if (i == 8 || i == 13 || i == 18 || i == 23) {
685f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        guid[i++] = '-';
686f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      } else {
687f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        sprintf(guid + i, "%04x", rand());
688f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        i += 4;
689f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      }
690f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
691f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
692f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    talk_base::sprintfn(guid_room, ARRAY_SIZE(guid_room),
693f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                        "private-chat-%s@%s", guid, pmuc_domain_.c_str());
694f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    room_jid = buzz::Jid(guid_room);
695f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
696f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
697f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (!room_jid.IsValid()) {
698f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Unable to make valid muc endpoint for %s", room.c_str());
699f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
700f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
701f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
702f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  MucMap::iterator elem = mucs_.find(room_jid);
703f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (elem != mucs_.end()) {
704f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("This MUC already exists.");
705f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
706f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
707f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
708f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Muc* muc = new buzz::Muc(room_jid, xmpp_client_->jid().node());
709f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  mucs_[room_jid] = muc;
710f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_out_->SendDirected(muc->local_jid(), my_status_);
711f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
712f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
713f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnMucInviteReceived(const buzz::Jid& inviter,
714f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    const buzz::Jid& room,
715f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    const std::vector<buzz::AvailableMediaEntry>& avail) {
716f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
717f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->Printf("Invited to join %s by %s.\n", room.Str().c_str(),
718f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      inviter.Str().c_str());
719f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->Printf("Available media:\n");
720f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (avail.size() > 0) {
721f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    for (std::vector<buzz::AvailableMediaEntry>::const_iterator i =
722f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch            avail.begin();
723f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        i != avail.end();
724f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        ++i) {
725f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      console_->Printf("  %s, %s\n",
726f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch          buzz::AvailableMediaEntry::TypeAsString(i->type),
727f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch          buzz::AvailableMediaEntry::StatusAsString(i->status));
728f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
729f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else {
730f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("  None\n");
731f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
732f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // We automatically join the room.
733f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  JoinMuc(room.Str());
734f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
735f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
736f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnMucJoined(const buzz::Jid& endpoint) {
737f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  MucMap::iterator elem = mucs_.find(endpoint);
738f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  ASSERT(elem != mucs_.end() &&
739f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch         elem->second->state() == buzz::Muc::MUC_JOINING);
740f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
741f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Muc* muc = elem->second;
742f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  muc->set_state(buzz::Muc::MUC_JOINED);
743f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  console_->Printf("Joined \"%s\"", muc->jid().Str().c_str());
744f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
745f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
746f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnMucStatusUpdate(const buzz::Jid& jid,
747f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    const buzz::MucStatus& status) {
748f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
749f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // Look up this muc.
750f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  MucMap::iterator elem = mucs_.find(jid);
751f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  ASSERT(elem != mucs_.end() &&
752f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch         elem->second->state() == buzz::Muc::MUC_JOINED);
753f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
754f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Muc* muc = elem->second;
755f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
756f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (status.jid().IsBare() || status.jid() == muc->local_jid()) {
757f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // We are only interested in status about other users.
758f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
759f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
760f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
761f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (!status.available()) {
762f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // User is leaving the room.
763f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    buzz::Muc::MemberMap::iterator elem =
764f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      muc->members().find(status.jid().resource());
765f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
766f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    ASSERT(elem != muc->members().end());
767f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
768f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // If user had src-ids, they have the left the room without explicitly
769f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // hanging-up; must tear down the stream if in a call to this room.
770f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (call_ && session_->remote_name() == muc->jid().Str()) {
771f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      RemoveStream(elem->second.audio_src_id(), elem->second.video_src_id());
772f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
773f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
774f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // Remove them from the room.
775f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    muc->members().erase(elem);
776f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else {
777f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // Either user has joined or something changed about them.
778f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // Note: The [] operator here will create a new entry if it does not
779f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // exist, which is what we want.
780f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    buzz::MucStatus& member_status(
781f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        muc->members()[status.jid().resource()]);
782f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (call_ && session_->remote_name() == muc->jid().Str()) {
783f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // We are in a call to this muc. Must potentially update our streams.
784f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // The following code will correctly update our streams regardless of
785f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // whether the SSRCs have been removed, added, or changed and regardless
786f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // of whether that has been done to both or just one. This relies on the
787f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // fact that AddStream/RemoveStream do nothing for SSRC arguments that are
788f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // zero.
789f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      uint32 remove_audio_src_id = 0;
790f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      uint32 remove_video_src_id = 0;
791f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      uint32 add_audio_src_id = 0;
792f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      uint32 add_video_src_id = 0;
793f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      if (member_status.audio_src_id() != status.audio_src_id()) {
794f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        remove_audio_src_id = member_status.audio_src_id();
795f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        add_audio_src_id = status.audio_src_id();
796f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      }
797f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      if (member_status.video_src_id() != status.video_src_id()) {
798f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        remove_video_src_id = member_status.video_src_id();
799f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch        add_video_src_id = status.video_src_id();
800f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      }
801f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // Remove the old SSRCs, if any.
802f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      RemoveStream(remove_audio_src_id, remove_video_src_id);
803f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      // Add the new SSRCs, if any.
804f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      AddStream(add_audio_src_id, add_video_src_id);
805f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
806f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // Update the status. This will use the compiler-generated copy
807f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // constructor, which is perfectly adequate for this class.
808f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    member_status = status;
809f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
810f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
811f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
812f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::LeaveMuc(const std::string& room) {
813f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Jid room_jid;
814f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (room.length() > 0) {
815f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    room_jid = buzz::Jid(room);
816f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (mucs_.size() > 0) {
817f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // leave the first MUC if no JID specified
818f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    room_jid = mucs_.begin()->first;
819f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
820f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
821f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (!room_jid.IsValid()) {
822f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Invalid MUC JID.");
823f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
824f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
825f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
826f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  MucMap::iterator elem = mucs_.find(room_jid);
827f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (elem == mucs_.end()) {
828f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("No such MUC.");
829f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
830f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
831f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
832f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Muc* muc = elem->second;
833f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  muc->set_state(buzz::Muc::MUC_LEAVING);
834f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
835f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Status status;
836f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  status.set_jid(my_status_.jid());
837f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  status.set_available(false);
838f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  status.set_priority(0);
839f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  presence_out_->SendDirected(muc->local_jid(), status);
840f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
841f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
842f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnMucLeft(const buzz::Jid& endpoint, int error) {
843f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // We could be kicked from a room from any state.  We would hope this
844f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // happens While in the MUC_LEAVING state
845f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  MucMap::iterator elem = mucs_.find(endpoint);
846f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (elem == mucs_.end())
847f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
848f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
849f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Muc* muc = elem->second;
850f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (muc->state() == buzz::Muc::MUC_JOINING) {
851f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Failed to join \"%s\", code=%d",
852f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                     muc->jid().Str().c_str(), error);
853f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else if (muc->state() == buzz::Muc::MUC_JOINED) {
854f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("Kicked from \"%s\"",
855f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch                     muc->jid().Str().c_str());
856f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
857f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
858f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  delete muc;
859f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  mucs_.erase(elem);
860f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
861f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
862f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::InviteToMuc(const std::string& user, const std::string& room) {
863f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // First find the room.
864f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  const buzz::Muc* found_muc;
865f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (room.length() == 0) {
866f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (mucs_.size() == 0) {
867f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      console_->Printf("Not in a room yet; can't invite.\n");
868f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return;
869f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
870f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    // Invite to the first muc
871f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    found_muc = mucs_.begin()->second;
872f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  } else {
873f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    MucMap::iterator elem = mucs_.find(buzz::Jid(room));
874f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (elem == mucs_.end()) {
875f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      console_->Printf("Not in room %s.\n", room.c_str());
876f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      return;
877f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
878f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    found_muc = elem->second;
879f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
880f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  // Now find the user. We invite all of their resources.
881f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  bool found_user = false;
882f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  buzz::Jid user_jid(user);
883f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  for (RosterMap::iterator iter = roster_->begin();
884f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch       iter != roster_->end(); ++iter) {
885f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    if (iter->second.jid.BareEquals(user_jid)) {
886f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      muc_invite_send_->Send(iter->second.jid, *found_muc);
887f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch      found_user = true;
888f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    }
889f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
890f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  if (!found_user) {
891f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    console_->Printf("No such friend as %s.\n", user.c_str());
892f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    return;
893f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
894f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
895f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
896f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::GetDevices() {
897f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  std::vector<std::string> names;
898f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->GetAudioInputDevices(&names);
899f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  printf("Audio input devices:\n");
900f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  PrintDevices(names);
901f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->GetAudioOutputDevices(&names);
902f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  printf("Audio output devices:\n");
903f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  PrintDevices(names);
904f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->GetVideoCaptureDevices(&names);
905f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  printf("Video capture devices:\n");
906f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  PrintDevices(names);
907f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
908f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
909f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::PrintDevices(const std::vector<std::string>& names) {
910f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  for (size_t i = 0; i < names.size(); ++i) {
911f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch    printf("%d: %s\n", static_cast<int>(i), names[i].c_str());
912f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  }
913f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
914f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
915f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::OnDevicesChange() {
916f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  printf("Devices changed.\n");
917f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  RefreshStatus();
918f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
919f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch
920f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid CallClient::SetVolume(const std::string& level) {
921f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch  media_client_->SetOutputVolume(strtol(level.c_str(), NULL, 10));
922f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}
923