// Constructor ContactSet::ContactSet(ResourceCached* resource, UtlString& uri) : mResource(resource), mUri(uri) { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ContactSet:: this = %p, resource = %p, mUri = '%s'", this, mResource, mUri.data()); // Set up the subscriptions. // Until we have any information from our SUBSCRIBE for "reg" events, // there will be one subscription to mUri. updateSubscriptions(); // Start the subscription. UtlBoolean ret; UtlString mUriNameAddr = "<" + mUri + ">"; OsSysLog::add(FAC_RLS, PRI_DEBUG, "SubscriptionSet:: mUri = '%s', mUriNameAddr = '%s'", mUri.data(), mUriNameAddr.data()); ret = getResourceListServer()->getSubscribeClient(). addSubscription(mUri.data(), REG_EVENT_TYPE, REG_EVENT_CONTENT_TYPE, getResourceListServer()->getClientFromURI(), mUriNameAddr.data(), getResourceListServer()->getClientContactURI(), getResourceListServer()->getResubscribeInterval(), getResourceListSet(), ResourceListSet::subscriptionEventCallbackAsync, ResourceListSet::notifyEventCallbackAsync, mSubscriptionEarlyDialogHandle); if (ret) { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ContactSet:: addSubscription succeeded mUri = '%s', mSubscriptionEarlyDialogHandle = '%s'", mUri.data(), mSubscriptionEarlyDialogHandle.data()); // Add this ContactSet to mSubscribeMap. getResourceListSet()->addSubscribeMapping(&mSubscriptionEarlyDialogHandle, this); } else { OsSysLog::add(FAC_RLS, PRI_WARNING, "ContactSet:: addSubscription failed mUri = '%s', mSubscriptionEarlyDialogHandle = '%s'", mUri.data(), mSubscriptionEarlyDialogHandle.data()); } }
// Destructor ResourceInstance::~ResourceInstance() { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ResourceInstance::~ mInstanceName = '%s'", mInstanceName.data()); // Delete this ResourceInstance from mNotifyMap. getResourceListSet()->deleteNotifyMapping(&mInstanceName); // Terminate the subscription for this resource instance. UtlBoolean ret; ret = getResourceListServer()->getSubscribeClient(). endSubscriptionGroup(mInstanceName.data()); OsSysLog::add(FAC_RLS, ret ? PRI_DEBUG : PRI_WARNING, "ResourceInstance::~ endSubscriptionGroup %s mInstanceName = '%s'", ret ? "succeeded" : "failed", mInstanceName.data()); // Delete the XML in mXmlDialogs. destroyXmlDialogs(); mContentPresent = FALSE; // This destructor does not mark any resource lists for publication, // as the termination of this instance has been published previously, // and there is no need to publish this instance's vanishing quickly. }
// Delete a resource identified by position. bool ResourceList::deleteResourceAt(size_t at) { bool resource_deleted = false; Os::Logger::instance().log(FAC_RLS, PRI_DEBUG, "ResourceList::deleteResourceAt mUserPart = '%s', at = %d", mUserPart.data(), (int) at); // Remove the ResourceReference from mResourcesList. ResourceReference* resource = dynamic_cast <ResourceReference*> (mResourcesList.removeAt(at)); if (resource) { // Remove the URI from mChangesList. UtlString uri(*(resource->getUri())); mChangesList.destroy(&uri); // Delete the ResourceReference. resource_deleted = getResourceListSet()->getResourceCache(). destroyResourceReference(resource); // Publish the change. setToBePublished(); } return resource_deleted; }
// Incrementally remove and delete one component of the ResourceList. void ResourceList::shrink(bool& listEmpty, bool& resourceDeleted) { resourceDeleted = false; Os::Logger::instance().log(FAC_RLS, PRI_DEBUG, "ResourceList::shrink mUserPart = '%s'", mUserPart.data()); // Incrementally remove element from the ResourceReferences and delete it. // Get pointer to the first ResourceReference. ResourceReference* rr = dynamic_cast <ResourceReference*> (mResourcesList.first()); // If one exists, delete it. if (rr) { mResourcesList.removeReference(rr); // A ResourceReference can be deleted outright because it contains // no lists. // It may cause deletion of the ResourceCached that it points to. resourceDeleted = getResourceListSet()->getResourceCache(). destroyResourceReference(rr); // Publish the change. setToBePublished(); } // mChangesList is not handled here, as it is just a list of UtlStrings. listEmpty = rr == NULL; }
// Constructor ResourceReference::ResourceReference(ResourceList* resourceList, const char* uri, const char* nameXml, const char* display_name) : mResourceList(resourceList), mNameXml(nameXml), mDisplayName(display_name) { // If the name XML is not empty and does not end with LF, add CR-LF. if (!mNameXml.isNull() && mNameXml(mNameXml.length() - 1) != '\n') { mNameXml += "\r\n"; } OsSysLog::add(FAC_RLS, PRI_DEBUG, "ResourceReference:: this = %p, resourceList = %p, mUri = '%s', mNameXml = '%s', mDisplayName = '%s'", this, mResourceList, uri, mNameXml.data(), mDisplayName.data()); // Pass the request to the ResourceCache and save the (ResourceCached*) // that it returns. mResourceCached = getResourceListSet()->getResourceCache(). addReferenceToResource(this, uri); // Publish the change to our containing ResourceList. resourceList->setToBePublished(); }
// Destructor ContactSet::~ContactSet() { // Delete this ContactSet from mSubscribeMap (for the "reg" subscription). getResourceListSet()->deleteSubscribeMapping(&mSubscriptionEarlyDialogHandle); // End the "reg" subscription. UtlBoolean ret; ret = getResourceListServer()->getSubscribeClient(). endSubscriptionGroup(mSubscriptionEarlyDialogHandle); OsSysLog::add(FAC_RLS, ret ? PRI_DEBUG : PRI_WARNING, "ContactSet::~ endSubscriptionGroup %s mUri = '%s', mSubscriptionEarlyDialogHandle = '%s'", ret ? "succeeded" : "failed", mUri.data(), mSubscriptionEarlyDialogHandle.data()); // Remove this ContactSet from mNotifyMap for all subscriptions. { UtlHashMapIterator itor(mSubscriptions); UtlString* handle; while ((handle = dynamic_cast <UtlString*> (itor()))) { getResourceListSet()->deleteNotifyMapping(handle); } } // Delete the contents of mSubscriptions. mSubscriptions.destroyAll(); // Delete the subordinate SubscriptionSet's for the contacts. { // Have to use a loop to remove items individually, because // destroyAll() deadlocks with the access to mSubscriptionSets // during the the attempt to publish new status for the resource // as the ResourceInstances are recursively deleted. UtlHashMapIterator itor(mSubscriptionSets); UtlContainable* k; while ((k = itor())) { UtlContainable* v = itor.value(); mSubscriptionSets.removeReference(k); delete k; delete v; } } }
// Constructor ResourceCached::ResourceCached(ResourceCache* resourceCache, const char* uri) : UtlString(uri), mResourceCache(resourceCache), mContactSetP(0), mSeqNo(getResourceListSet()->getNextSeqNo()), mRefreshTimer(getResourceListServer()->getResourceListTask(). getMessageQueue(), (void*)(mSeqNo + ResourceListSet::REFRESH_TIMEOUT)) { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ResourceCached:: this = %p, URI = '%s'", this, data()); // Add the sequence number mapping. getResourceListSet()->addResourceSeqNoMapping(mSeqNo, this); // Start subscriptions. startSubscriptions(); }
// 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()); } }
// Destructor ResourceCached::~ResourceCached() { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ResourceCached::~ this = %p, URI = '%s'", this, data()); // Terminate any existing subscriptions. terminateSubscriptions(); // Delete the sequence number mapping. getResourceListSet()->deleteResourceSeqNoMapping(mSeqNo); }
// Declare that the contents have changed and need to be published. void ResourceCached::setToBePublished(UtlBoolean publishNow, const UtlString* chgUri) { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ResourceCached::setToBePublished URI = '%s'", data()); // Iterate through all our ResourceReference's and mark their // containing ResourceList's as needing to be published. UtlHashBagIterator itor(mReferences); ResourceReference* reference; while ((reference = dynamic_cast <ResourceReference*> (itor()))) { reference->getResourceList()->setToBePublished(chgUri); } // If publishing is suppressed, do nothing further. if (!getResourceListSet()->publishingSuspended()) { if (publishNow) { // If publishNow is specified, loop through the // ResourceList's again and publish them. (Doing this in a // second loop avoids publishing a list twice if it contains // this resource twice.) UtlHashBagIterator itor(mReferences); ResourceReference* reference; while ((reference = dynamic_cast <ResourceReference*> (itor()))) { reference->getResourceList()->publishIfNecessary(); } } else { // In the more usual case, we just set the publishing timer. getResourceListSet()->schedulePublishing(); } } }
// Destructor ResourceReference::~ResourceReference() { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ResourceReference::~ this = %p, URI = '%s'", this, mResourceCached->getUri()->data()); // Tell the ResourceCache that we no longer have a reference to the // ResourceCached. getResourceListSet()->getResourceCache(). deleteReferenceToResource(this, mResourceCached); // Publish the change to our containing ResourceList. getResourceList()->setToBePublished(); }
// Publish the contents if necessary and clear the publishing indicator. void ResourceList::publishIfNecessary() { Os::Logger::instance().log(FAC_RLS, PRI_DEBUG, "ResourceList::publishIfNecessary mUserPart = '%s'", mUserPart.data()); // Do nothing if publishing is suspended. if (!getResourceListSet()->publishingSuspended()) { if (mChangesToPublish) { mChangesToPublish = FALSE; publish(); } } }
// Destructor SubscriptionSet::~SubscriptionSet() { OsSysLog::add(FAC_RLS, PRI_DEBUG, "SubscriptionSet::~ mUri = '%s'", mUri.data()); // Delete this SubscriptionSet from mSubscribeMap. getResourceListSet()->deleteSubscribeMapping(&mSubscriptionEarlyDialogHandle); // Terminate the master subscription. UtlBoolean ret; ret = getResourceListServer()->getSubscribeClient(). endSubscription(mSubscriptionEarlyDialogHandle.data()); OsSysLog::add(FAC_RLS, ret ? PRI_DEBUG : PRI_WARNING, "SubscriptionSet::~ endSubscription %s mUri = '%s', mSubscriptionEarlyDialogHandle = '%s'", ret ? "succeeded" : "failed", mUri.data(), mSubscriptionEarlyDialogHandle.data()); // Delete all the child subscriptions. // First, send their termination content. UtlSListIterator itor(mSubscriptions); ResourceInstance* inst; while ((inst = dynamic_cast <ResourceInstance*> (itor()))) { // Set the state of the resource instance to terminated. // We do not know what their termination reason is. // (:TODO: But it might be possible to determine that.) inst->terminateContent("terminated"); } // Since we are about to delete the ResourceInstances, we have to // publish their terminated state now or forgo doing so. getResourceCached()->setToBePublished(TRUE); // Delete the ResourceInstance objects. mSubscriptions.destroyAll(); // Record that the content has been changed again and needs to be // published. getResourceCached()->setToBePublished(); }
// Constructor ResourceInstance::ResourceInstance(SubscriptionSet* parent, const char* instanceName, const char* subscriptionState) : mSubscriptionSet(parent), mInstanceName(instanceName), mSubscriptionState(subscriptionState), mContentPresent(FALSE) { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ResourceInstance:: mInstanceName = '%s', mSubscriptionSet = '%s', subscriptionState = '%s'", mInstanceName.data(), mSubscriptionSet->getUri()->data(), subscriptionState); // Add this ResourceInstance to mNotifyMap. getResourceListSet()->addNotifyMapping(mInstanceName, this); // Publish the state with the new instance. getResourceCached()->setToBePublished(); }
// Generate and publish the content for the resource list. void ResourceList::publish() { UtlBoolean publishingSuspended = getResourceListSet()->publishingSuspended(); Os::Logger::instance().log(FAC_RLS, PRI_DEBUG, "ResourceList::publish mUserPart = '%s', publishingSuspended = %d", mUserPart.data(), publishingSuspended); // Do the publishing if it's not suppressed. if (!publishingSuspended) { // Generate and publish the RFC 4662 ("full") notice body. genAndPublish(FALSE, mResourceListUri); // Generate and publish the consolidated notice body. genAndPublish(TRUE, mResourceListUriCons); // Flush the list of partial updates. mChangesList.destroyAll(); } }
// Create and add a resource to the resource list. void ResourceList::addResource(const char* uri, const char* nameXml, const char* display_name, bool& resource_added, bool& resource_cached_created, ssize_t no_check_start, ssize_t no_check_end) { Os::Logger::instance().log(FAC_RLS, PRI_DEBUG, "ResourceList::addResource mUserPart = '%s', uri = '%s', nameXml = '%s', display_name = '%s', no_check_start = %d, no_check_end = %d", mUserPart.data(), uri, nameXml, display_name, (int) no_check_start, (int) no_check_end); // See if 'uri' is already in the list of ResourceReference's. ssize_t location; { UtlSListIterator itor(mResourcesList); ResourceReference* r; // Exit this loop if 'itor' runs out of elements, or if the resource // URI of an element (that is not excluded) string-equals 'uri'. for (location = 0; (r = dynamic_cast <ResourceReference*> (itor())) != NULL; location++) { if (// If this element is not excluded from comparison and ... !(no_check_start <= location && location <= location) && // ... it has the same URI value as the new element ... r->getUri()->compareTo(uri) == 0) { // ... exit the loop and return an error. break; } } // At this point, r != NULL if some ResourceReference in mResourcesList // that is not between no_check_start and no_check_end has // getUri() string-equal to 'uri'. resource_added = r == NULL; } resource_cached_created = false; if (resource_added) { ResourceReference* rr; getResourceListSet()->getResourceCache(). createResourceReference(this, uri, nameXml, display_name, rr, resource_cached_created); mResourcesList.append(rr); // Publish the change. setToBePublished(); } else { Os::Logger::instance().log(FAC_RLS, PRI_WARNING, "ResourceList::addResource Resource URI '%s' is already in resource list '%s' at location %d", uri, mUserPart.data(), (int) location); } Os::Logger::instance().log(FAC_RLS, PRI_DEBUG, "ResourceList::addResource " "resource_added = %d, resource_cached_created = %d", resource_added, resource_cached_created); }
// 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"; }
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; } }