Esempio n. 1
0
BOOL SERVER::SetMonitor (BOOL fShouldMonitor, ULONG *pStatus)
{
   BOOL rc = TRUE;
   ULONG status = 0;

   if (m_fMonitor != fShouldMonitor)
      {
      LPCELL lpCell;
      if ((lpCell = m_lpiCell->OpenCell (&status)) == NULL)
         rc = FALSE;
      else
         {
         NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier());

         if ((m_fMonitor = fShouldMonitor) == FALSE)
            {
            FreeAll();
            (lpCell->m_nServersUnmonitored)++;
            }
         else // (fMonitor == TRUE)
            {
            (lpCell->m_nServersUnmonitored)--;
            Invalidate();
            rc = RefreshAll (&status);
            }

         NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), m_lastStatus);
         lpCell->Close();
         }
      }

   if (!rc && pStatus)
      *pStatus = status;
   return rc;
}
Esempio n. 2
0
PVOID SERVER::OpenVosObject (PVOID *phCell, ULONG *pStatus)
{
   if (!m_hCellVOS)
      {
      LPCELL lpCell;
      if ((lpCell = m_lpiCell->OpenCell (pStatus)) != NULL)
         {
         m_hCellVOS = lpCell->GetCellObject (pStatus);
         lpCell->Close();
         }
      }

   if (m_hCellVOS && !m_hVOS)
      {
      TCHAR szCell[ cchNAME ];
      m_lpiCell->GetCellName (szCell);

      WORKERPACKET wp;
      wp.wpVosServerOpen.hCell = m_hCellVOS;
      wp.wpVosServerOpen.pszServer = m_szName;
      if (Worker_DoTask (wtaskVosServerOpen, &wp, pStatus))
         m_hVOS = wp.wpVosServerOpen.hServer;
      }

   if (m_hVOS)
      {
      if (phCell)
         *phCell = m_hCellVOS;
      ++m_cReqVOS;
      }
   return m_hVOS;
}
DWORD WINAPI AfsAdmSvr_AutoOpen_ThreadProc (PVOID lp)
{
   DWORD dwScope = PtrToUlong(lp);
   ULONG status;

   if (!l.fOperational)
      return 0;

   // First we'll have to find out which cell to open
   //
   TCHAR szCell[ cchNAME ];
   if (!CELL::GetDefaultCell (szCell, &status))
      {
      Print (dlERROR, TEXT("CELL::GetDefaultCell failed; error 0x%08lX"), status);
      }
   else
      {
      // Then try to actually open the cell
      //
      Print (dlSTANDARD, TEXT("Auto-opening cell %s; scope=%s"), szCell, (dwScope == (AFSADMSVR_SCOPE_VOLUMES | AFSADMSVR_SCOPE_USERS)) ? TEXT("full") : (dwScope == AFSADMSVR_SCOPE_VOLUMES) ? TEXT("volumes") : TEXT("users"));

      LPIDENT lpiCell;
      if ((lpiCell = CELL::OpenCell ((LPTSTR)szCell, &status)) == NULL)
         {
         Print (dlERROR, TEXT("Auto-open of cell %s failed; error 0x%08lX"), szCell, status);
         }
      else
         {
         LPCELL lpCell;
         if ((lpCell = lpiCell->OpenCell (&status)) == NULL)
            {
            Print (dlERROR, TEXT("Auto-open: OpenCell failed; error 0x%08lX"), status);
            }
         else
            {
            AfsAdmSvr_AddToMinScope (dwScope);
            if (!lpCell->RefreshAll (&status))
               Print (dlERROR, TEXT("Auto-open: RefreshCell failed; error 0x%08lX"), status);
            else
               Print (dlSTANDARD, TEXT("Auto-open of cell %s successful"), szCell);
            lpCell->Close();

            // We intentionally do not call CELL::CloseCell() here--as would
            // ordinarily be necessary to balance our CELL::OpenCell() call
            // above--because we never want to close our cache for this cell.
            // The point of calling AutoOpen() up front is to keep an admin
            // server alive and ready for use on a particular cell--calling
            // CELL::CloseCell() here negates that purpose.

            }
         }
      }

   return 0;
}
Esempio n. 4
0
BOOL SERVER::RefreshStatus (BOOL fNotify, ULONG *pStatus)
{
   BOOL rc = TRUE;
   DWORD status = 0;

   if (m_fStatusOutOfDate)
      {
      m_fStatusOutOfDate = FALSE;

      if (fNotify)
         NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier());

      LPCELL lpCell;
      if ((lpCell = OpenCell (&status)) == NULL)
         rc = FALSE;
      else
         {
         PVOID hCell;
         if ((hCell = lpCell->GetCellObject (&status)) == NULL)
            rc = FALSE;
         else
            {
            WORKERPACKET wp;
            wp.wpClientAFSServerGet.hCell = hCell;
            wp.wpClientAFSServerGet.pszServer = m_szName;

            if (!Worker_DoTask (wtaskClientAFSServerGet, &wp, &status))
               rc = FALSE;
            else
               {
               m_ss.nAddresses = 0;

               for (size_t iAddr = 0; iAddr < AFS_MAX_SERVER_ADDRESS; ++iAddr)
                  {
                  if (wp.wpClientAFSServerGet.Entry.serverAddress[ iAddr ] == 0)
                     continue;
                  AfsClass_IntToAddress (&m_ss.aAddresses[ m_ss.nAddresses++ ], wp.wpClientAFSServerGet.Entry.serverAddress[ iAddr ]);
                  }

               lpCell->m_lServers->Update (this); // That update affected a hashlistkey
               }
            }
         lpCell->Close();
         }

      if (fNotify)
         NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), ((rc) ? 0 : status));
      }

   if (pStatus && !rc)
      *pStatus = status;
   return TRUE;
}
Esempio n. 5
0
SERVER::~SERVER (void)
{
   if (!m_fMonitor)
      {
      LPCELL lpCell;
      if ((lpCell = m_lpiCell->OpenCell()) != NULL)
         {
         (lpCell->m_nServersUnmonitored)--;
         lpCell->Close();
         }
      }

   if (m_lpiThis)
      m_lpiThis->m_cRef --;

   FreeAll();
   Delete (m_lAggregates);
   Delete (m_lServices);
}
Esempio n. 6
0
      // AfsAdmSvr_PushCredentials
      // ...requests that the specified AFS credentials be used hereafter
      //    when manipulating the specified cell. You should follow this
      //    call with a Refresh request if necessary.
      //
extern "C" int AfsAdmSvr_PushCredentials (UINT_PTR idClient, UINT_PTR hCreds, ASID idCell, ULONG *pStatus)
{
   ULONG status;
   size_t iOp = AfsAdmSvr_BeginOperation (idClient);

   Print (dlDETAIL, TEXT("Client 0x%08lX: PushCredentials (hCreds=0x%08lX, idCell=0x%08lX)"), idClient, hCreds, idCell);

   if (!AfsAdmSvr_fIsValidClient (idClient))
      return FALSE_(ERROR_INVALID_PARAMETER,pStatus,iOp);

   if (GetAsidType (idCell) != itCELL)
      return FALSE_(ERROR_INVALID_PARAMETER,pStatus,iOp);

   LPCELL lpCell;
   if ((lpCell = ((LPIDENT)idCell)->OpenCell (&status)) == NULL)
      return FALSE_(ERROR_INVALID_PARAMETER,pStatus,iOp);

   lpCell->SetCurrentCredentials ((PVOID)hCreds);
   lpCell->Close();

   AfsAdmSvr_EndOperation (iOp);
   return TRUE;
}
Esempio n. 7
0
DWORD WINAPI Alert_ScoutProc (LPVOID lp)
{
   // We'll keep working forever...
   //
   for (;;)
      {
      AfsClass_Enter();

      LPCELL lpCell = (g.lpiCell == NULL) ? NULL : g.lpiCell->OpenCell();
      if (lpCell != NULL)
         {
         // See if our credentials have expired
         //
         CheckForExpiredCredentials();

         // See if any new servers have arrived, or old servers disappeared.
         //
         lpCell->RefreshServerList();

         // Check all the out-of-date servers we can find.
         //
         HENUM hEnum;
         for (LPSERVER lpServer = lpCell->ServerFindFirst (&hEnum); lpServer; lpServer = lpCell->ServerFindNext (&hEnum))
            {
            LPIDENT lpiServer = lpServer->GetIdentifier();
            LPOBJECTALERTS lpoa;

            if ( ((lpoa = Alert_GetObjectAlerts (lpiServer)) != NULL) &&
                 (lpoa->dwTickNextTest <= GetTickCount()) )
               {

               // Okay!  We've found a server that needs to be tested for
               // alert conditions.  Do that now, and when we're done, set
               // its next query-time to some distance in the future.
               //
               if (lpoa->dwTickNextRefresh == 0)
                  {
                  if (lpoa->cTickRefresh != 0)
                     lpoa->dwTickNextRefresh = lpoa->cTickRefresh + GetTickCount();
                  }
               else if (lpoa->dwTickNextRefresh <= GetTickCount())
                  {
                  (void)lpServer->Invalidate();
                  (void)lpServer->RefreshAll();
                  lpoa->dwTickNextRefresh = lpoa->cTickRefresh + GetTickCount();
                  }

               (void)Alert_Scout_CheckServer (lpServer);
               }

            lpServer->Close();
            }

         lpCell->Close();
         }

      AfsClass_Leave();

      // Now that we have completed a pass over the servers in this cell,
      // and now that we're not holding any critical sections on which
      // other threads would otherwise block, go to sleep for a while.
      //
      WaitForSingleObjectEx (heScoutWakeup, 45L * cmsec1SECOND, FALSE);
      }

   return 0;
}
Esempio n. 8
0
BOOL SERVER::CanTalkToServer (ULONG *pStatus)
{
   // Ensure the server exists in the cell at all--
   // this call just updates the server's IP addresses
   // etc (information it gets from the database servers)
   // and doesn't require talking to the server itself.
   //
   if (!RefreshStatus (FALSE, pStatus))
      return FALSE;

   // Find a new refsec array element to use...
   //
   AfsClass_InitRefreshSections();
   EnterCriticalSection (pcsRefSec);

   int idSection;
   for (idSection = 0; idSection < (int)cRefSec; ++idSection)
      {
      if (!aRefSec[ idSection ].fInUse)
         break;
      }
   if (idSection == (int)cRefSec)
      {
      if (!REALLOC (aRefSec, cRefSec, 1+idSection, 4))
         {
         if (pStatus)
            *pStatus = GetLastError();
         LeaveCriticalSection (pcsRefSec);
         return FALSE;
         }
      }
   aRefSec[ idSection ].fInUse = TRUE;
   aRefSec[ idSection ].fCanceled = FALSE;
   aRefSec[ idSection ].lpServer = this;
   aRefSec[ idSection ].hCell = NULL;

   LPCELL lpCell;
   if ((lpCell = OpenCell()) != NULL)
      {
      aRefSec[ idSection ].hCell = lpCell->GetCellObject();
      lpCell->Close();
      }

   LeaveCriticalSection (pcsRefSec);

   // Until we find out differently, assume that we won't be
   // able to query VOS or BOS on this server.
   //
   m_fCanGetAggregates = FALSE;
   m_fCanGetServices = FALSE;
   m_lastStatus = 0;

   // Fork a separate thread, on which to quickly try to talk
   // to the server.
   //
   DWORD dwThreadID;
   HANDLE hThread;
   if ((hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)CanTalkToServer_ThreadProc, IntToPtr(idSection), 0, &dwThreadID)) == NULL)
      {
      EnterCriticalSection (pcsRefSec);
      aRefSec[ idSection ].fInUse = FALSE;
      LeaveCriticalSection (pcsRefSec);
      if (pStatus)
         *pStatus = GetLastError();
      return FALSE;
      }
   SetThreadPriority (hThread, THREAD_PRIORITY_BELOW_NORMAL);

   // Wait for that thread to terminate, or for our
   // newly-allocated RefSec entry to be marked Canceled.
   //
   DWORD dw;
   for (dw = STILL_ACTIVE; dw == STILL_ACTIVE; )
      {
      EnterCriticalSection (pcsRefSec);

      GetExitCodeThread (hThread, &dw);
      if (dw == STILL_ACTIVE)
         {
         if ( (aRefSec[ idSection ].fInUse) &&
              (aRefSec[ idSection ].lpServer == this) &&
              (aRefSec[ idSection ].fCanceled) )
            {
            if (m_lastStatus == 0)
               m_lastStatus = ERROR_CANCELLED;
            dw = 0;
            }
         }

      LeaveCriticalSection (pcsRefSec);

      if (dw == STILL_ACTIVE)
         Sleep(100);	// wait another brief instant
      }

   // dw == 0 : user canceled operation (thread is still running!)
   // dw == 1 : thread completed successfully, and set fCanTalkTo* flags.
   //
   // Note that the thread will clear aRefSec[idSection].fInUse when it
   // terminates (so, if dw!=-1, it has already done so).
   //
   if (pStatus)
      *pStatus = m_lastStatus;
   return (dw == 0) ? FALSE : TRUE;
}
Esempio n. 9
0
BOOL SERVER::RefreshAll (ULONG *pStatus, double dInit, double dFactor)
{
   BOOL rc = TRUE;
   ULONG status = 0;

   if (m_fAggregatesOutOfDate || m_fServicesOutOfDate)
      {
      if ((++cRefreshAllReq) == 1)
         {
         NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllBegin, GetIdentifier(), 0);
         }

      double perAGGREGATES = 65.0; // % of time spent finding aggs & sets
      double perSERVICES   = 25.0; // % of time spent finding services
      double perVLDB       = 10.0; // % of time spent finding VLDB info

      if (cRefreshAllReq >= 2) // being called as part of a cell-wide op?
         {
         perAGGREGATES = 80.0; // % of time spent finding aggs & sets
         perSERVICES   = 20.0; // % of time spent finding services
         perVLDB       = 0.0;  // we won't query VLDB stuff ourself.
         }

      NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier());

      if (!CanTalkToServer (&status))  // Determines fCanGetAggregates, fCanGetServices
         {
         if (m_fMonitor)
            SetMonitor (FALSE);
         rc = FALSE;
         }
      else
         {
         if (!m_fCanGetAggregates)
            {
            FreeAggregates();
            m_fAggregatesOutOfDate = FALSE;
            }
         else
            {
            size_t nAggregates = 0;
            size_t iAggregate = 0;
            HENUM hEnum;
            LPAGGREGATE lpAggregate;
            for (lpAggregate = AggregateFindFirst (&hEnum); lpAggregate; lpAggregate = AggregateFindNext (&hEnum))
               {
               ++nAggregates;
               lpAggregate->Close();
               }

            if (nAggregates)
               {
               for (lpAggregate = AggregateFindFirst (&hEnum); lpAggregate; lpAggregate = AggregateFindNext (&hEnum))
                  {
                  ULONG perComplete = (ULONG)( ((double)perAGGREGATES / 100.0) * ((double)iAggregate * 100.0 / nAggregates) );
                  NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllUpdate, lpAggregate->GetIdentifier(), NULL, NULL, NULL, (ULONG)( 100.0 * dInit + dFactor * (double)perComplete ), 0);

                  lpAggregate->RefreshFilesets (TRUE);
                  lpAggregate->Close();

                  ++iAggregate;
                  }
               }
            }

         if (!m_fCanGetServices)
            {
            FreeServices();
            m_fServicesOutOfDate = FALSE;
            }
         else
            {
            size_t nServices = 0;
            size_t iService = 0;
            HENUM hEnum;
            LPSERVICE lpService;
            for (lpService = ServiceFindFirst (&hEnum); lpService; lpService = ServiceFindNext (&hEnum))
               {
               ++nServices;
               lpService->Close();
               }

            if (nServices)
               {
               for (lpService = ServiceFindFirst (&hEnum); lpService; lpService = ServiceFindNext (&hEnum))
                  {
                  ULONG perComplete = (ULONG)( (double)perAGGREGATES + ((double)perSERVICES / 100.0) * ((double)iService * 100.0 / nServices) );
                  NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllUpdate, lpService->GetIdentifier(), NULL, NULL, NULL, (ULONG)( 100.0 * dInit + dFactor * (double)perComplete ), 0);

                  lpService->RefreshStatus (TRUE);
                  lpService->Close();

                  ++iService;
                  }
               }
            }

         if (cRefreshAllReq == 1) // not being called as part of a cell-wide op?
            {
            LPCELL lpCell;
            if ((lpCell = OpenCell()) != NULL)
               {
               lpCell->RefreshVLDB (GetIdentifier(), TRUE, NULL);
               lpCell->Close();
               }
            }
         }

      NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), m_lastStatus);

      if ((--cRefreshAllReq) == 0)
         {
         NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllEnd, GetIdentifier(), NULL, NULL, NULL, 100, m_lastStatus);
         }
      }

   if (rc && m_lastStatus)
      rc = FALSE;
   if (!rc && pStatus)
      *pStatus = status;
   return rc;
}
Esempio n. 10
0
BOOL USER::RefreshStatus (BOOL fNotify, ULONG *pStatus)
{
   BOOL rc = TRUE;
   DWORD status = 0;
   DWORD kasStatus = 0;
   DWORD ptsStatus = 0;

   if (m_fStatusOutOfDate)
      {
      m_fStatusOutOfDate = FALSE;

      if (fNotify)
         NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier());

      memset (&m_us, 0x00, sizeof(m_us));

      FreeString (m_mszOwnerOf);
      m_mszOwnerOf = NULL;

      FreeString (m_mszMemberOf);
      m_mszMemberOf = NULL;

      TCHAR szFullName[ cchNAME ];
      AfsClass_GenFullUserName (szFullName, m_szPrincipal, m_szInstance);

      LPCELL lpCell;
      if ((lpCell = OpenCell (&status)) == NULL)
         rc = FALSE;
      else
         {
         PVOID hCell;
         if ((hCell = lpCell->GetCellObject (&status)) == NULL)
            rc = FALSE;
         else
            {
            // Try to get KAS information.
            //
            WORKERPACKET wpGetKas;
            wpGetKas.wpKasPrincipalGet.hCell = hCell;
            wpGetKas.wpKasPrincipalGet.hServer = lpCell->GetKasObject (&kasStatus);
            wpGetKas.wpKasPrincipalGet.pszPrincipal = m_szPrincipal;
            wpGetKas.wpKasPrincipalGet.pszInstance = m_szInstance;

            if (Worker_DoTask (wtaskKasPrincipalGet, &wpGetKas, &kasStatus))
               {
               m_us.fHaveKasInfo = TRUE;

               TCHAR szLastModPrincipal[ cchNAME ];
               TCHAR szLastModInstance[ cchNAME ];
               CopyAnsiToString (szLastModPrincipal, wpGetKas.wpKasPrincipalGet.Data.lastModPrincipal.principal);
               CopyAnsiToString (szLastModInstance, wpGetKas.wpKasPrincipalGet.Data.lastModPrincipal.instance);

               m_us.KASINFO.fIsAdmin = (wpGetKas.wpKasPrincipalGet.Data.adminSetting == KAS_ADMIN) ? TRUE : FALSE;
               m_us.KASINFO.fCanGetTickets = (wpGetKas.wpKasPrincipalGet.Data.tgsSetting == TGS) ? TRUE : FALSE;
               m_us.KASINFO.fEncrypt = (wpGetKas.wpKasPrincipalGet.Data.encSetting == ENCRYPT) ? TRUE : FALSE;
               m_us.KASINFO.fCanChangePassword = (wpGetKas.wpKasPrincipalGet.Data.cpwSetting == CHANGE_PASSWORD) ? TRUE : FALSE;
               m_us.KASINFO.fCanReusePasswords = (wpGetKas.wpKasPrincipalGet.Data.rpwSetting == REUSE_PASSWORD) ? TRUE : FALSE;
               AfsClass_UnixTimeToSystemTime (&m_us.KASINFO.timeExpires, wpGetKas.wpKasPrincipalGet.Data.userExpiration);
               AfsClass_UnixTimeToSystemTime (&m_us.KASINFO.timeLastPwChange, wpGetKas.wpKasPrincipalGet.Data.lastChangePasswordTime);
               AfsClass_UnixTimeToSystemTime (&m_us.KASINFO.timeLastMod, wpGetKas.wpKasPrincipalGet.Data.lastModTime);
               m_us.KASINFO.lpiLastMod = IDENT::FindUser (m_lpiCell, szLastModPrincipal, szLastModInstance);
               m_us.KASINFO.csecTicketLifetime = wpGetKas.wpKasPrincipalGet.Data.maxTicketLifetime;
               m_us.KASINFO.keyVersion = wpGetKas.wpKasPrincipalGet.Data.keyVersion;
               memcpy (&m_us.KASINFO.key.key, &wpGetKas.wpKasPrincipalGet.Data.key.key, ENCRYPTIONKEY_LEN);
               m_us.KASINFO.dwKeyChecksum = wpGetKas.wpKasPrincipalGet.Data.keyCheckSum;
               m_us.KASINFO.cdayPwExpire = wpGetKas.wpKasPrincipalGet.Data.daysToPasswordExpire;
               m_us.KASINFO.cFailLogin = wpGetKas.wpKasPrincipalGet.Data.failLoginCount;
               m_us.KASINFO.csecFailLoginLock = wpGetKas.wpKasPrincipalGet.Data.lockTime;
               }

            // Try to get PTS information.
            //
            WORKERPACKET wpGetPts;
            wpGetPts.wpPtsUserGet.hCell = hCell;
            wpGetPts.wpPtsUserGet.pszUser = szFullName;
            if (Worker_DoTask (wtaskPtsUserGet, &wpGetPts, &ptsStatus))
               {
               m_us.fHavePtsInfo = TRUE;

               m_us.PTSINFO.cgroupCreationQuota = wpGetPts.wpPtsUserGet.Entry.groupCreationQuota;
               m_us.PTSINFO.cgroupMember = wpGetPts.wpPtsUserGet.Entry.groupMembershipCount;
               m_us.PTSINFO.uidName = wpGetPts.wpPtsUserGet.Entry.nameUid;
               m_us.PTSINFO.uidOwner = wpGetPts.wpPtsUserGet.Entry.ownerUid;
               m_us.PTSINFO.uidCreator = wpGetPts.wpPtsUserGet.Entry.creatorUid;

               CopyAnsiToString (m_us.PTSINFO.szOwner, wpGetPts.wpPtsUserGet.Entry.owner);
               CopyAnsiToString (m_us.PTSINFO.szCreator, wpGetPts.wpPtsUserGet.Entry.creator);

               m_us.PTSINFO.aaListStatus = USERACCESS_TO_ACCOUNTACCESS (wpGetPts.wpPtsUserGet.Entry.listStatus);
               m_us.PTSINFO.aaGroupsOwned = USERACCESS_TO_ACCOUNTACCESS (wpGetPts.wpPtsUserGet.Entry.listGroupsOwned);
               m_us.PTSINFO.aaMembership = USERACCESS_TO_ACCOUNTACCESS (wpGetPts.wpPtsUserGet.Entry.listMembership);
               }

            // Grab the list of groups to which this user belongs
            //
            WORKERPACKET wpBegin;
            wpBegin.wpPtsUserMemberListBegin.hCell = hCell;
            wpBegin.wpPtsUserMemberListBegin.pszUser = szFullName;
            if (Worker_DoTask (wtaskPtsUserMemberListBegin, &wpBegin, &status))
               {
               for (;;)
                  {
                  TCHAR szGroup[ cchNAME ];

                  WORKERPACKET wpNext;
                  wpNext.wpPtsUserMemberListNext.hEnum = wpBegin.wpPtsUserMemberListBegin.hEnum;
                  wpNext.wpPtsUserMemberListNext.pszGroup = szGroup;
                  if (!Worker_DoTask (wtaskPtsUserMemberListNext, &wpNext))
                     break;

                  FormatMultiString (&m_mszMemberOf, FALSE, TEXT("%1"), TEXT("%s"), szGroup);
                  }

               WORKERPACKET wpDone;
               wpDone.wpPtsUserMemberListDone.hEnum = wpBegin.wpPtsUserMemberListBegin.hEnum;
               Worker_DoTask (wtaskPtsUserMemberListDone, &wpDone);
               }

            // Grab the list of groups which this user owns
            //
            wpBegin.wpPtsOwnedGroupListBegin.hCell = hCell;
            wpBegin.wpPtsOwnedGroupListBegin.pszOwner = szFullName;
            if (Worker_DoTask (wtaskPtsOwnedGroupListBegin, &wpBegin, &status))
               {
               for (;;)
                  {
                  TCHAR szGroup[ cchNAME ];

                  WORKERPACKET wpNext;
                  wpNext.wpPtsOwnedGroupListNext.hEnum = wpBegin.wpPtsOwnedGroupListBegin.hEnum;
                  wpNext.wpPtsOwnedGroupListNext.pszGroup = szGroup;
                  if (!Worker_DoTask (wtaskPtsOwnedGroupListNext, &wpNext))
                     break;

                  FormatMultiString (&m_mszOwnerOf, FALSE, TEXT("%1"), TEXT("%s"), szGroup);
                  }

               WORKERPACKET wpDone;
               wpDone.wpPtsOwnedGroupListDone.hEnum = wpBegin.wpPtsOwnedGroupListBegin.hEnum;
               Worker_DoTask (wtaskPtsOwnedGroupListDone, &wpDone);
               }
            }

         lpCell->Close();
         }

      if (fNotify)
         NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), ((rc) ? 0 : status));
      }

   if (rc && (!m_us.fHaveKasInfo) && (!status) && kasStatus)
      {
      status = kasStatus;
      rc = FALSE;
      }
   if (rc && (!m_us.fHavePtsInfo) && (!status) && ptsStatus)
      {
      status = ptsStatus;
      // not fatal; rc remains TRUE
      }
   if (pStatus && !rc)
      *pStatus = status;
   return TRUE;
}