void
gabble_jingle_content_parse_accept (GabbleJingleContent *c,
    WockyNode *content_node, gboolean google_mode, GError **error)
{
  GabbleJingleContentPrivate *priv = c->priv;
  const gchar *senders;
  WockyNode *trans_node, *desc_node;
  JingleDialect dialect = gabble_jingle_session_get_dialect (c->session);
  JingleContentSenders newsenders;

  desc_node = wocky_node_get_child (content_node, "description");
  trans_node = wocky_node_get_child (content_node, "transport");
  senders = wocky_node_get_attribute (content_node, "senders");

  if (GABBLE_IS_JINGLE_MEDIA_RTP (c) &&
      JINGLE_IS_GOOGLE_DIALECT (dialect) && trans_node == NULL)
    {
      DEBUG ("no transport node, assuming GTalk3 dialect");
      /* gtalk lj0.3 assumes google-p2p transport */
      g_object_set (c->session, "dialect", JINGLE_DIALECT_GTALK3, NULL);
    }

  if (senders == NULL)
    newsenders = get_default_senders (c);
  else
    newsenders = parse_senders (senders);

  if (newsenders == JINGLE_CONTENT_SENDERS_NONE)
    {
      SET_BAD_REQ ("invalid content senders");
      return;
    }

  if (newsenders != priv->senders)
    {
      DEBUG ("changing senders from %s to %s", produce_senders (priv->senders),
          produce_senders (newsenders));
      priv->senders = newsenders;
      g_object_notify ((GObject *) c, "senders");
    }

  parse_description (c, desc_node, error);
  if (*error != NULL)
      return;

  priv->state = JINGLE_CONTENT_STATE_ACKNOWLEDGED;
  g_object_notify ((GObject *) c, "state");

  if (trans_node != NULL)
    {
      gabble_jingle_transport_iface_parse_candidates (priv->transport,
        trans_node, NULL);
    }
}
void
gabble_jingle_content_update_senders (GabbleJingleContent *c,
    WockyNode *content_node, GError **error)
{
  GabbleJingleContentPrivate *priv = c->priv;
  JingleContentSenders senders;

  senders = parse_senders (wocky_node_get_attribute (content_node, "senders"));

  if (senders == JINGLE_CONTENT_SENDERS_NONE)
    {
      SET_BAD_REQ ("invalid content senders in stream");
      return;
    }

  priv->senders = senders;
  g_object_notify ((GObject *) c, "senders");
}
void
gabble_jingle_content_parse_description_info (GabbleJingleContent *c,
    WockyNode *content_node, GError **error)
{
  GabbleJingleContentPrivate *priv = c->priv;
  WockyNode *desc_node;
  desc_node = wocky_node_get_child (content_node, "description");
  if (desc_node == NULL)
    {
      SET_BAD_REQ ("invalid description-info action");
      return;
    }

  if (priv->created_by_us && priv->state < JINGLE_CONTENT_STATE_ACKNOWLEDGED)
    {
      /* The stream was created by us and the other side didn't acknowledge it
       * yet, thus we don't have their codec information, thus the
       * description-info isn't meaningful and can be ignored */
      DEBUG ("Ignoring description-info as we didn't receive the codecs yet");
      return;
    }

  parse_description (c, desc_node, error);
}
static void
parse_candidates (GabbleJingleTransportIface *obj,
    LmMessageNode *transport_node, GError **error)
{
  GabbleJingleTransportGoogle *t = GABBLE_JINGLE_TRANSPORT_GOOGLE (obj);
  GabbleJingleTransportGooglePrivate *priv = t->priv;
  GList *candidates = NULL;
  NodeIter i;

  for (i = node_iter (transport_node); i; i = node_iter_next (i))
    {
      LmMessageNode *node = node_iter_data (i);
      const gchar *name, *address, *user, *pass, *str;
      guint port, net, gen, component;
      int pref;
      JingleTransportProtocol proto;
      JingleCandidateType ctype;
      JingleCandidate *c;

      if (tp_strdiff (lm_message_node_get_name (node), "candidate"))
          continue;

      name = lm_message_node_get_attribute (node, "name");
      if (name == NULL)
          break;

      if (!g_hash_table_lookup_extended (priv->component_names, name,
              NULL, NULL))
        {
          DEBUG ("component name %s unknown to this transport", name);
          continue;
        }

      component = GPOINTER_TO_INT (g_hash_table_lookup (priv->component_names,
              name));
      address = lm_message_node_get_attribute (node, "address");
      if (address == NULL)
          break;

      str = lm_message_node_get_attribute (node, "port");
      if (str == NULL)
          break;
      port = atoi (str);

      str = lm_message_node_get_attribute (node, "protocol");
      if (str == NULL)
          break;

      if (!tp_strdiff (str, "udp"))
        {
          proto = JINGLE_TRANSPORT_PROTOCOL_UDP;
        }
      else if (!tp_strdiff (str, "tcp"))
        {
          /* candiates on port 443 must be "ssltcp" */
          if (port == 443)
              break;

          proto = JINGLE_TRANSPORT_PROTOCOL_TCP;
        }
      else if (!tp_strdiff (str, "ssltcp"))
        {
          /* "ssltcp" must use port 443 */
          if (port != 443)
              break;

          /* we really don't care about "ssltcp" otherwise */
          proto = JINGLE_TRANSPORT_PROTOCOL_TCP;
        }
      else
        {
          /* unknown protocol */
          DEBUG ("unknown protocol: %s", str);
          break;
        }

      str = lm_message_node_get_attribute (node, "preference");
      if (str == NULL)
          break;

      pref = g_ascii_strtod (str, NULL) * 65536;

      str = lm_message_node_get_attribute (node, "type");
      if (str == NULL)
          break;

      if (!tp_strdiff (str, "local"))
        {
          ctype = JINGLE_CANDIDATE_TYPE_LOCAL;
        }
      else if (!tp_strdiff (str, "stun"))
        {
          ctype = JINGLE_CANDIDATE_TYPE_STUN;
        }
      else if (!tp_strdiff (str, "relay"))
        {
          ctype = JINGLE_CANDIDATE_TYPE_RELAY;
        }
      else
        {
          /* unknown candidate type */
          DEBUG ("unknown candidate type: %s", str);
          break;
        }

      user = lm_message_node_get_attribute (node, "username");
      if (user == NULL)
          break;

      pass = lm_message_node_get_attribute (node, "password");
      if (pass == NULL)
          break;

      str = lm_message_node_get_attribute (node, "network");
      if (str == NULL)
          break;
      net = atoi (str);

      str = lm_message_node_get_attribute (node, "generation");
      if (str == NULL)
          break;
      gen = atoi (str);

      str = lm_message_node_get_attribute (node, "component");
      if (str != NULL)
          component = atoi (str);

      c = jingle_candidate_new (proto, ctype, NULL, component,
          address, port, gen, pref, user, pass, net);

      candidates = g_list_append (candidates, c);
    }

  if (i != NULL)
    {
      DEBUG ("not all nodes were processed, reporting error");
      /* rollback these */
      jingle_transport_free_candidates (candidates);
      SET_BAD_REQ ("invalid candidate");
      return;
    }

  DEBUG ("emitting %d new remote candidates", g_list_length (candidates));

  g_signal_emit (obj, signals[NEW_CANDIDATES], 0, candidates);

  /* append them to the known remote candidates */
  priv->remote_candidates = g_list_concat (priv->remote_candidates, candidates);
}
void
gabble_jingle_content_parse_add (GabbleJingleContent *c,
    WockyNode *content_node, gboolean google_mode, GError **error)
{
  GabbleJingleContentPrivate *priv = c->priv;
  const gchar *name, *creator, *senders, *disposition;
  WockyNode *trans_node, *desc_node;
  GType transport_type = 0;
  GabbleJingleTransportIface *trans = NULL;
  JingleDialect dialect = gabble_jingle_session_get_dialect (c->session);

  priv->created_by_us = FALSE;

  desc_node = wocky_node_get_child (content_node, "description");
  trans_node = wocky_node_get_child (content_node, "transport");
  creator = wocky_node_get_attribute (content_node, "creator");
  name = wocky_node_get_attribute (content_node, "name");
  senders = wocky_node_get_attribute (content_node, "senders");

  g_assert (priv->transport_ns == NULL);

  if (google_mode)
    {
      if (creator == NULL)
          creator = "initiator";

      /* the google protocols don't give the contents names, so put in a dummy
       * value if none was set by the session*/
      if (priv->name == NULL)
        name = priv->name = g_strdup ("gtalk");
      else
        name = priv->name;

      if (trans_node == NULL)
        {
          /* gtalk lj0.3 assumes google-p2p transport */
          DEBUG ("detected GTalk3 dialect");

          dialect = JINGLE_DIALECT_GTALK3;
          g_object_set (c->session, "dialect", JINGLE_DIALECT_GTALK3, NULL);
          transport_type = gabble_jingle_factory_lookup_transport (
              gabble_jingle_session_get_factory (c->session),
              "");

          /* in practice we do support gtalk-p2p, so this can't happen */
          if (G_UNLIKELY (transport_type == 0))
            {
              SET_BAD_REQ ("gtalk-p2p transport unsupported");
              return;
            }

          priv->transport_ns = g_strdup ("");
        }
    }
  else
    {
      if (creator == NULL &&
          gabble_jingle_session_peer_has_cap (c->session,
              QUIRK_GOOGLE_WEBMAIL_CLIENT))
        {
          if (gabble_jingle_content_creator_is_initiator (c))
            creator = "initiator";
          else
            creator = "responder";

          DEBUG ("Working around GMail omitting creator=''; assuming '%s'",
              creator);
        }

      if ((trans_node == NULL) || (creator == NULL) || (name == NULL))
        {
          SET_BAD_REQ ("missing required content attributes or elements");
          return;
        }

      /* In proper protocols the name comes from the stanza */
      g_assert (priv->name == NULL);
      priv->name = g_strdup (name);
    }

  /* if we didn't set it to google-p2p implicitly already, detect it */
  if (transport_type == 0)
    {
      const gchar *ns = wocky_node_get_ns (trans_node);

      transport_type = gabble_jingle_factory_lookup_transport (
          gabble_jingle_session_get_factory (c->session), ns);

      if (transport_type == 0)
        {
          SET_BAD_REQ ("unsupported content transport");
          return;
        }

      priv->transport_ns = g_strdup (ns);
    }

  if (senders == NULL)
    priv->senders = get_default_senders (c);
  else
    priv->senders = parse_senders (senders);

  if (priv->senders == JINGLE_CONTENT_SENDERS_NONE)
    {
      SET_BAD_REQ ("invalid content senders");
      return;
    }

  parse_description (c, desc_node, error);
  if (*error != NULL)
      return;

  disposition = wocky_node_get_attribute (content_node, "disposition");
  if (disposition == NULL)
      disposition = "session";

  if (wocky_strdiff (disposition, priv->disposition))
    {
      g_free (priv->disposition);
      priv->disposition = g_strdup (disposition);
    }

  DEBUG ("content creating new transport type %s", g_type_name (transport_type));

  trans = gabble_jingle_transport_iface_new (transport_type,
      c, priv->transport_ns);

  g_signal_connect (trans, "new-candidates",
      (GCallback) new_transport_candidates_cb, c);

  /* Depending on transport, there may be initial candidates specified here */
  if (trans_node != NULL)
    {
      gabble_jingle_transport_iface_parse_candidates (trans, trans_node, error);
      if (*error)
        {
          g_object_unref (trans);
          return;
        }
    }

  g_assert (priv->transport == NULL);
  priv->transport = trans;
  transport_created (c);

  g_assert (priv->creator == NULL);
  priv->creator = g_strdup (creator);

  priv->state = JINGLE_CONTENT_STATE_NEW;

  /* GTalk4 seems to require "transport-accept" for acknowledging
   * the transport type. wjt confirms that this is apparently necessary for
   * incoming calls to work.
   */
  if (dialect == JINGLE_DIALECT_GTALK4)
    priv->gtalk4_event_id = g_idle_add (send_gtalk4_transport_accept, c);

  return;
}