Example #1
0
// Delete all resource lists.
void ResourceListSet::deleteAllResourceLists(bool abortOnShutdown)
{
   Os::Logger::instance().log(FAC_RLS, PRI_DEBUG,
                 "ResourceListSet::deleteAllResourceLists "
                 "this = %p, abortOnShutdown = %d",
                 this, abortOnShutdown);

   // Gradually remove elements from the ResourceLists and delete them.
   ResourceList* rl;
   int changeDelay = getResourceListServer()->getChangeDelay();
   do {
      // Set to true if a ResourceCached was deleted and so we need to delay.
      bool resource_deleted = false;

      {
         // Serialize access to the ResourceListSet.
         OsLock lock(mSemaphore);

         // Get pointer to the first ResourceList.
         rl = dynamic_cast <ResourceList*> (mResourceLists.first());

         // If one exists, shrink it.
         if (rl) {
            bool list_empty;
            rl->shrink(list_empty, resource_deleted);
            if (list_empty) {
               // The ResourceList is empty, and so can be removed and deleted.
               mResourceLists.removeReference(rl);
               delete rl;
            }
         }
      }

      if (resource_deleted)
      {
         // Delay to allow the consequent processing to catch up.
         OsTask::delay(changeDelay);
      }
   } while (rl && !gShutdownFlag);
}
Example #2
0
//! Start subscriptions for this resource.
void ResourceCached::startSubscriptions()
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "ResourceCached::startSubscriptions URI = '%s'",
                 data());

   // Create the ContactSet.
   mContactSetP = new ContactSet(this,
                                 // The URI, which is the UtlString-nature
                                 // of a RecourceCached.
                                 *(static_cast <UtlString*> (this))
      );
   // Start the refresh timer.
   // Choose a random time between 1/2 and 1 times
   // ResourceListServer::getRefreshInterval().
   int refresh_time =
      (int) ((1.0 + ((float) sRandom.rand()) / RAND_MAX) / 2.0 *
             getResourceListServer()->getRefreshInterval());
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "ResourceCached::startSubscriptions refresh_time = %d",
                 refresh_time);
   OsTime rt(refresh_time, 0);
   mRefreshTimer.oneshotAfter(rt);
}
Example #3
0
// Insert a subscription into the set.
void SubscriptionSet::addInstance(const char* instanceName,
                                  const char* subscriptionState)
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "SubscriptionSet::addInstance instanceName = '%s', subscriptionState = '%s'",
                 instanceName, subscriptionState);

   // Check that we don't have too many instances.
   if (mSubscriptions.entries() <
       getResourceListServer()->getMaxResInstInCont())
   {
      // Add the instance to the set.
      ResourceInstance* inst =
         new ResourceInstance(this, instanceName, subscriptionState);
      mSubscriptions.append(inst);
   }
   else
   {
      OsSysLog::add(FAC_RLS, PRI_ERR,
                    "SubscriptionSet::addInstance cannot add ResourceInstance with name '%s', already %zu in SubscriptionSet '%s'",
                    instanceName, mSubscriptions.entries(),
                    mUri.data());
   }
}
Example #4
0
// Generate and publish the full and partial RLMI for the specified URI
// (full/consolidated) of a resource list.
// Both the Full and the Partial RLMI are sent to the SIP Subscribe Server.
// The Partial RLMI will then be sent out right away and the Full RLMI
// will be stored in the Subscribe Server to be sent on any initial
// SUBSCRIBEs and re-SUBSCRIBEs.
void ResourceList::genAndPublish(UtlBoolean consolidated, UtlString resourceListUri)
{
   const UtlBoolean RLMI_FULL = TRUE;
   const UtlBoolean RLMI_PARTIAL = FALSE;
   HttpBody* body;
   UtlSList partialList;

   // Generate and publish the fullState=TRUE notice body.
   // Note that the full-state publish() must be done before the partial-state
   // publish() to avoid race conditions with regard to starting a new
   // subscription.
   body = generateRlmiBody(consolidated, RLMI_FULL, mResourcesList);
   getResourceListServer()->getEventPublisher().
      publish(resourceListUri.data(),
              getResourceListServer()->getEventType(),
              getResourceListServer()->getEventType(),
              1, &body,
              RLMI_FULL,
              // Suppress generating notifications for this call of
              // SipPublishContentMgr::publish, because the call below
              // will generate notifications for the same subscribed-to
              // URIs.
              TRUE);

   // Generate and publish the fullState=FALSE notice body.
   // If there are no URIs in mChangesList, then there have been no changes
   // and publish() does not need to be called to trigger notifications.
   if (genPartialList(partialList))
   {
      body = generateRlmiBody(consolidated, RLMI_PARTIAL, partialList);
      getResourceListServer()->getEventPublisher().
         publish(resourceListUri.data(),
                 getResourceListServer()->getEventType(),
                 getResourceListServer()->getEventType(),
                 1, &body,
                 RLMI_PARTIAL,
                 // This call to SipPublishContentMgr::publish triggers
                 // notification.
                 FALSE);
   }
}
Example #5
0
// Constructor
SubscriptionSet::SubscriptionSet(ResourceCached* resource,
                                 UtlString& uri) :
   ResourceSubscriptionReceiver(),
   mResource(resource),
   mUri(uri)
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "SubscriptionSet:: this = %p, resource = %p, mUri = '%s'",
                 this, mResource, mUri.data());

   // Start the subscription for dialog events.
   UtlBoolean ret;
   UtlString mUriNameAddr = "<" + mUri + ">";
   ret = getResourceListServer()->getSubscribeClient().
      addSubscription(mUri.data(),
                      getResourceListServer()->getEventType(),
                      getResourceListServer()->getContentType(),
                      getResourceListServer()->getClientFromURI(),
                      mUriNameAddr.data(),
                      getResourceListServer()->getClientContactURI(),
                      getResourceListServer()->getResubscribeInterval(),
                      getResourceListSet(),
                      ResourceListSet::subscriptionEventCallbackAsync,
                      ResourceListSet::notifyEventCallbackAsync,
                      mSubscriptionEarlyDialogHandle);
   if (ret)
   {
      OsSysLog::add(FAC_RLS, PRI_DEBUG,
                    "SubscriptionSet:: addSubscription for '%s' succeeded",
                    mUri.data());
      // Add this SubscriptionSet to mSubscribeMap.
      getResourceListSet()->addSubscribeMapping(&mSubscriptionEarlyDialogHandle,
                                                this);
   }
   else
   {
      OsSysLog::add(FAC_RLS, PRI_WARNING,
                    "SubscriptionSet:: addSubscription for '%s' failed",
                    mUri.data());
   }
}
Example #6
0
//! Generate the HttpBody for the current state of the resource list.
HttpBody* ResourceList::generateRlmiBody(UtlBoolean consolidated,
                                         UtlBoolean fullRlmi,
                                         UtlSList& listToSend)
{
   if (Os::Logger::instance().willLog(FAC_RLS, PRI_DEBUG))
   {
      UtlString l;

      UtlSListIterator resourcesItor(listToSend);
      ResourceReference* resource;
      while ((resource = dynamic_cast <ResourceReference*> (resourcesItor())))
      {
         l.append(*resource->getUri());
         l.append(",");
      }
      if (!l.isNull())
      {
         l.remove(l.length() - 1);
      }

      Os::Logger::instance().log(FAC_RLS, PRI_DEBUG,
                    "ResourceList::generateRlmiBody cons=%d URI='%s' full=%d listToSend='%s'",
                    consolidated,
                    (consolidated ? mResourceListNameCons.data() : mResourceListName.data()),
                    fullRlmi,
                    l.data());
   }

   // Construct the multipart body.
   // We add the <...> here, as they are used in all the contexts where
   // rlmiBodyPartCid appears.
   UtlString rlmiBodyPartCid;
   rlmiBodyPartCid += "<rlmi@";
   rlmiBodyPartCid += getResourceListServer()->getDomainName();
   rlmiBodyPartCid += ">";

   UtlString content_type(CONTENT_TYPE_MULTIPART_RELATED
                          ";type=\"" RLMI_CONTENT_TYPE "\""
                          ";start=\"");
   content_type += rlmiBodyPartCid;
   content_type += "\"";
   HttpBody* body = new HttpBodyMultipart(content_type);

   // This is the Resource List Meta-Information, XML describing the resources
   // and their instances.  It is the main part of the NOTIFY body.
   UtlString rlmi;

   // Generate the initial part of the RLMI.
   rlmi += "<?xml version=\"1.0\"?>\r\n";
   rlmi += "<list xmlns=\"" RLMI_XMLNS "\" uri=\"";
   XmlEscape(rlmi,
             consolidated ? mResourceListNameCons : mResourceListName);
   // Placeholder for version from SIP stack.
   rlmi += "\" version=\"" VERSION_PLACEHOLDER "\" ";

   // Generate either the full or the partial RLMI.
   if (fullRlmi)
   {
      rlmi += "fullState=\"true\">\r\n";
   }
   else
   {
      rlmi += "fullState=\"false\">\r\n";
   }

   // If we implemented names for resource lists, <name> elements would be added here.

   // Iterate through the resources.
   UtlSListIterator resourcesItor(listToSend);
   ResourceReference* resource;
   while ((resource = dynamic_cast <ResourceReference*> (resourcesItor())))
   {
      // Add the content for the resource.
      resource->generateBody(rlmi, *body, consolidated);
   }

   // Generate the postamble for the resource list.
   rlmi += "</list>\r\n";

   // Construct the RLMI body part.
   HttpBody rlmi_body(rlmi.data(), rlmi.length(), RLMI_CONTENT_TYPE);
   UtlDList rlmi_body_parameters;
   rlmi_body_parameters.append(new NameValuePair(HTTP_CONTENT_ID_FIELD,
                                                 rlmiBodyPartCid));

   // Attach the RLMI.
   body->appendBodyPart(rlmi_body, rlmi_body_parameters);

   // Clean up the parameter list.
   rlmi_body_parameters.destroyAll();

   return body;
}
Example #7
0
// Add to the HttpBody the current state of the resource.
void ResourceCached::generateBody(UtlString& rlmi,
                                  HttpBody& body,
                                  UtlBoolean consolidated,
                                  const UtlString& nameXml,
                                  const UtlString& displayName) const
{
   // Generate the preamble for the resource.
   rlmi += "  <resource uri=\"";
   XmlEscape(rlmi, *(static_cast <const UtlString*> (this)));
   rlmi += "\">\r\n";
   if (!nameXml.isNull())
   {
      rlmi += "    ";
      rlmi += nameXml;
   }

   if (consolidated)
   {
      // If consolidating resource instances, generate the XML for the
      // unified resource instance.
      rlmi += "    <instance id=\"consolidated\" state=\"active\"";

      UtlString contentBodyPartCid;
      // Use the count of parts in 'body' to generate a unique identifier for
      // each part.
      contentBodyPartCid.appendNumber(body.getMultipartCount());
      contentBodyPartCid += "@";
      contentBodyPartCid += getResourceListServer()->getDomainName();

      rlmi += " cid=\"";
      rlmi += contentBodyPartCid;
      rlmi += "\"";

      // Now add the <...> and use it in the header.
      contentBodyPartCid.prepend("<");
      contentBodyPartCid.append(">");

      // Create a single HttpBody to contain the unified dialog event.
      UtlString dialog_event;

      // XML declaration is optional, but Broadworks uses it.
      dialog_event += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
      dialog_event += BEGIN_DIALOG_INFO;
      dialog_event += VERSION_EQUAL;
      dialog_event += "\"";
      // The consolidated dialog events need to have persistent
      // version numbers, as they all have the same instance id.
      // So we use a global version number in the ResourceListSet.
      dialog_event.appendNumber(getResourceListSet()->getVersion());
      dialog_event += "\"";
      dialog_event += STATE_EQUAL;
      dialog_event += "\"full\"";
      dialog_event += ENTITY_EQUAL;
      dialog_event += "\"";
      dialog_event += *(static_cast <const UtlString*> (this));
      dialog_event += "\">\r\n";
      // Save the length of dialog_event, so we can tell later if
      // any <dialog>s have been added to it.
      unsigned int preamble_length = dialog_event.length();

      // Call the ContactSet to generate the consolidated dialog event body.
      if (mContactSetP)
      {
         mContactSetP->generateBody(dialog_event, body, consolidated,
                                    displayName);
      }

      // If no <dialog>s have been added, we have to add a dummy
      // <dialog> to carry the display name.
      if (dialog_event.length() == preamble_length)
      {
         dialog_event +=
            "<dialog id=\";\"><state>terminated</state><local><identity display=\"";
         XmlEscape(dialog_event, displayName);
         dialog_event += "\">";
         XmlEscape(dialog_event, *(static_cast <const UtlString*> (this)));
         dialog_event += "</identity></local></dialog>\r\n";
      }

      dialog_event += END_DIALOG_INFO;

      // Insert the consolidated dialog event body into the multiplart body.
      HttpBody content_body(dialog_event.data(),
                            dialog_event.length(),
                            DIALOG_EVENT_CONTENT_TYPE);
      UtlDList content_body_parameters;
      content_body_parameters.append(
         new NameValuePair(HTTP_CONTENT_ID_FIELD,
                           contentBodyPartCid));
      body.appendBodyPart(content_body, content_body_parameters);
      content_body_parameters.destroyAll();

      // Finish the <instance> element.
      rlmi += "/>\r\n";
   }
   else
   {
      // Call the ContactSet to do the work.
      if (mContactSetP)
      {
         mContactSetP->generateBody(rlmi, body, consolidated, displayName);
      }
   }   
   
// Generate the postamble for the resource.
   rlmi += "  </resource>\r\n";
}
Example #8
0
// Add to the HttpBody the current state of the resource.
void ResourceInstance::generateBody(UtlString& rlmi,
                                    HttpBody& body,
                                    UtlBoolean consolidated,
                                    const UtlString& displayName) const
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "ResourceInstance::generateBody mInstanceName = '%s', consolidated = %d, displayName = '%s', mContentPresent = %d",
                 mInstanceName.data(), consolidated, displayName.data(),
                 mContentPresent);

   if (consolidated)
   {
      if (mContentPresent)
      {
         // If this is a consolidated dialog event list, edit the
         // stored XML into the right form and append each dialog
         // to the resource list event notice.

         TiXmlUtlStringWriter writer(&rlmi);

         // Iterate through all the <dialog> elements.
         UtlHashMapIterator itor(mXmlDialogs);
         UtlContainable* id;
         while ((id = itor()))
         {
            UtlVoidPtr* p = dynamic_cast <UtlVoidPtr*> (itor.value());
            TiXmlElement* dialog_element =
               static_cast <TiXmlElement*> (p->getValue());

            // Now that we've got a <dialog> element, edit it to fit
            // into a consolidated event notice.

            // Get the display name right.
            // Find the <local> element, which we know exists due to
            // earlier processing.
            TiXmlNode* local = dialog_element->FirstChild("local");
            // Find the <local><identity> element, which we know
            // exists due to earlier processing.
            TiXmlNode* identity = local->FirstChild("identity");
            // Update the display attribute, as that is what will show
            // on the phone.
            identity->ToElement()->
               SetAttribute("display", displayName);

            // Un-parse the dialog into the string for storage.
            writer << *dialog_element;
            writer << "\r\n";
         }
      }
   }
   else
   {
      // Generate the XML for the instance.
      rlmi += "    <instance id=\"";
      XmlEscape(rlmi, mInstanceName);
      rlmi += "\" state=\"";
      // Subscription states don't require escaping.
      rlmi += mSubscriptionState;
      rlmi += "\"";

      // Generate the body part for the instance, if necessary.
      if (mContentPresent)
      {
         UtlString contentBodyPartCid;
         // Use the count of parts in 'body' to generate a unique identifier for
         // each part.
         contentBodyPartCid.appendNumber(body.getMultipartCount());
         contentBodyPartCid += "@";
         contentBodyPartCid += getResourceListServer()->getDomainName();

         rlmi += " cid=\"";
         rlmi += contentBodyPartCid;
         rlmi += "\"";

         // Now add the <...> and use it in the header.
         contentBodyPartCid.prepend("<");
         contentBodyPartCid.append(">");

         HttpBody content_body(mContent.data(),
                               mContent.length(),
                               getResourceListServer()->getContentType());
         UtlDList content_body_parameters;
         content_body_parameters.append(
            new NameValuePair(HTTP_CONTENT_ID_FIELD,
                              contentBodyPartCid));
         body.appendBodyPart(content_body, content_body_parameters);
         content_body_parameters.destroyAll();
      }

      rlmi += "/>\r\n";
   }
}
Example #9
0
// Process a notify event callback.
void ResourceInstance::notifyEventCallback(const UtlString* dialogHandle,
                                           const UtlString* content)
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "ResourceInstance::notifyEventCallback mInstanceName = '%s', content = '%s'",
                 mInstanceName.data(), content->data());
   // Set to true if we find publishable data.
   bool publish = false;

   // Set the subscription state to "active".
   mSubscriptionState = "active";

   // Save the content as text for the RFC 4662 resource list events.
   mContent.remove(0);
   mContent.append(*content);
   mContentPresent = TRUE;

   // Dissect the XML for each dialog event and store it in a map
   // so we can construct BroadWorks-style resource list events
   // (which have to have full state).

   // Initialize Tiny XML document object.
   TiXmlDocument xmlDialogEvent;
   TiXmlNode* dialog_info_node;
   if (
       // Load the XML into it.
       xmlDialogEvent.Parse(mContent.data()) &&
       // Find the top element, which should be a <dialog-info>.
       (dialog_info_node = xmlDialogEvent.FirstChild("dialog-info")) != NULL &&
       dialog_info_node->Type() == TiXmlNode::ELEMENT)
   {
      // Check the state attribute.
      const char* p = dialog_info_node->ToElement()->Attribute("state");
      if (p && strcmp(p, "full") == 0)
      {
         // If the state is "full", terminate all non-terminated dialogs.  (XECS-1668)
         terminateXmlDialogs();
         publish = true;
         OsSysLog::add(FAC_RLS, PRI_DEBUG,
                       "ResourceInstance::notifyEventCallback all non-terminated dialogs");
      }

      // Find all the <dialog> elements.
      for (TiXmlNode* dialog_node = 0;
           (dialog_node = dialog_info_node->IterateChildren("dialog",
                                                            dialog_node));
         )
      {
         if (dialog_node->Type() == TiXmlNode::ELEMENT)
         {
            TiXmlElement* dialog_element = dialog_node->ToElement();

            // Determine if the <dialog> is a bogus report of a NAT Keepalive
            // OPTIONS message, as reported by Polycom SPIP firmware 3.1.2.
            // (XTRN-425)  If so, ignore it.

#ifdef NAT_KEEPALIVE_DETECT
            const char* call_id_attr = dialog_element->Attribute("call-id");
            // Reject <dialog>s on the narrowest grounds, that is, only if the
            // call-id attribute is present and contains NAT_KEEPALIVE_SIGNATURE.
            const bool ok =
               !(call_id_attr &&
                 strstr(call_id_attr,
                        NAT_KEEPALIVE_SIGNATURE) != NULL);
#else
            const bool ok = true;
#endif
            if (ok)
            {
               // Now that we've got a <dialog> element, edit it to fit
               // into a consolidated event notice.
               publish = true;

               // Prepend the resource instance name to the 'id'
               // attribute, so it is unique within the <resource>.
               UtlString id(mInstanceName);
               // mInstanceName is guaranteed to not contain ';', because
               // it is a dialog handle that we generate by concatenating
               // the Call-Id and tags using ',' as a separator.  And ';'
               // may not appear in Call-Ids or tags.
               id.append(";");
               id.append(dialog_element->Attribute("id"));
               dialog_element->SetAttribute("id", id.data());

               // Prepare the display name, so we can insert it easily
               // when we generate consolidated events.
               // Find or add the <local> element.
               TiXmlNode* local = dialog_element->FirstChild("local");
               if (!local)
               {
                  local = dialog_element->LinkEndChild(new TiXmlElement("local"));
               }
               // Find or add the <local><identity> element.
               TiXmlNode* identity = local->FirstChild("identity");
               if (!identity)
               {
                  identity =
                     local->LinkEndChild(new TiXmlElement("identity"));
               }
               // Clear the display attribute.
               identity->ToElement()->SetAttribute("display", "");

               // Put the resource URI as the content of the
               // <local><identity> element.
               // First, remove all text children.
               TiXmlNode* child;
               for (TiXmlNode* prev_child = 0;
                    (child = identity->IterateChildren(prev_child));
                  )
               {
                  if (child->Type() == TiXmlNode::TEXT)
                  {
                     identity->RemoveChild(child);
                     // Leave prev_child unchanged.
                  }
                  else
                  {
                     prev_child = child;
                  }
               }
               // Insert a text child containing the URI.
               identity->LinkEndChild(new TiXmlText(getResourceCached()->
                                                    getUri()->data()));

               // Now that we have the XML all nice and pretty, store a copy of
               // it in mXmlDialogs.
               // Clone the XML and create a UtlVoidPtr to wrap it.
               TiXmlElement* alloc_xml = dialog_element->Clone()->ToElement();

               // Look for an earlier version of this dialog in the hash map.
               UtlVoidPtr* p = dynamic_cast <UtlVoidPtr*> (mXmlDialogs.findValue(&id));
               if (p)
               {
                  // Replace the old XML with new XML.
                  delete static_cast <TiXmlElement*> (p->getValue());
                  p->setValue(alloc_xml);
                  OsSysLog::add(FAC_RLS, PRI_DEBUG,
                                "ResourceInstance::notifyEventCallback replaced dialog with id '%s'",
                                id.data());
               }
               else
               {
                  // Check that we don't have too many dialogs.
                  if (mXmlDialogs.entries() <
                      getResourceListServer()->getMaxDialogsInResInst())
                  {
                     mXmlDialogs.insertKeyAndValue(new UtlString(id),
                                                   new UtlVoidPtr(alloc_xml));
                     OsSysLog::add(FAC_RLS, PRI_DEBUG,
                                   "ResourceInstance::notifyEventCallback added dialog with id '%s'",
                                   id.data());
                  }
                  else
                  {
                     // Free alloc_xml, because we aren't saving a pointer to it.
                     delete alloc_xml;
                     OsSysLog::add(FAC_RLS, PRI_ERR,
                                   "ResourceInstance::notifyEventCallback cannot add dialog with id '%s', already %zu in ResourceInstance '%s'",                                id.data(), mXmlDialogs.entries(),
                                   mInstanceName.data());
                  }
               }
            }
            else
            {
               // The <dialog> was rejected because it appears to report
               // a NAT Maintainer OPTIONS message.
               // We log this at DEBUG level because if these appear,
               // there is likely to be one every 20 seconds.
               OsSysLog::add(FAC_RLS, PRI_DEBUG,
                             "ResourceInstance::notifyEventCallback "
                             "ignored <dialog> reporting a NAT Keepalive message "
                             "in subscription dialog handle '%s' - "
                             "see XTRN-426",
                             mInstanceName.data());
            }
         }
      }
   }
   else
   {
      // Report error parsing XML.
      OsSysLog::add(FAC_RLS, PRI_ERR,
                    "ResourceInstance::notifyEventCallback "
                    "Dialog event from '%s' not parsable.",
                    getResourceCached()->getUri()->data());
      OsSysLog::add(FAC_RLS, PRI_INFO,
                    "ResourceInstance::notifyEventCallback "
                    "Dialog event content is '%s'",
                    content->data());
      // Throw away the content, since we cannot generate matching
      // consolidated content.
      mContentPresent = FALSE;
      mContent.remove(0);
      destroyXmlDialogs();
   }

   // Get the change published, if we found <dialog> that was not incorrect.
   if (publish)
   {
      getResourceCached()->setToBePublished(FALSE, getResourceCached()->getUri());
   }
}
Example #10
0
// Update the subscriptions we maintain to agree with the current contact state
void ContactSet::updateSubscriptions()
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "ContactSet::updateSubscriptions mUri = '%s'",
                 mUri.data());

   // First, scan mSubscriptions to construct a list of all the
   // call-id/contact combinations.
   // (If a phone reboots and starts registering with a different Call-Id,
   // the call-id/contact combination will be different even if the contact
   // URI is unchanged.  So the new registration will appear to be different
   // to this machinery, and it will establish a new SubscriptionSet to
   // the contact URI.  This compensates for the fact that the previous
   // SubscriptionSet to the contact URI appears to the RLS to be
   // working but the phone no longer knows of the subscription.  The
   // reg events will eventually terminate the old combination and we
   // will delete its SubscriptionSet.)
   UtlHashBag callid_contacts;

   UtlHashMapIterator subs_itor(mSubscriptions);
   while (subs_itor())
   {
      if (OsSysLog::willLog(FAC_RLS, PRI_DEBUG))
      {
         OsSysLog::add(FAC_RLS, PRI_DEBUG,
                       "ContactSet::updateSubscriptions subscription '%s'",
                       (dynamic_cast <UtlString*> (subs_itor.key()))->data());
      }
      UtlHashMap* contact_state =
         dynamic_cast <UtlHashMap*> (subs_itor.value());
      OsSysLog::add(FAC_RLS, PRI_DEBUG,
                    "ContactSet::updateSubscriptions contact_state = %p",
                    contact_state);
      UtlHashMapIterator contact_itor(*contact_state);
      while (contact_itor())
      {
         UtlString* contact =
            dynamic_cast <UtlString*> (contact_itor.value());
         if (OsSysLog::willLog(FAC_RLS, PRI_DEBUG))
         {
            OsSysLog::add(FAC_RLS, PRI_DEBUG,
                          "ContactSet::updateSubscriptions contact id '%s', Call-Id/URI '%s'",
                          (dynamic_cast <UtlString*> (contact_itor.key()))->data(),
                          contact->data());
         }
         // Check if the contact is already in callid_contacts.
         if (!callid_contacts.find(contact))
         {
            // If not, add it.
            UtlString* c = new UtlString(*contact);
            callid_contacts.insert(c);
            OsSysLog::add(FAC_RLS, PRI_DEBUG,
                          "ContactSet::updateSubscriptions contact added");
         }
      }
   }

   // If the list of callid_contacts is empty, add mUri as the default contact
   // (with an empty registration Call-Id).
   if (callid_contacts.isEmpty())
   {
      OsSysLog::add(FAC_RLS, PRI_DEBUG,
                    "ContactSet::updateSubscriptions adding default contact mUri = '%s'",
                    mUri.data());
      UtlString* c = new UtlString(";");
      c->append(mUri);
      callid_contacts.insert(c);
   }

   // Now that we have a clean list of callid_contacts, update
   // SubscriptionSets to match it.

   // If we both terminate subscriptions and create subscriptions,
   // wait a short while to allow the terminations to complete.  This
   // should not be necessary, but it makes life easier on Polycom
   // phones which (at this time) cannot support two subscriptions at
   // a time, and if the termination of the old subscription arrives
   // after the initiation of the new subscription, the new
   // subscription will be lost.
   // This variable tracks whether such a wait is needed before a
   // subscription is started.
   bool subscription_ended_but_no_wait_done_yet = false;

   // Iterate through the list of SubscriptionSets and remove any that aren't
   // in callid_contacts.
   {
      UtlHashMapIterator itor(mSubscriptionSets);
      UtlString* ss;
      while ((ss = dynamic_cast <UtlString*> (itor())))
      {
         if (!callid_contacts.find(ss))
         {
            OsSysLog::add(FAC_RLS, PRI_DEBUG,
                          "ContactSet::updateSubscriptions deleting subscription for '%s' in mUri = '%s'",
                          ss->data(), mUri.data());
            mSubscriptionSets.destroy(ss);
	    subscription_ended_but_no_wait_done_yet = true;
         }
      }
   }

   // Iterate through callid_contacts and add a SubscriptionSet for
   // any that aren't in mSubscriptionSets.
   // We don't limit the number of additions here, as the size of
   // callid_contacts is guarded by the tests in notifyEventCallback.
   {
      UtlHashBagIterator itor(callid_contacts);
      UtlString* callid_contact;
      while ((callid_contact = dynamic_cast <UtlString*> (itor())))
      {
         if (!mSubscriptionSets.find(callid_contact))
         {
	    // If we both terminate subscriptions and create subscriptions,
	    // wait a short while to allow the terminations to complete.
            // Note that this wait must be no more than the bulk add/delete
            // change delay, as that is how fast ResourceListFileReader
            // generates requests to the ResourceListServer task.
            int wait = getResourceListServer()->getChangeDelay();
            if (wait > 0)
            {
               if (subscription_ended_but_no_wait_done_yet)
               {
                  OsSysLog::add(FAC_RLS, PRI_DEBUG,
                                "ContactSet::updateSubscriptions waiting for %d msec",
                                wait);
                  OsTask::delay(wait);
                  subscription_ended_but_no_wait_done_yet = false;
               }
            }

            OsSysLog::add(FAC_RLS, PRI_DEBUG,
                          "ContactSet::updateSubscriptions adding subscription for '%s' in mUri = '%s'",
                          callid_contact->data(), mUri.data());
            // Get the contact URI into a UtlString.
            UtlString uri(callid_contact->data() +
                          callid_contact->index(';') +
                          1);
            mSubscriptionSets.insertKeyAndValue(new UtlString(*callid_contact),
                                                new SubscriptionSet(mResource,
                                                                    uri));
         }
      }
   }

   // Free callid_contacts.
   callid_contacts.destroyAll();
}
Example #11
0
// Process a notify event callback.
// This involves parsing the content of the callback and revising our record
// of the state for that subscription.  Then, we must regenerate the list
// of contacts and update the set of subscriptions to match the current
// contacts.
void ContactSet::notifyEventCallback(const UtlString* dialogHandle,
                                     const UtlString* content)
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "ContactSet::notifyEventCallback mUri = '%s', dialogHandle = '%s', content = '%s'",
                 mUri.data(), dialogHandle->data(), content->data());

   // Parse the XML and update the contact status.

   // Find the UtlHashMap for this subscription.
   UtlHashMap* state_from_this_subscr =
      dynamic_cast <UtlHashMap*> (mSubscriptions.findValue(dialogHandle));
   if (!state_from_this_subscr)
   {
      // No state for this dialogHandle, so we need to add one.
      OsSysLog::add(FAC_RLS, PRI_WARNING,
                    "ContactSet::notifyEventCallback mSubscriptions element does not exist for this dialog handle mUri = '%s', dialogHandle = '%s'",
                    mUri.data(),
                    dialogHandle->data());

      // Check that we don't have too many subscriptions.
      if (mSubscriptions.entries() <
          getResourceListServer()->getMaxRegSubscInResource())
      {
         state_from_this_subscr = new UtlHashMap;
         mSubscriptions.insertKeyAndValue(new UtlString(*dialogHandle),
                                          state_from_this_subscr);
      }
      else
      {
         OsSysLog::add(FAC_RLS, PRI_ERR,
                       "ContactSet::notifyEventCallback cannot add reg subscription with dialog handle '%s', already %zu in ContactSet '%s'",
                       dialogHandle->data(), mSubscriptions.entries(),
                       mUri.data());
      }
   }

   // Perform the remainder of the processing if we obtained a hash map
   // from the above processing.
   if (state_from_this_subscr)
   {
      // Initialize Tiny XML document object.
      TiXmlDocument document;
      TiXmlNode* reginfo_node;
      if (
         // Load the XML into it.
         document.Parse(content->data()) &&
         // Find the top element, which should be a <reginfo>.
         (reginfo_node = document.FirstChild("reginfo")) != NULL &&
         reginfo_node->Type() == TiXmlNode::ELEMENT)
      {
         // Check the state attribute.
         const char* p = reginfo_node->ToElement()->Attribute("state");
         if (p && strcmp(p, "full") == 0)
         {
            // If the state is "full", delete the current state.
            state_from_this_subscr->destroyAll();
            OsSysLog::add(FAC_RLS, PRI_DEBUG,
                          "ContactSet::notifyEventCallback clearing state");
         }

         // Find all the <registration> elements for this URI.
         for (TiXmlNode* registration_node = 0;
              (registration_node =
               reginfo_node->IterateChildren("registration",
                                             registration_node));
            )
         {
            // Do not test the aor attribute of <registration> elements
            // because the reg event server may be operating with the real
            // AOR for this URI, whereas we may have been told of an alias.

            // Find all the <contact> elements.
            for (TiXmlNode* contact_node = 0;
                 (contact_node =
                  registration_node->IterateChildren("contact",
                                                     contact_node));
               )
            {
               TiXmlElement* contact_element = contact_node->ToElement();

               // Get the state attribute.
               const char* state = contact_element->Attribute("state");
               // Get the id attribute
               const char* id = contact_element->Attribute("id");

               // Get the Contact URI for the phone. If a GRUU address is present we should
               // use that as the Contact URI. Otherwise, use the contact URI present in the
               // "uri" element, and append any path headers that are present to the ROUTE
               // header parameter in the URI. This will ensure proper routing in HA systems.
               // Please refer to XECS-1694 for more details.

               UtlString* uri_allocated = new UtlString;
               UtlBoolean check_uri = TRUE;
               TiXmlNode* pub_gruu_node = contact_element->FirstChild("gr:pub-gruu");
               if (pub_gruu_node)
               {
                  TiXmlElement* pub_gruu_element = pub_gruu_node->ToElement();
                  UtlString pub_gruu_uri(pub_gruu_element->Attribute("uri"));
                  if (!pub_gruu_uri.isNull())
                  {
                     // Check the URI Scheme. Only accept the GRUU address if it is of either
                     // a sip or sips scheme
                     Url tmp(pub_gruu_uri, TRUE);
                     Url::Scheme uriScheme = tmp.getScheme();
                     if(Url::SipUrlScheme == uriScheme ||
                        Url::SipsUrlScheme == uriScheme)
                     {
                        tmp.removeAngleBrackets();
                        tmp.getUri(*uri_allocated);
                        check_uri = FALSE;
                     }
                  }
               }

               // If we did not find a GRUU address, then use the address in the "uri" element as the
               // contact URI, and check for path headers.
               if (check_uri)
               {
                  TiXmlNode* u = contact_element->FirstChild("uri");
                  if (u)
                  {
                     textContentShallow(*uri_allocated, u);

                     // Iterate through all the path header elements. Path headers are stored in the
                     // "unknown-param" elements that have a "name" attribute value of "path".
                     for (TiXmlNode* unknown_param_node = 0;
                          (unknown_param_node = contact_node->IterateChildren("unknown-param",
                                                                               unknown_param_node));
                          )
                     {
                        TiXmlElement* unknown_param_element = unknown_param_node->ToElement();
                        UtlString path(unknown_param_element->Attribute("name"));
                        if(0 == path.compareTo("path"))
                        {
                           UtlString pathVector;
                           textContentShallow(pathVector, unknown_param_node);
                           if(!pathVector.isNull())
                           {
                              Url contact_uri(*uri_allocated, TRUE);

                              // there is already a Route header parameter in the contact; append it to the
                              // Route derived from the Path vector.
                              UtlString existingRouteValue;
                              if ( contact_uri.getHeaderParameter(SIP_ROUTE_FIELD, existingRouteValue))
                              {
                                 pathVector.append(SIP_MULTIFIELD_SEPARATOR);
                                 pathVector.append(existingRouteValue);
                              }
                              contact_uri.setHeaderParameter(SIP_ROUTE_FIELD, pathVector);
                              contact_uri.removeAngleBrackets();
                              contact_uri.getUri(*uri_allocated);
                           }
                        }
                     }
                  }
               }

               // Only process <contact> elements that have the needed values.
               if (state && state[0] != '\0' &&
                   id && id[0] != '\0' &&
                   !uri_allocated->isNull())
               {
                  UtlString* id_allocated = new UtlString(id);

                  if (strcmp(state, "active") == 0)
                  {
                     // Add the contact if it is not already present.
                     if (!state_from_this_subscr->find(id_allocated))
                     {
                        // Prepend the registration Call-Id and ';' to *uri_allocated..
                        uri_allocated->insert(0, ';');
                        const char* call_id = contact_element->Attribute("callid");
                        if (call_id)
                        {
                           uri_allocated->insert(0, call_id);
                        }

                        // Check that we don't have too many contacts.
                        if (state_from_this_subscr->entries() <
                            getResourceListServer()->getMaxContInRegSubsc())
                        {
                           // Insert the registration record.
                           if (state_from_this_subscr->insertKeyAndValue(id_allocated,
                                                                         uri_allocated))
                           {
                              OsSysLog::add(FAC_RLS, PRI_DEBUG,
                                            "ContactSet::notifyEventCallback adding id = '%s' Call-Id;URI = '%s'",
                                            id, uri_allocated->data());
                              id_allocated = NULL;
                              uri_allocated = NULL;
                           }
                           else
                           {
                              OsSysLog::add(FAC_RLS, PRI_ERR,
                                            "ContactSet::notifyEventCallback adding id = '%s' Call-Id;URI = '%s' failed",
                                            id_allocated->data(), uri_allocated->data());
                              OsSysLog::add(FAC_RLS, PRI_DEBUG,
                                            "ContactSet::notifyEventCallback *state_from_this_subscr is:");
                              UtlHashMapIterator itor(*state_from_this_subscr);
                              UtlContainable* k;
                              while ((k = itor()))
                              {
                                 UtlContainable* v = itor.value();
                                 OsSysLog::add(FAC_RLS, PRI_DEBUG,
                                               "ContactSet::notifyEventCallback (*state_from_this_subscr)['%s'] = '%s'",
                                               (dynamic_cast <UtlString*> (k))->data(),
                                               (dynamic_cast <UtlString*> (v))->data());
                              }
                           }
                        }
                        else
                        {
                           OsSysLog::add(FAC_RLS, PRI_ERR,
                                         "ContactSet::notifyEventCallback cannot add Call-Id;RUI '%s', already %zu in ContactSet '%s' subscription '%s'",
                                         uri_allocated->data(),
                                         state_from_this_subscr->entries(),
                                         mUri.data(), dialogHandle->data());
                        }
                     }
                  }
                  else if (strcmp(state, "terminated") == 0)
                  {
                     // Delete it from the contact state.
                     state_from_this_subscr->destroy(id_allocated);
                     OsSysLog::add(FAC_RLS, PRI_DEBUG,
                                   "ContactSet::notifyEventCallback deleting id = '%s'",
                                   id);
                  }
                  // Free id_allocated, if it is not pointed to by a data
                  // structure, which is indicated by setting it to NULL.
                  if (id_allocated)
                  {
                     delete id_allocated;
                  }
               }
               else
               {
                  OsSysLog::add(FAC_RLS, PRI_ERR,
                                "ContactSet::notifyEventCallback <contact> element with id = '%s' is missing id, state, and/or URI",
                                id ? id : "(missing)");
               }
               // Free uri_allocated, if it is not pointed to by a data
               // structure, which is indicated by setting it to NULL.
               if (uri_allocated)
               {
                  delete uri_allocated;
               }
            }
         }
      }
      else
      {
         // Error parsing the contents.
         OsSysLog::add(FAC_RLS, PRI_ERR,
                       "ContactSet::notifyEventCallback malformed reg event content for mUri = '%s'",
                       mUri.data());
      }

      // Update the subscriptions we maintain to agree with the new state.
      updateSubscriptions();
   }
}
Example #12
0
void ContactSet::subscriptionEventCallback(
   const UtlString* earlyDialogHandle,
   const UtlString* dialogHandle,
   SipSubscribeClient::SubscriptionState newState,
   const UtlString* subscriptionState)
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "ContactSet::subscriptionEventCallback mUri = '%s', newState = %d, earlyDialogHandle = '%s', dialogHandle = '%s', subscriptionState = '%s'",
                 mUri.data(),
                 newState, mSubscriptionEarlyDialogHandle.data(),
                 dialogHandle->data(), subscriptionState->data());

   switch (newState)
   {
   case SipSubscribeClient::SUBSCRIPTION_INITIATED:
      break;
   case SipSubscribeClient::SUBSCRIPTION_SETUP:
   {
         // Remember it in mSubscriptions, if there isn't already an entry.
         if (!mSubscriptions.find(dialogHandle))
         {
            // Check that we don't have too many subscriptions.
            if (mSubscriptions.entries() <
                getResourceListServer()->getMaxRegSubscInResource())
            {
               // Add this ContactSet to mNotifyMap for the subscription.
               // (::addNotifyMapping() copies *dialogHandle.)
               getResourceListSet()->addNotifyMapping(*dialogHandle, this);

               // Remember to make a copy of *dialogHandle.
               mSubscriptions.insertKeyAndValue(new UtlString(*dialogHandle),
                                                new UtlHashMap);
            }
            else
            {
               OsSysLog::add(FAC_RLS, PRI_ERR,
                             "ContactSet::subscriptionEventCallback cannot add reg subscription with dialog handle '%s', already %zu in ContactSet '%s'",
                             dialogHandle->data(), mSubscriptions.entries(),
                             mUri.data());
            }
         }
         else
         {
            OsSysLog::add(FAC_RLS, PRI_DEBUG,
                          "ContactSet::subscriptionEventCallback mSubscriptions element already exists for this dialog handle mUri = '%s', dialogHandle = '%s'",
                          mUri.data(),
                          dialogHandle->data());
         }
   }
   break;
   case SipSubscribeClient::SUBSCRIPTION_TERMINATED:
   {
      // Remove this ContactSet from mNotifyMap for the subscription.
      getResourceListSet()->deleteNotifyMapping(dialogHandle);

      // Delete this subscription from mSubscriptions.
      mSubscriptions.destroy(dialogHandle);
      // Update the subscriptions.
      updateSubscriptions();
   }
   break;
   }
}