Home | History | Annotate | Download | only in call
      1 /*
      2  * libjingle
      3  * Copyright 2004--2005, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "talk/examples/call/presencepushtask.h"
     29 
     30 #include "talk/base/stringencode.h"
     31 #include "talk/examples/call/muc.h"
     32 #include "talk/xmpp/constants.h"
     33 
     34 
     35 
     36 namespace buzz {
     37 
     38 // string helper functions -----------------------------------------------------
     39 
     40 static bool
     41 IsXmlSpace(int ch) {
     42   return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
     43 }
     44 
     45 static bool ListContainsToken(const std::string & list,
     46                               const std::string & token) {
     47   size_t i = list.find(token);
     48   if (i == std::string::npos || token.empty())
     49     return false;
     50   bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1]));
     51   bool boundary_after = (i == list.length() - token.length() ||
     52                          IsXmlSpace(list[i + token.length()]));
     53   return boundary_before && boundary_after;
     54 }
     55 
     56 
     57 bool PresencePushTask::HandleStanza(const XmlElement * stanza) {
     58   if (stanza->Name() != QN_PRESENCE)
     59     return false;
     60   QueueStanza(stanza);
     61   return true;
     62 }
     63 
     64 static bool IsUtf8FirstByte(int c) {
     65   return (((c)&0x80)==0) || // is single byte
     66     ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte
     67 }
     68 
     69 int PresencePushTask::ProcessStart() {
     70   const XmlElement * stanza = NextStanza();
     71   if (stanza == NULL)
     72     return STATE_BLOCKED;
     73 
     74   Jid from(stanza->Attr(QN_FROM));
     75   std::map<Jid, buzz::Muc*>::const_iterator elem =
     76       client_->mucs().find(from.BareJid());
     77   if (elem == client_->mucs().end()) {
     78     HandlePresence(from, stanza);
     79   } else {
     80     HandleMucPresence(elem->second, from, stanza);
     81   }
     82 
     83   return STATE_START;
     84 }
     85 
     86 void PresencePushTask::HandlePresence(const Jid& from,
     87                                       const XmlElement* stanza) {
     88   if (stanza->Attr(QN_TYPE) == STR_ERROR)
     89     return;
     90 
     91   Status s;
     92   FillStatus(from, stanza, &s);
     93   SignalStatusUpdate(s);
     94 }
     95 
     96 void PresencePushTask::HandleMucPresence(buzz::Muc* muc,
     97                                          const Jid& from,
     98                                          const XmlElement* stanza) {
     99   if (from == muc->local_jid()) {
    100     if (!stanza->HasAttr(QN_TYPE)) {
    101       // We joined the MUC.
    102       const XmlElement* elem = stanza->FirstNamed(QN_MUC_USER_X);
    103       if (elem) {
    104         elem = elem->FirstNamed(QN_MUC_USER_STATUS);
    105       }
    106       if (elem && (elem->Attr(QN_CODE) == "110" ||
    107           elem->Attr(QN_CODE) == "100")) {
    108         SignalMucJoined(muc->jid());
    109       }
    110     } else {
    111       // We've been kicked. Bye.
    112       int error = 0;
    113       if (stanza->Attr(QN_TYPE) == STR_ERROR) {
    114         const XmlElement* elem = stanza->FirstNamed(QN_ERROR);
    115         if (elem && elem->HasAttr(QN_CODE)) {
    116           error = atoi(elem->Attr(QN_CODE).c_str());
    117         }
    118       }
    119       SignalMucLeft(muc->jid(), error);
    120     }
    121   } else {
    122     MucStatus s;
    123     FillMucStatus(from, stanza, &s);
    124     SignalMucStatusUpdate(muc->jid(), s);
    125   }
    126 }
    127 
    128 void PresencePushTask::FillStatus(const Jid& from, const XmlElement* stanza,
    129                                   Status* s) {
    130   s->set_jid(from);
    131   if (stanza->Attr(QN_TYPE) == STR_UNAVAILABLE) {
    132     s->set_available(false);
    133   } else {
    134     s->set_available(true);
    135     const XmlElement * status = stanza->FirstNamed(QN_STATUS);
    136     if (status != NULL) {
    137       s->set_status(status->BodyText());
    138 
    139       // Truncate status messages longer than 300 bytes
    140       if (s->status().length() > 300) {
    141         size_t len = 300;
    142 
    143         // Be careful not to split legal utf-8 chars in half
    144         while (!IsUtf8FirstByte(s->status()[len]) && len > 0) {
    145           len -= 1;
    146         }
    147         std::string truncated(s->status(), 0, len);
    148         s->set_status(truncated);
    149       }
    150     }
    151 
    152     const XmlElement * priority = stanza->FirstNamed(QN_PRIORITY);
    153     if (priority != NULL) {
    154       int pri;
    155       if (talk_base::FromString(priority->BodyText(), &pri)) {
    156         s->set_priority(pri);
    157       }
    158     }
    159 
    160     const XmlElement * show = stanza->FirstNamed(QN_SHOW);
    161     if (show == NULL || show->FirstChild() == NULL) {
    162       s->set_show(Status::SHOW_ONLINE);
    163     }
    164     else {
    165       if (show->BodyText() == "away") {
    166         s->set_show(Status::SHOW_AWAY);
    167       }
    168       else if (show->BodyText() == "xa") {
    169         s->set_show(Status::SHOW_XA);
    170       }
    171       else if (show->BodyText() == "dnd") {
    172         s->set_show(Status::SHOW_DND);
    173       }
    174       else if (show->BodyText() == "chat") {
    175         s->set_show(Status::SHOW_CHAT);
    176       }
    177       else {
    178         s->set_show(Status::SHOW_ONLINE);
    179       }
    180     }
    181 
    182     const XmlElement * caps = stanza->FirstNamed(QN_CAPS_C);
    183     if (caps != NULL) {
    184       std::string node = caps->Attr(QN_NODE);
    185       std::string ver = caps->Attr(QN_VER);
    186       std::string exts = caps->Attr(QN_EXT);
    187 
    188       s->set_know_capabilities(true);
    189 
    190       if (node == GOOGLE_CLIENT_NODE) {
    191         s->set_is_google_client(true);
    192         s->set_version(ver);
    193       }
    194 
    195       if (ListContainsToken(exts, "voice-v1")) {
    196         s->set_phone_capability(true);
    197       }
    198       if (ListContainsToken(exts, "video-v1")) {
    199         s->set_video_capability(true);
    200       }
    201     }
    202 
    203     const XmlElement* delay = stanza->FirstNamed(kQnDelayX);
    204     if (delay != NULL) {
    205       // Ideally we would parse this according to the Psuedo ISO-8601 rules
    206       // that are laid out in JEP-0082:
    207       // http://www.jabber.org/jeps/jep-0082.html
    208       std::string stamp = delay->Attr(kQnStamp);
    209       s->set_sent_time(stamp);
    210     }
    211   }
    212 }
    213 
    214 void PresencePushTask::FillMucStatus(const Jid& from, const XmlElement* stanza,
    215                                      MucStatus* s) {
    216   // First get the normal user status info. Happily, this is in the same
    217   // format as it is for user presence.
    218   FillStatus(from, stanza, s);
    219 
    220   // Now look for src IDs, which will be present if this user is in a
    221   // multiway call to this MUC.
    222   const XmlElement* xstanza = stanza->FirstNamed(QN_MUC_USER_X);
    223   if (xstanza) {
    224     const XmlElement* media;
    225     for (media = xstanza->FirstNamed(QN_GOOGLE_MUC_USER_MEDIA);
    226         media; media = media->NextNamed(QN_GOOGLE_MUC_USER_MEDIA)) {
    227 
    228       const XmlElement* type = media->FirstNamed(QN_GOOGLE_MUC_USER_TYPE);
    229       if (!type) continue; // Shouldn't happen
    230 
    231       const XmlElement* src_id = media->FirstNamed(QN_GOOGLE_MUC_USER_SRC_ID);
    232       if (!src_id) continue; // Shouldn't happen
    233 
    234       char *endptr;
    235       uint32 src_id_num = strtoul(src_id->BodyText().c_str(), &endptr, 10);
    236       if (src_id->BodyText().c_str()[0] == '\0' || endptr[0] != '\0') {
    237         // String is not composed exclusively of leading whitespace plus a
    238         // number (shouldn't happen). Ignore it.
    239         continue;
    240       }
    241       // Else it's valid. Set it.
    242 
    243       if (type->BodyText() == "audio") {
    244         // This is the audio media element. Get the src-id.
    245         s->set_audio_src_id(src_id_num);
    246       } else if (type->BodyText() == "video") {
    247         // This is the video media element. Get the src-id.
    248         s->set_video_src_id(src_id_num);
    249       }
    250     }
    251   }
    252 }
    253 
    254 }
    255