Update to 0.5.4: Adding MUC room lookup and bug fixes.

git-svn-id: http://libjingle.googlecode.com/svn/trunk@61 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/CHANGELOG b/CHANGELOG
index 73ffc71..77c8795 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,9 @@
 Libjingle
 
+0.5.4 - May 13, 2011
+  - Support for MUC lookup by name
+  - Bug fixes
+
 0.5.3 - May 10, 2011
   - Stream notification and selection.
   - Better XEP-0045 support.
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 0dd58a9..895b58b 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -52,6 +52,7 @@
 #include "talk/session/phone/mediasessionclient.h"
 #include "talk/session/phone/videorendererfactory.h"
 #include "talk/xmpp/constants.h"
+#include "talk/xmpp/mucroomlookuptask.h"
 
 namespace {
 
@@ -115,7 +116,8 @@
 "  vcall [jid] [bw]    Initiates a video call to the user[/room] with\n"
 "                      the given JID and with optional bandwidth.\n"
 "  voicemail [jid]     Leave a voicemail for the user with the given JID.\n"
-"  join [room]         Joins a multi-user-chat.\n"
+"  join [room_jid]     Joins a multi-user-chat with room JID.\n"
+"  ljoin [room_name]   Joins a MUC by looking up JID from room name.\n"
 "  invite user [room]  Invites a friend to a multi-user-chat.\n"
 "  leave [room]        Leaves a multi-user-chat.\n"
 "  getdevs             Prints the available media devices.\n"
@@ -199,6 +201,8 @@
       MakeCallTo(to, options);
     } else if (command == "join") {
       JoinMuc(GetWord(words, 1, ""));
+    } else if (command == "ljoin") {
+      LookupAndJoinMuc(GetWord(words, 1, ""));
     } else if ((words.size() >= 2) && (command == "invite")) {
       InviteToMuc(words[1], GetWord(words, 2, ""));
     } else if (command == "leave") {
@@ -637,30 +641,46 @@
   talk_base::Thread::Current()->Quit();
 }
 
-void CallClient::JoinMuc(const std::string& room) {
-  buzz::Jid room_jid;
-  if (room.length() > 0) {
-    room_jid = buzz::Jid(room);
-  } else {
-    // generate a GUID of the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,
-    // for an eventual JID of private-chat-<GUID>@groupchat.google.com
-    char guid[37], guid_room[256];
-    for (size_t i = 0; i < ARRAY_SIZE(guid) - 1;) {
-      if (i == 8 || i == 13 || i == 18 || i == 23) {
-        guid[i++] = '-';
-      } else {
-        sprintf(guid + i, "%04x", rand());
-        i += 4;
-      }
-    }
-
-    talk_base::sprintfn(guid_room, ARRAY_SIZE(guid_room),
-                        "private-chat-%s@%s", guid, pmuc_domain_.c_str());
-    room_jid = buzz::Jid(guid_room);
+void CallClient::LookupAndJoinMuc(const std::string& room_name) {
+  // The room_name can't be empty for lookup task.
+  if (room_name.empty()) {
+    console_->Print("Please provide a room name or room jid.");
+    return;
   }
 
+  std::string room = room_name;
+  std::string domain =  xmpp_client_->jid().domain();
+  if (room_name.find("@") != std::string::npos) {
+    // Assume the room_name is a fully qualified room name.
+    // We'll find the room name string and domain name string from it.
+    room = room_name.substr(0, room_name.find("@") - 1);
+    domain = room_name.substr(room_name.find("@") + 1);
+  }
+
+  buzz::MucRoomLookupTask* lookup_query_task =
+      new buzz::MucRoomLookupTask(xmpp_client_, room, domain);
+  lookup_query_task->SignalRoomLookupResponse.connect(this,
+      &CallClient::OnRoomLookupResponse);
+  lookup_query_task->SignalRoomLookupError.connect(this,
+      &CallClient::OnRoomLookupError);
+  lookup_query_task->Start();
+}
+
+void CallClient::JoinMuc(const std::string& room_jid_str) {
+  if (room_jid_str.empty()) {
+    buzz::Jid room_jid = GenerateRandomMucJid();
+    console_->Printf("Generated a random room jid: %s",
+                     room_jid.Str().c_str());
+    JoinMuc(room_jid);
+  } else {
+    JoinMuc(buzz::Jid(room_jid_str));
+  }
+}
+
+void CallClient::JoinMuc(const buzz::Jid& room_jid) {
   if (!room_jid.IsValid()) {
-    console_->Printf("Unable to make valid muc endpoint for %s", room.c_str());
+    console_->Printf("Unable to make valid muc endpoint for %s",
+                     room_jid.Str().c_str());
     return;
   }
 
@@ -675,6 +695,15 @@
   presence_out_->SendDirected(muc->local_jid(), my_status_);
 }
 
+void CallClient::OnRoomLookupResponse(const buzz::MucRoomInfo& room_info) {
+  JoinMuc(room_info.room_jid);
+}
+
+void CallClient::OnRoomLookupError(const buzz::XmlElement* stanza) {
+  console_->Printf("%s\n", "Failed to look up the room_jid.",
+      stanza->Str().c_str());
+}
+
 void CallClient::OnMucInviteReceived(const buzz::Jid& inviter,
     const buzz::Jid& room,
     const std::vector<buzz::AvailableMediaEntry>& avail) {
@@ -695,7 +724,7 @@
     console_->Printf("  None\n");
   }
   // We automatically join the room.
-  JoinMuc(room.Str());
+  JoinMuc(room);
 }
 
 void CallClient::OnMucJoined(const buzz::Jid& endpoint) {
@@ -903,3 +932,24 @@
   }
   call_->SendViewRequest(session, request);
 }
+
+buzz::Jid CallClient::GenerateRandomMucJid() {
+  // Generate a GUID of the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,
+  // for an eventual JID of private-chat-<GUID>@groupchat.google.com.
+  char guid[37], guid_room[256];
+  for (size_t i = 0; i < ARRAY_SIZE(guid) - 1;) {
+    if (i == 8 || i == 13 || i == 18 || i == 23) {
+      guid[i++] = '-';
+    } else {
+      sprintf(guid + i, "%04x", rand());
+      i += 4;
+    }
+  }
+
+  talk_base::sprintfn(guid_room,
+                      ARRAY_SIZE(guid_room),
+                      "private-chat-%s@%s",
+                      guid,
+                      pmuc_domain_.c_str());
+  return buzz::Jid(guid_room);
+}
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index 4c2da17..82fc472 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -49,8 +49,11 @@
 class DiscoInfoQueryTask;
 class Muc;
 class Status;
+class MucRoomLookupTask;
 class MucStatus;
+class XmlElement;
 struct AvailableMediaEntry;
+struct MucRoomInfo;
 }
 
 namespace talk_base {
@@ -110,7 +113,9 @@
 
   void SendChat(const std::string& to, const std::string msg);
   void InviteFriend(const std::string& user);
-  void JoinMuc(const std::string& room);
+  void JoinMuc(const buzz::Jid& room_jid);
+  void JoinMuc(const std::string& room_jid_str);
+  void LookupAndJoinMuc(const std::string& room_name);
   void InviteToMuc(const std::string& user, const std::string& room);
   void LeaveMuc(const std::string& room);
   void SetPortAllocatorFlags(uint32 flags) { portallocator_flags_ = flags; }
@@ -160,6 +165,9 @@
   void OnMediaSourcesUpdate(cricket::Call* call,
                             cricket::Session* session,
                             const cricket::MediaSources& sources);
+  void OnRoomLookupResponse(const buzz::MucRoomInfo& room_info);
+  void OnRoomLookupError(const buzz::XmlElement* stanza);
+  buzz::Jid GenerateRandomMucJid();
 
   void AddStaticRenderedView(
       cricket::Session* session,
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index cc7947c..1e47ab5 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -200,6 +200,7 @@
                "xmllite/xmlprinter.cc",
                "xmpp/constants.cc",
                "xmpp/jid.cc",
+               "xmpp/mucroomlookuptask.cc",
                "xmpp/ratelimitmanager.cc",
                "xmpp/saslmechanism.cc",
                "xmpp/xmppclient.cc",
diff --git a/talk/p2p/base/sessionmessages.cc b/talk/p2p/base/sessionmessages.cc
index d15c9e3..a0b1aa7 100644
--- a/talk/p2p/base/sessionmessages.cc
+++ b/talk/p2p/base/sessionmessages.cc
@@ -127,13 +127,7 @@
   if (jingle == NULL)
     return false;
 
-  return (jingle->HasAttr(buzz::QN_ACTION) &&
-          (jingle->HasAttr(QN_SID)
-           // TODO: This works around a bug in old jingle
-           // clients that set QN_ID instead of QN_SID.  Once we know
-           // there are no clients which have this bug, we can remove
-           // this code.
-           || jingle->HasAttr(QN_ID)));
+  return (jingle->HasAttr(buzz::QN_ACTION) && jingle->HasAttr(QN_SID));
 }
 
 bool IsGingleMessage(const buzz::XmlElement* stanza) {
@@ -176,12 +170,6 @@
   std::string type_string = jingle->Attr(buzz::QN_ACTION);
   msg->type = ToActionType(type_string);
   msg->sid = jingle->Attr(QN_SID);
-  // TODO: This works around a bug in old jingle clients
-  // that set QN_ID instead of QN_SID.  Once we know there are no
-  // clients which have this bug, we can remove this code.
-  if (msg->sid.empty()) {
-    msg->sid = jingle->Attr(buzz::QN_ID);
-  }
   msg->initiator = GetXmlAttr(jingle, QN_INITIATOR, buzz::STR_EMPTY);
   msg->action_elem = jingle;
 
@@ -235,15 +223,9 @@
   buzz::XmlElement* jingle = new buzz::XmlElement(QN_JINGLE, true);
   jingle->AddAttr(buzz::QN_ACTION, ToJingleString(msg.type));
   jingle->AddAttr(QN_SID, msg.sid);
-  // TODO: This works around a bug in old jingle clinets
-  // that expected QN_ID instead of QN_SID.  Once we know there are no
-  // clients which have this bug, we can remove this code.
-  jingle->AddAttr(QN_ID, msg.sid);
-  // TODO: Right now, the XMPP server rejects a jingle-only
-  // (non hybrid) message with "feature-not-implemented" if there is
-  // no initiator.  Fix the server, and then only set the initiator on
-  // session-initiate messages here.
-  jingle->AddAttr(QN_INITIATOR, msg.initiator);
+  if (msg.type == ACTION_SESSION_INITIATE) {
+    jingle->AddAttr(QN_INITIATOR, msg.initiator);
+  }
   AddXmlChildren(jingle, action_elems);
   return jingle;
 }
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index a150688..64ebcef 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -548,8 +548,12 @@
         // For backwards compatibility, we remove by nick.
         // TODO: Remove once all senders use explicit remove by ssrc.
         found = media_sources.GetFirstAudioSourceByNick(it->nick);
-        it->SetSsrc(found->ssrc);
-        it->removed = true;
+        if (found) {
+          it->SetSsrc(found->ssrc);
+          it->removed = true;
+        } else {
+          continue;  // No ssrc to remove.
+        }
       }
       if (it->removed && found) {
         LOG(LS_INFO) << "Remove voice stream:  " << found->ssrc;
@@ -571,8 +575,12 @@
         // For backwards compatibility, we remove by nick.
         // TODO: Remove once all senders use explicit remove by ssrc.
         found = media_sources.GetFirstVideoSourceByNick(it->nick);
-        it->SetSsrc(found->ssrc);
-        it->removed = true;
+        if (found) {
+          it->SetSsrc(found->ssrc);
+          it->removed = true;
+        } else {
+          continue;  // No ssrc to remove.
+        }
       }
       if (it->removed && found) {
         LOG(LS_INFO) << "Remove video stream:  " << found->ssrc;
diff --git a/talk/xmpp/constants.cc b/talk/xmpp/constants.cc
index db9095f..b19c124 100644
--- a/talk/xmpp/constants.cc
+++ b/talk/xmpp/constants.cc
@@ -158,6 +158,7 @@
 
 const std::string STR_UNAVAILABLE("unavailable");
 
+const std::string STR_MUC_LOOKUP_DOMAIN("lookup.groupchat.google.com");
 
 const QName QN_STREAM_STREAM(true, NS_STREAM, STR_STREAM);
 const QName QN_STREAM_FEATURES(true, NS_STREAM, "features");
@@ -395,6 +396,15 @@
 const QName QN_MUC_USER_STATUS(true, NS_MUC_USER, "status");
 
 
+// JEP 0055 - Jabber Search
+const std::string NS_SEARCH("jabber:iq:search");
+const QName QN_SEARCH_QUERY(true, NS_SEARCH, "query");
+const QName QN_SEARCH_ITEM(true, NS_SEARCH, "item");
+const QName QN_SEARCH_ROOM_NAME(true, NS_SEARCH, "room-name");
+const QName QN_SEARCH_ORGANIZERS_DOMAIN(true, NS_SEARCH, "organizers-domain");
+const QName QN_SEARCH_ROOM_JID(true, NS_SEARCH, "room-jid");
+
+
 // JEP 0115
 const std::string NS_CAPS("http://jabber.org/protocol/caps");
 const QName QN_CAPS_C(true, NS_CAPS, "c");
diff --git a/talk/xmpp/constants.h b/talk/xmpp/constants.h
index 8df83b9..dfd4178 100644
--- a/talk/xmpp/constants.h
+++ b/talk/xmpp/constants.h
@@ -112,6 +112,7 @@
 
 extern const std::string STR_UNAVAILABLE;
 
+extern const std::string STR_MUC_LOOKUP_DOMAIN;
 
 extern const QName QN_STREAM_STREAM;
 extern const QName QN_STREAM_FEATURES;
@@ -361,6 +362,15 @@
 extern const QName QN_MUC_USER_STATUS;
 
 
+// JEP 0055 - Jabber Search
+extern const std::string NS_SEARCH;
+extern const QName QN_SEARCH_QUERY;
+extern const QName QN_SEARCH_ITEM;
+extern const QName QN_SEARCH_ROOM_NAME;
+extern const QName QN_SEARCH_ROOM_JID;
+extern const QName QN_SEARCH_ORGANIZERS_DOMAIN;
+
+
 // JEP 0115
 extern const std::string NS_CAPS;
 extern const QName QN_CAPS_C;