// Dump the object's internal state.
void AppearanceGroup::dumpState()
{
   // indented 4
   Os::Logger::instance().log(FAC_SAA, PRI_INFO,
                 "\t    AppearanceGroup %p mSharedUser = '******', mSubscriptionEarlyDialogHandle = '%s'",
                 this, mSharedUser.data(), mSubscriptionEarlyDialogHandle.data());

   UtlHashMapIterator itor3(mAppearances);
   UtlString* handle;
   while ((handle = dynamic_cast <UtlString*> (itor3())))
   {
      Appearance* ss = dynamic_cast <Appearance*> (itor3.value());
      ss->dumpState();
   }
}
   void AppearanceTest()
   {
      instantiateAllTestFixtures( "appearance-groups1.xml",
                                  "subscription1",
                                  "credential1",
                                  "177.0.0.1:54140");

      UtlString sharedUri = "sip:[email protected]:54140";
      UtlString app1uri = "sip:127.0.0.1:45141";
      UtlString dialogHandle;

      // receive the reg-info subscribe
      SipMessage request;
      UtlString b;
      ssize_t l;
      while(getNextMessageFromAppearanceAgentUnderTest( request, 5 ))
      {
         request.getBytes(&b, &l);
         OsSysLog::add(FAC_RLS, PRI_DEBUG, "got message %s", b.data());
         UtlString method;
         request.getRequestMethod(&method);
         if(!request.isResponse() &&
            0 == method.compareTo(SIP_SUBSCRIBE_METHOD) )
         {
            // Accept the Subscription, regardless of whether it for a 'dialog' or 'reg' event
            // in order to stop retransmissions
            SipMessage regResponse;
            regResponse.setResponseData(&request, 202, "Accepted", app1uri);
            SipMessage * dispatchedMessage = new SipMessage(regResponse);
            dispatchedMessage->getBytes(&b, &l);
            OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data());
            pAppearanceAgentUnderTest->mServerUserAgent.dispatch(dispatchedMessage);

            // Deal with the two events separately
            UtlString eventField;
            request.getEventField(eventField);
            if(0 == eventField.compareTo("reg"))
            {
               UtlString contactInfo;
               request.getContactUri(0, &contactInfo);
               UtlString callid;
               request.getCallIdField(&callid);
               int cseq;
               request.getCSeqField(&cseq, NULL);

               Url toField;
               regResponse.getToUrl(toField);

               SipMessage regNotify;
               regNotify.setNotifyData(&request, 1, "", "", "reg");
               UtlString regInfo ("<?xml version=\"1.0\"?>\r\n"
                                 "<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" "
                                 "xmlns:gr=\"urn:ietf:params:xml:ns:gruuinfo\" version=\"911\" state=\"full\">\r\n"
                                 "   <registration aor=\"sip:[email protected]:54140\" id=\"sip:[email protected]:54140\" state=\"active\">\r\n "
                                 "      <contact id=\"sip:[email protected]:54140@@&lt;");
               regInfo.append(contactInfo);
               regInfo.append("&gt;\" state=\"active\" event=\"registered\" q=\"1\" callid=\"");
               regInfo.append(callid);
               regInfo.append("\" cseq=\"");
               regInfo.appendNumber(cseq);
               regInfo.append("\">\r\n");
               regInfo.append("<uri>");
               regInfo.append(app1uri);
               regInfo.append("</uri>");
               regInfo.append("      </contact>\r\n"
                              "   </registration>\r\n"
                              "</reginfo>");
               HttpBody * newBody = new HttpBody (regInfo, strlen(regInfo), "application/reginfo+xml");
               regNotify.setContentType("application/reginfo+xml");
               regNotify.setBody(newBody);

               // Set the From field the same as the to field from the 202 response, as it
               // contains the dialog identifying to tags
               regNotify.setRawFromField(toField.toString().data());
               sendToAppearanceAgentUnderTest( regNotify );
               regNotify.getBytes(&b, &l);
               OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent reg NOTIFY to AppAgent");
               OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data());
            }
            else if (0 == eventField.compareTo(SLA_EVENT_TYPE))
             {
                // should send empty NOTIFY, but no one will care
                // save dialogHandle for this subscription/Appearance (ignore retransmissions)
                if (dialogHandle.isNull())
                {
                   SipMessage fake(b);
                   fake.getDialogHandle(dialogHandle);
                   OsSysLog::add(FAC_RLS, PRI_DEBUG, "got SUBSCRIBE(sla) request: dialogHandle %s", dialogHandle.data());
                }
             }
         }
      }

      CPPUNIT_ASSERT( !dialogHandle.isNull() );
      OsSysLog::add(FAC_RLS, PRI_DEBUG, "we now have an Appearance - test it");
      AppearanceGroup* pAppGroup = pAppearanceAgentUnderTest->getAppearanceGroupSet().
         findAppearanceGroup(sharedUri);
      CPPUNIT_ASSERT( pAppGroup );

      Appearance* pApp = pAppGroup->findAppearance(dialogHandle);
      CPPUNIT_ASSERT( pApp );
      ASSERT_STR_EQUAL( app1uri.data(), pApp->getUri()->data() );

      // test adding a new dialog
      const char* dialogEventString =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"0\" state=\"partial\" entity=\"sip:[email protected]:54140\">\n"
            "<dialog id=\"1\" call-id=\"[email protected]\" local-tag=\"264460498\" remote-tag=\"1c10982\" direction=\"recipient\">\n"
            "<state>confirmed</state>\n"
            "<local>\n"
            "<identity>[email protected]:5120</identity>\n"
            "<target uri=\"sip:[email protected]:5120\">\n"
            "<param pname=\"x-line-id\" pval=\"0\"/>\n"
            "<param pname=\"+sip.rendering\" pval=\"yes\"/>\n"
            "</target>\n"
            "</local>\n"
            "<remote>\n"
            "<identity>[email protected]</identity>\n"
            "</remote>\n"
            "</dialog>\n"
            "</dialog-info>\n"
            ;
      SipDialogEvent dialogEvent(dialogEventString);
      bool bFullContentChanged = false;
      bool bPartialContentChanged = pApp->updateState(&dialogEvent, bFullContentChanged);
      CPPUNIT_ASSERT(bPartialContentChanged);
      CPPUNIT_ASSERT(bFullContentChanged);
      pApp->dumpState();
      CPPUNIT_ASSERT(pApp->appearanceIsBusy());
      CPPUNIT_ASSERT(pApp->appearanceIdIsSeized("0"));
      CPPUNIT_ASSERT(!pApp->appearanceIdIsSeized("1"));

      // simulate user putting the call on hold
      const char* dialogEventString2 =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"0\" state=\"partial\" entity=\"sip:[email protected]:54140\">\n"
            "<dialog id=\"1\" call-id=\"[email protected]\" local-tag=\"264460498\" remote-tag=\"1c10982\" direction=\"recipient\">\n"
            "<state>confirmed</state>\n"
            "<local>\n"
            "<identity>[email protected]:5120</identity>\n"
            "<target uri=\"sip:[email protected]:5120\">\n"
            "<param pname=\"x-line-id\" pval=\"0\"/>\n"
            "<param pname=\"+sip.rendering\" pval=\"no\"/>\n"
            "</target>\n"
            "</local>\n"
            "<remote>\n"
            "<identity>[email protected]</identity>\n"
            "</remote>\n"
            "</dialog>\n"
            "</dialog-info>\n"
            ;
      SipDialogEvent dialogEvent2(dialogEventString2);
      bPartialContentChanged = pApp->updateState(&dialogEvent2, bFullContentChanged);
      CPPUNIT_ASSERT(bPartialContentChanged);
      CPPUNIT_ASSERT(bFullContentChanged);
      pApp->dumpState();
      CPPUNIT_ASSERT(!pApp->appearanceIsBusy());
      CPPUNIT_ASSERT(pApp->appearanceIdIsSeized("0"));
      CPPUNIT_ASSERT(!pApp->appearanceIdIsSeized("1"));

      // test MESSAGE debug handling
      const char* message =
         "MESSAGE sip:[email protected]:54140 SIP/2.0\r\n"
         "From: <sip:[email protected]>;tag=17211757-9E4FBD78\r\n"
         "To: <sip:[email protected]:54140>\r\n"
         "CSeq: 1 MESSAGE\r\n"
         "Call-ID: 51405734-b9be4835-dcd9d196\r\n"
         "Contact: <sip:[email protected]>\r\n"
         "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, INFO, MESSAGE, SUBSCRIBE, NOTIFY, PRACK, UPDATE, REFER\r\n"
         "Event: dialog\r\n"
         "User-Agent: UnitTest\r\n"
         "Accept-Language: en\r\n"
         "Accept: application/dialog-info+xml\r\n"
         "Max-Forwards: 70\r\n"
         "Expires: 3600\r\n"
         "Content-Length: 0\r\n"
         "\r\n";

      // send the MESSAGE
      SipMessage messageRequest( message, strlen( message ) );
      CPPUNIT_ASSERT( sendToAppearanceAgentUnderTest( messageRequest ) );

      // receive the 200 OK response
      SipMessage response;
      CPPUNIT_ASSERT( getNextMessageFromAppearanceAgentUnderTest( response, 5 ) );
      CPPUNIT_ASSERT( response.isResponse() );
      CPPUNIT_ASSERT( response.getResponseStatusCode() == SIP_OK_CODE );
   }