feb27bf6c3e67ba8220b6c65b14574cc0c4dfa90
[openafs.git] / src / WINNT / afsclass / c_cell.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 }
14
15 #include <WINNT/afsclass.h>
16 #include "internal.h"
17
18
19 /*
20  * DEFINITIONS ________________________________________________________________
21  *
22  */
23
24          // Each CELL object maintains a list of servers; that list has
25          // hashtables placed across the (shortened) server name for
26          // faster lookup; it also maintains a hashtable across the
27          // servers' primary IP address (the first in the list; most
28          // servers will only have one anyway). The default table size
29          // in a HASHLIST is 1000 elements--that's too large for a list
30          // of servers-in-a-cell, as it's enough to handle up to 30,000
31          // servers before the table would need to resize iteself (see
32          // the documentation in hashlist.cpp for info). Instead, we
33          // choose a more reasonable default table size.
34          //
35 #define cKEYSERVERNAME_TABLESIZE     50
36
37 #define cKEYSERVERADDR_TABLESIZE     50
38
39          // Enable the definition below to do a better job of finding
40          // user entries in PTS which have no KAS entries (for instance,
41          // machine IP accounts).
42          //
43 #define FIND_PTS_DEBRIS
44
45
46 /*
47  * VARIABLES __________________________________________________________________
48  *
49  */
50
51 LPHASHLIST CELL::x_lCells = NULL;
52
53
54 /*
55  * CONSTRUCTION _______________________________________________________________
56  *
57  */
58
59 void CELL::InitClass (void)
60 {
61    if (x_lCells == NULL)
62       {
63       x_lCells = New (HASHLIST);
64       x_lCells->SetCriticalSection (AfsClass_GetCriticalSection());
65       }
66 }
67
68
69 CELL::CELL (LPTSTR pszCellName, PVOID hCreds)
70 {
71    AfsClass_Enter();
72    InitClass();
73
74    m_hCell = 0;
75    m_hKas = 0;
76    lstrcpy (m_szName, pszCellName);
77    m_nReqs = 0;
78    m_hCreds = hCreds;
79    m_apszServers = 0;
80
81    m_fStatusOutOfDate = TRUE;
82    m_fVLDBOutOfDate = TRUE;
83    m_lpiThis = NULL;
84
85    m_fServersOutOfDate = TRUE;
86    m_nServersUnmonitored = 0;
87
88    m_lServers = New (HASHLIST);
89    m_lServers->SetCriticalSection (AfsClass_GetCriticalSection());
90    m_lkServerName = m_lServers->CreateKey ("Server Name", CELL::KeyServerName_Compare, CELL::KeyServerName_HashObject, CELL::KeyServerName_HashData, cKEYSERVERNAME_TABLESIZE);
91    m_lkServerAddr = m_lServers->CreateKey ("Server Primary Address", CELL::KeyServerAddr_Compare, CELL::KeyServerAddr_HashObject, CELL::KeyServerAddr_HashData, cKEYSERVERADDR_TABLESIZE);
92
93    m_fUsersOutOfDate = TRUE;
94    m_lUsers = New (HASHLIST);
95    m_lUsers->SetCriticalSection (AfsClass_GetCriticalSection());
96    m_lkUserName = m_lUsers->CreateKey ("User Name", CELL::KeyUserName_Compare, CELL::KeyUserName_HashObject, CELL::KeyUserName_HashData);
97    m_lGroups = New (HASHLIST);
98    m_lGroups->SetCriticalSection (AfsClass_GetCriticalSection());
99    m_lkGroupName = m_lGroups->CreateKey ("Group Name", CELL::KeyGroupName_Compare, CELL::KeyGroupName_HashObject, CELL::KeyGroupName_HashData);
100
101    AfsClass_Leave();
102 }
103
104
105 CELL::~CELL (void)
106 {
107    FreeUsers (FALSE);
108    FreeServers (FALSE);
109
110    if (m_lpiThis)
111       m_lpiThis->m_cRef --;
112    Delete (m_lServers);
113 }
114
115
116 void CELL::FreeServers (BOOL fNotify)
117 {
118    for (LPENUM pEnum = m_lServers->FindLast(); pEnum; pEnum = pEnum->FindPrevious())
119       {
120       LPSERVER lpServer = (LPSERVER)(pEnum->GetObject());
121       if (fNotify)
122          lpServer->SendDeleteNotifications();
123       m_lServers->Remove (lpServer);
124       Delete (lpServer);
125       }
126    if (m_apszServers)
127       {
128       for (size_t ii = 0; m_apszServers[ii]; ++ii)
129          FreeString (m_apszServers[ii]);
130       Free (m_apszServers);
131       m_apszServers = NULL;
132       }
133 }
134
135
136 void CELL::FreeUsers (BOOL fNotify)
137 {
138    LPENUM pEnum;
139    for (pEnum = m_lGroups->FindLast(); pEnum; pEnum = pEnum->FindPrevious())
140       {
141       LPPTSGROUP lpGroup = (LPPTSGROUP)(pEnum->GetObject());
142       if (fNotify)
143          lpGroup->SendDeleteNotifications();
144       m_lGroups->Remove (lpGroup);
145       Delete (lpGroup);
146       }
147
148    for (pEnum = m_lUsers->FindLast(); pEnum; pEnum = pEnum->FindPrevious())
149       {
150       LPUSER lpUser = (LPUSER)(pEnum->GetObject());
151       if (fNotify)
152          lpUser->SendDeleteNotifications();
153       m_lUsers->Remove (lpUser);
154       Delete (lpUser);
155       }
156 }
157
158
159 /*
160  * CELL-LIST MANAGEMENT _______________________________________________________
161  *
162  */
163
164 void CELL::Close (void)
165 {
166    AfsClass_Leave();
167 }
168
169
170 BOOL CELL::GetDefaultCell (LPTSTR pszName, ULONG *pStatus)
171 {
172    WORKERPACKET wp;
173    wp.wpClientLocalCellGet.pszCell = pszName;
174    return Worker_DoTask (wtaskClientLocalCellGet, &wp, pStatus);
175 }
176
177
178 LPIDENT CELL::OpenCell (LPTSTR pszCell, PVOID hCreds, ULONG *pStatus)
179 {
180    LPIDENT lpiCell = NULL;
181    AfsClass_Enter();
182    InitClass();
183
184    LPCELL lpCell;
185    if ((lpCell = ReopenCell (pszCell, pStatus)) != NULL)
186       {
187       lpiCell = lpCell->GetIdentifier();
188       lpCell->m_nReqs++;
189       lpCell->Close();
190       }
191    else // cell hasn't been opened before? see if we can reach the cell.
192       {
193       lpCell = New2 (CELL,(pszCell, hCreds));
194       if ((lpCell->m_hCell = lpCell->GetCellObject (pStatus)) == NULL)
195          Delete (lpCell);
196       else
197          {
198          lpiCell = lpCell->GetIdentifier();
199          lpCell->m_nReqs = 1;
200          x_lCells->Add (lpCell);
201          NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpiCell);
202          }
203       }
204
205    AfsClass_Leave();
206    return lpiCell;
207 }
208
209
210 void CELL::CloseCell (LPIDENT lpiCell)
211 {
212    LPCELL lpCell;
213    if ((lpCell = lpiCell->OpenCell()) != NULL)
214       {
215       if (lpCell->m_nReqs > 1)
216          {
217          lpCell->m_nReqs--;
218          lpCell->Close();
219          }
220       else
221          {
222          NOTIFYCALLBACK::SendNotificationToAll (evtDestroy, lpiCell);
223          lpCell->CloseKasObject();
224          lpCell->CloseCellObject();
225          lpCell->Close();
226          x_lCells->Remove (lpCell);
227          Delete (lpCell);
228          }
229       }
230 }
231
232
233 LPCELL CELL::ReopenCell (LPTSTR pszCell, ULONG *pStatus)
234 {
235    LPCELL lpCell = NULL;
236    AfsClass_Enter();
237    InitClass();
238
239    // Ordinarily we'd place a key on the cell name within the list of
240    // cells--however, the most likely case only has one cell anyway.
241    // So why expend the memory?
242    //
243    for (LPENUM pEnum = x_lCells->FindFirst(); pEnum; pEnum = pEnum->FindNext())
244       {
245       LPCELL lpCellFound = (LPCELL)( pEnum->GetObject() );
246
247       if (!lstrcmpi (lpCellFound->m_szName, pszCell))
248          {
249          lpCell = lpCellFound;
250          Delete (pEnum);
251          break;
252          }
253       }
254
255    if (lpCell == NULL)
256       {
257       AfsClass_Leave();
258       if (pStatus)
259          *pStatus = ERROR_FILE_NOT_FOUND;
260       }
261
262    // AfsClass_Leave() has been called only if no cell was opened in the search.
263    return lpCell;
264 }
265
266
267 PVOID CELL::GetCurrentCredentials (void)
268 {
269    return m_hCreds;
270 }
271
272
273 void CELL::SetCurrentCredentials (PVOID hCreds)
274 {
275    CloseCellObject();
276
277    m_hCreds = hCreds;
278
279    GetCellObject();
280 }
281
282
283 /*
284  * SERVER-LIST MANAGEMENT _____________________________________________________
285  *
286  */
287
288 LPSERVER CELL::OpenServer (LPTSTR pszName, ULONG *pStatus)
289 {
290    if (!RefreshServers (TRUE, pStatus))
291       return NULL;
292
293    LPSERVER lpServer;
294    if ((lpServer = (LPSERVER)(m_lkServerName->GetFirstObject (pszName))) != NULL)
295       AfsClass_Enter();
296
297    return lpServer;
298 }
299
300
301 LPSERVER CELL::OpenServer (LPSOCKADDR_IN pAddr, ULONG *pStatus)
302 {
303    if (!RefreshServers (TRUE, pStatus))
304       return NULL;
305
306    // We'll try to use our lookup key first--since most machines only
307    // have one IP address anyway, our hashtable should make this lookup
308    // super fast. If it misses (i.e., if the server is multi-homed and
309    // for some reason VLDB refers to it by the second address), we'll
310    // have to do a brute-force search across each server in the cell.
311    // Again, we could have a better-designed lookup table here--but
312    // since multi-homing is the exception (by a vast majority), it's not
313    // worth the extra effort and memory. This technique is fast enough.
314    //
315    LPSERVER lpServer;
316    if ((lpServer = (LPSERVER)(m_lkServerAddr->GetFirstObject (pAddr))) != NULL)
317       {
318       AfsClass_Enter(); // Aren't HashLists great? We found the server.
319       }
320    else // Try brute-force search
321       {
322       HENUM hEnum;
323       for (lpServer = ServerFindFirst (&hEnum, TRUE, pStatus); lpServer; lpServer = ServerFindNext (&hEnum))
324          {
325          SERVERSTATUS ss;
326          if (lpServer->GetStatus (&ss, TRUE, pStatus))
327             {
328             for (size_t iAddr = 0; iAddr < ss.nAddresses; ++iAddr)
329                {
330                if (!memcmp (&ss.aAddresses[ iAddr ], pAddr, sizeof(SOCKADDR_IN)))
331                   {
332                   // don't close server! we're going to return this pointer.
333                   break;
334                   }
335                }
336             }
337          lpServer->Close();
338          }
339       ServerFindClose (&hEnum);
340       }
341    return lpServer;
342 }
343
344
345 LPSERVER CELL::ServerFindFirst (HENUM *phEnum, BOOL fNotify, ULONG *pStatus)
346 {
347    return ServerFindFirst (phEnum, NULL, fNotify, pStatus);
348 }
349
350
351 LPSERVER CELL::ServerFindFirst (HENUM *phEnum, LPIDENT lpiFind, BOOL fNotify, ULONG *pStatus)
352 {
353    LPSERVER lpServer = NULL;
354
355    if (!RefreshServers (fNotify, pStatus))
356       return NULL;
357
358    if (lpiFind != NULL)
359       {
360       lpServer = lpiFind->OpenServer();
361       *phEnum = NULL;
362       }
363    else if ((*phEnum = m_lServers->FindFirst()) != NULL)
364       {
365       lpServer = (LPSERVER)( (*phEnum)->GetObject() );
366       AfsClass_Enter();
367       }
368
369    if (!lpServer && pStatus)
370       *pStatus = ERROR_FILE_NOT_FOUND;
371    return lpServer;
372 }
373
374
375 LPSERVER CELL::ServerFindNext (HENUM *phEnum)
376 {
377    LPSERVER lpServer = NULL;
378
379    if (*phEnum)
380       {
381       if ((*phEnum = (*phEnum)->FindNext()) != NULL)
382          {
383          lpServer = (LPSERVER)( (*phEnum)->GetObject() );
384          AfsClass_Enter();
385          }
386       }
387
388    return lpServer;
389 }
390
391
392 void CELL::ServerFindClose (HENUM *phEnum)
393 {
394    if (*phEnum)
395       {
396       Delete (*phEnum);
397       *phEnum = NULL;
398       }
399 }
400
401
402 BOOL CELL::RefreshServers (BOOL fNotify, ULONG *pStatus)
403 {
404    if (!m_fServersOutOfDate)
405       return TRUE;
406
407    return RefreshServerList (fNotify, pStatus);
408 }
409
410
411 BOOL CELL::RefreshServerList (BOOL fNotify, ULONG *pStatus)
412 {
413    BOOL rc = TRUE;
414    ULONG status = 0;
415    BOOL fNotified = FALSE;
416
417    if (fNotify && m_fServersOutOfDate)
418       {
419       NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServersBegin, GetIdentifier());
420       fNotified = TRUE;
421       }
422
423    BOOL fReplaceList = m_fServersOutOfDate;
424    m_fServersOutOfDate = FALSE;
425
426    // Ordinarily we'd just clear the list of servers and
427    // requery it from scratch; however, servers are an exception
428    // to that technique: occasionally we may get a request to
429    // just look for servers that have appeared or disappeared,
430    // without refreshing data for other servers. Thus the revised
431    // technique:
432    //
433    //    1- if fReplaceList, empty the list of servers.
434    //       otherwise, set each server's fDelete flag.
435    //
436    //    2- enumerate the servers in the cell: for each server,
437    //          if fReplaceList, add the server to the list
438    //          otherwise, if the server is in the list, clear its fDelete
439    //
440    //    3- if !fReplaceList, enumerate the list of servers: for each server,
441    //          if the server's fDelete flag is set, remove the server
442    //
443    for (LPENUM pEnum = m_lServers->FindFirst(); pEnum; pEnum = pEnum->FindNext())
444       {
445       LPSERVER lpServer = (LPSERVER)(pEnum->GetObject());
446
447       if (fReplaceList)
448          {
449          lpServer->SendDeleteNotifications();
450          m_lServers->Remove (lpServer);
451          Delete (lpServer);
452          }
453       else // the list of servers isn't invalidated, so just mark fDelete
454          {
455          lpServer->m_fDelete = TRUE;
456          }
457       }
458
459    // Enumerate the servers in the cell.
460    //
461    PVOID hCell;
462    if ((hCell = GetCellObject (&status)) == NULL)
463       {
464       rc = FALSE;
465       FreeUsers (TRUE);
466       FreeServers (TRUE);
467       }
468    else
469       {
470       WORKERPACKET wpBegin;
471       wpBegin.wpClientAFSServerGetBegin.hCell = hCell;
472
473       if (!Worker_DoTask (wtaskClientAFSServerGetBegin, &wpBegin, &status))
474          {
475          rc = FALSE;
476          }
477       else
478          {
479          for (;;)
480             {
481             WORKERPACKET wpNext;
482             wpNext.wpClientAFSServerGetNext.hEnum = wpBegin.wpClientAFSServerGetBegin.hEnum;
483             if (!Worker_DoTask (wtaskClientAFSServerGetNext, &wpNext, &status))
484                {
485                if (status == ADMITERATORDONE)
486                   status = 0;
487                else
488                   rc = FALSE;
489                break;
490                }
491
492             afs_serverEntry_p pEntry = &wpNext.wpClientAFSServerGetNext.Entry;
493
494             TCHAR szServer[ cchNAME ];
495             CopyAnsiToString (szServer, pEntry->serverName);
496             if (!szServer[0])
497                {
498                int addrNetwork = htonl (pEntry->serverAddress[0]);
499                lstrcpy (szServer, inet_ntoa (*(struct in_addr *)&addrNetwork));
500                }
501
502             // The server identified by {pEntry} is in the cell. Now if we're
503             // building a list of SERVER objects from scratch, we can just
504             // add it--but if we're only touching up an existing list,
505             // first make sure there isn't such an animal in there now.
506             //
507             BOOL fNotifyAboutThisServer = FALSE;
508
509             LPSERVER lpServer = NULL;
510             if (!fReplaceList)
511                {
512                if ((lpServer = (LPSERVER)(m_lkServerName->GetFirstObject (szServer))) != NULL)
513                   lpServer->m_fDelete = FALSE;
514                }
515
516             if (lpServer == NULL)
517                {
518                // Okay, it's a new server. Create a SERVER object for it and
519                // add it to the list.
520                //
521                lpServer = New2 (SERVER,(this, szServer));
522                lpServer->m_wGhost |= GHOST_HAS_SERVER_ENTRY;
523                m_lServers->Add (lpServer);
524                fNotifyAboutThisServer = TRUE;
525                }
526
527             // Update the server's IP addresses
528             //
529             lpServer->m_ss.nAddresses = 0;
530
531             for (size_t iAddr = 0; iAddr < AFS_MAX_SERVER_ADDRESS; ++iAddr)
532                {
533                if (pEntry->serverAddress[ iAddr ] == 0)
534                   continue;
535                AfsClass_IntToAddress (&lpServer->m_ss.aAddresses[ lpServer->m_ss.nAddresses++ ], pEntry->serverAddress[ iAddr ]);
536                }
537
538             m_lServers->Update (lpServer); // That update affected a hashlistkey
539
540             // Tell our clients that we've found a server
541             //
542             if (fNotify && fNotifyAboutThisServer)
543                {
544                if (!fNotified)
545                   {
546                   NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServersBegin, GetIdentifier());
547                   fNotified = TRUE;
548                   }
549                NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpServer->GetIdentifier());
550                }
551             }
552
553          WORKERPACKET wpDone;
554          wpDone.wpClientAFSServerGetDone.hEnum = wpBegin.wpClientAFSServerGetBegin.hEnum;
555          Worker_DoTask (wtaskClientAFSServerGetDone, &wpDone);
556          }
557       }
558
559    // Finally, look through our list of servers: if any have fDelete set,
560    // then we didn't find them in the cell any longer. Remove those servers.
561    //
562    if (rc)
563       {
564       for (LPENUM pEnum = m_lServers->FindFirst(); pEnum; pEnum = pEnum->FindNext())
565          {
566          LPSERVER lpServer = (LPSERVER)(pEnum->GetObject());
567          if (lpServer->m_fDelete)
568             {
569             if (fNotify && !fNotified)
570                {
571                NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServersBegin, GetIdentifier());
572                fNotified = TRUE;
573                }
574             lpServer->SendDeleteNotifications();
575             m_lServers->Remove (lpServer);
576             Delete (lpServer);
577             }
578          }
579       }
580
581    // Fix m_apszServers if we did anything to the list of servers
582    //
583    if (fNotified)
584       {
585       if (m_apszServers)
586          {
587          for (size_t ii = 0; m_apszServers[ii]; ++ii)
588             FreeString (m_apszServers[ii]);
589          Free (m_apszServers);
590          m_apszServers = NULL;
591          }
592
593       size_t cServers = 0;
594       LPENUM pEnum;
595       for (pEnum = m_lServers->FindFirst(); pEnum; pEnum = pEnum->FindNext())
596          ++cServers;
597
598       if (cServers)
599          {
600          m_apszServers = (char**)Allocate (sizeof(char*) * (1+cServers));
601          memset (m_apszServers, 0x00, sizeof(char*) * (1+cServers));
602
603          size_t iServer = 0;
604          for (pEnum = m_lServers->FindFirst(); pEnum; pEnum = pEnum->FindNext())
605             {
606             LPSERVER lpServer = (LPSERVER)(pEnum->GetObject());
607             m_apszServers[ iServer ] = AllocateAnsi (cchNAME+1);
608             CopyStringToAnsi (m_apszServers[ iServer ], lpServer->m_szName);
609             ++iServer;
610             }
611          }
612       }
613
614    if (fNotified)
615       NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServersEnd, GetIdentifier(), ((rc) ? 0 : status));
616
617    if (pStatus && !rc)
618       *pStatus = status;
619    return rc;
620 }
621
622
623 /*
624  * SERVER-LIST KEYS ___________________________________________________________
625  *
626  */
627
628 BOOL CALLBACK CELL::KeyServerName_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
629 {
630    if (!lstrcmp (((LPSERVER)pObject)->m_szName, (LPTSTR)pData))
631       return TRUE;
632
633    TCHAR szShortName[ cchNAME ];
634    SERVER::ShortenName (szShortName, ((LPSERVER)pObject)->m_szName);
635    if (!lstrcmp (szShortName, (LPTSTR)pData))
636       return TRUE;
637
638    return FALSE;
639 }
640
641 HASHVALUE CALLBACK CELL::KeyServerName_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
642 {
643    return CELL::KeyServerName_HashData (pKey, ((LPSERVER)pObject)->m_szName);
644 }
645
646 HASHVALUE CALLBACK CELL::KeyServerName_HashData (LPHASHLISTKEY pKey, PVOID pData)
647 {
648    TCHAR szShortName[ cchNAME ];
649    SERVER::ShortenName (szShortName, (LPTSTR)pData);
650    return HashString (szShortName);
651 }
652
653
654 BOOL CALLBACK CELL::KeyServerAddr_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
655 {
656    return !memcmp (&((LPSERVER)pObject)->m_ss.aAddresses[0], (LPSOCKADDR)pData, sizeof(SOCKADDR_IN));
657 }
658
659 HASHVALUE CALLBACK CELL::KeyServerAddr_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
660 {
661    return CELL::KeyServerAddr_HashData (pKey, &((LPSERVER)pObject)->m_ss.aAddresses[0]);
662 }
663
664 HASHVALUE CALLBACK CELL::KeyServerAddr_HashData (LPHASHLISTKEY pKey, PVOID pData)
665 {
666    return *(DWORD*)pData;
667 }
668
669
670 BOOL CALLBACK CELL::KeyUserName_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
671 {
672    return !lstrcmpi (((LPUSER)pObject)->m_szPrincipal, (LPTSTR)pData);
673 }
674
675 HASHVALUE CALLBACK CELL::KeyUserName_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
676 {
677    return CELL::KeyUserName_HashData (pKey, ((LPUSER)pObject)->m_szPrincipal);
678 }
679
680 HASHVALUE CALLBACK CELL::KeyUserName_HashData (LPHASHLISTKEY pKey, PVOID pData)
681 {
682    return HashString ((LPTSTR)pData);
683 }
684
685
686 BOOL CALLBACK CELL::KeyGroupName_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
687 {
688    return !lstrcmpi (((LPPTSGROUP)pObject)->m_szName, (LPTSTR)pData);
689 }
690
691 HASHVALUE CALLBACK CELL::KeyGroupName_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
692 {
693    return CELL::KeyGroupName_HashData (pKey, ((LPPTSGROUP)pObject)->m_szName);
694 }
695
696 HASHVALUE CALLBACK CELL::KeyGroupName_HashData (LPHASHLISTKEY pKey, PVOID pData)
697 {
698    return HashString ((LPTSTR)pData);
699 }
700
701
702 /*
703  * CELL OBJECT ________________________________________________________________
704  *
705  */
706
707 PVOID CELL::GetCellObject (ULONG *pStatus)
708 {
709    if (!m_hCell)
710       {
711       ULONG status;
712       NOTIFYCALLBACK::SendNotificationToAll (evtOpenCellBegin, m_szName);
713
714       WORKERPACKET wpOpen;
715       wpOpen.wpClientCellOpen.pszCell = m_szName;
716       wpOpen.wpClientCellOpen.hCreds = m_hCreds;
717
718       if (Worker_DoTask (wtaskClientCellOpen, &wpOpen, &status))
719          m_hCell = wpOpen.wpClientCellOpen.hCell;
720
721       if (pStatus)
722          *pStatus = status;
723       NOTIFYCALLBACK::SendNotificationToAll (evtOpenCellEnd, m_szName, status);
724       }
725
726    return m_hCell;
727 }
728
729
730 BOOL CELL::CloseCellObject (ULONG *pStatus)
731 {
732    BOOL rc = TRUE;
733
734    if (m_hCell != NULL)
735       {
736       WORKERPACKET wp;
737       wp.wpClientCellClose.hCell = m_hCell;
738       rc = Worker_DoTask (wtaskClientCellClose, &wp, pStatus);
739       m_hCell = NULL;
740       }
741
742    return rc;
743 }
744
745
746 PVOID CELL::GetKasObject (ULONG *pStatus)
747 {
748    // m_hKas is actually never set non-NULL;
749    // leaving it NULL indicates we will work happily with *any* server.
750    //
751    return m_hKas;
752 }
753
754
755 BOOL CELL::CloseKasObject (ULONG *pStatus)
756 {
757    BOOL rc = TRUE;
758
759    if (m_hKas != NULL)
760       {
761       WORKERPACKET wp;
762       wp.wpKasServerClose.hServer = m_hKas;
763       rc = Worker_DoTask (wtaskKasServerClose, &wp, pStatus);
764       m_hKas = NULL;
765       }
766
767    return rc;
768 }
769
770
771 /*
772  * CELL GENERAL _______________________________________________________________
773  *
774  */
775
776 LPIDENT CELL::GetIdentifier (void)
777 {
778    if (m_lpiThis == NULL)
779       {
780       if ((m_lpiThis = IDENT::FindIdent (this)) == NULL)
781          m_lpiThis = New2 (IDENT,(this));
782       m_lpiThis->m_cRef ++;
783       }
784
785    return m_lpiThis;
786 }
787
788
789 void CELL::GetName (LPTSTR pszName)
790 {
791    lstrcpy (pszName, m_szName);
792 }
793
794
795 PVOID CELL::GetUserParam (void)
796 {
797    return GetIdentifier()->GetUserParam();
798 }
799
800
801 void CELL::SetUserParam (PVOID pUserNew)
802 {
803    GetIdentifier()->SetUserParam (pUserNew);
804 }
805
806
807 BOOL CELL::fAnyServersUnmonitored (void)
808 {
809    return (m_nServersUnmonitored > 0) ? TRUE : FALSE;
810 }
811
812
813 /*
814  * CELL STATUS ________________________________________________________________
815  *
816  */
817
818 void CELL::Invalidate (void)
819 {
820    if (!m_fServersOutOfDate || !m_fStatusOutOfDate || !m_fVLDBOutOfDate || !m_fUsersOutOfDate)
821       {
822       CloseKasObject();
823       CloseCellObject();
824       m_fServersOutOfDate = TRUE;
825       m_fStatusOutOfDate = TRUE;
826       m_fVLDBOutOfDate = TRUE;
827       m_fUsersOutOfDate = TRUE;
828       NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier());
829       }
830 }
831
832
833 void CELL::InvalidateServers (void)
834 {
835    if (!m_fServersOutOfDate || !m_fVLDBOutOfDate)
836       {
837       CloseKasObject();
838       CloseCellObject();
839       m_fServersOutOfDate = TRUE;
840       m_fVLDBOutOfDate = TRUE;
841       NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier());
842       }
843 }
844
845
846 void CELL::InvalidateUsers (void)
847 {
848    if (!m_fUsersOutOfDate)
849       {
850       m_fUsersOutOfDate = TRUE;
851       NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier());
852       }
853 }
854
855
856 BOOL CELL::RefreshStatus (BOOL fNotify, ULONG *pStatus)
857 {
858    BOOL rc = TRUE;
859    ULONG status = 0;
860
861    if (m_fStatusOutOfDate)
862       {
863       if (fNotify)
864          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier());
865
866       // Hmmm...well, actually, there's nothing for us to do here.  I'm
867       // leaving this around, because the refreshed-cell-status notification
868       // may be useful as an appropriate hooking point.
869       rc = TRUE;
870       status = 0;
871
872       if (fNotify)
873          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), ((rc) ? 0 : status));
874       }
875
876    if (pStatus && !rc)
877       *pStatus = status;
878    return rc;
879 }
880
881
882 BOOL CELL::RefreshVLDB (BOOL fNotify, ULONG *pStatus)
883 {
884    BOOL rc = TRUE;
885
886    if (m_fVLDBOutOfDate)
887       {
888       if ((rc = RefreshVLDB (GetIdentifier(), fNotify, pStatus)) == TRUE)
889          {
890          m_fVLDBOutOfDate = FALSE;
891          }
892       }
893
894    return rc;
895 }
896
897
898 BOOL CELL::RefreshVLDB (LPIDENT lpiRef, BOOL fNotify, ULONG *pStatus, BOOL fAnythingRelatedToThisRWFileset)
899 {
900    BOOL rc = TRUE;
901    DWORD status = 0;
902
903    // What is the scope of this refresh operation?  The entire cell,
904    // or the filesets on a particular server or aggregate?
905    //
906    LPIDENT lpiRefCell = (lpiRef == NULL) ? GetIdentifier() : lpiRef->GetCell();
907    LPIDENT lpiRefServer = NULL;
908    LPIDENT lpiRefAggregate = NULL;
909    LPIDENT lpiRefFileset = NULL;
910    VOLUMEID *pvidRefFileset = NULL;
911    VOLUMEID vidRefFileset;
912
913    if (fAnythingRelatedToThisRWFileset)
914       {
915       pvidRefFileset = &vidRefFileset;
916       lpiRef->GetFilesetID (pvidRefFileset);
917       }
918    else
919       {
920       if (lpiRef && !lpiRef->fIsCell())
921          {
922          lpiRefServer = lpiRef->GetServer();
923          }
924       if (lpiRef && (lpiRef->fIsAggregate() || lpiRef->fIsFileset()))
925          {
926          lpiRefAggregate = lpiRef->GetAggregate();
927          }
928       if (lpiRef && lpiRef->fIsFileset())
929          {
930          lpiRefFileset = lpiRef;
931          }
932       }
933
934    // If we've been told to update only one server, aggregate or
935    // fileset, find out which IP addresses correspond with that
936    // server. We'll need this for comparisons later.
937    //
938    SERVERSTATUS ssRefServer;
939    if (rc && lpiRefServer)
940       {
941       LPSERVER lpServer;
942       if ((lpServer = lpiRefServer->OpenServer (&status)) == NULL)
943          rc = FALSE;
944       else
945          {
946          rc = lpServer->GetStatus (&ssRefServer, fNotify, &status);
947          lpServer->Close();
948          }
949       }
950
951    // Likewise, if we've been told to update only one aggregate,
952    // find that aggregate's ID.  We'll need it for comparisons later.
953    //
954    AGGREGATESTATUS asRefAggregate;
955    int idPartition = NO_PARTITION;
956    if (rc && lpiRefAggregate)
957       {
958       LPAGGREGATE lpAggregate;
959       if ((lpAggregate = lpiRefAggregate->OpenAggregate (&status)) == NULL)
960          rc = FALSE;
961       else
962          {
963          idPartition = lpAggregate->GetID();
964          rc = lpAggregate->GetStatus (&asRefAggregate, fNotify, &status);
965          lpAggregate->Close();
966          }
967       }
968
969    // Zip through the current list of objects that we're about to refresh.
970    // On each such object, remove the GHOST_HAS_VLDB_ENTRY flag,
971    // and delete objects entirely if that's the only ghost flag they have.
972    // (e.g., If we went through this routine earlier and created a ghost
973    // aggregate because VLDB referenced it and we couldn't find mention
974    // of it on the server, delete that aggregate.  We'll recreate it here
975    // if necessary; otherwise, it needs to be gone.)
976    //
977    if (rc)
978       {
979       RefreshVLDB_RemoveReferences (lpiRefServer, lpiRefAggregate, lpiRefFileset, pvidRefFileset);
980       }
981
982    // We'll get a new list of filesets from VLDB, and to do that, we'll
983    // need the cell's object. If we're enumerating a specific server, we'll
984    // also need that server's object. Finally, if we're enumerating a
985    // specific aggregate, we'll also need that aggregate's name.
986    //
987    PVOID hCell = NULL;
988    PVOID hServer = NULL;
989
990    if (rc)
991       {
992       if (!lpiRefServer)
993          {
994          if ((hCell = GetCellObject (&status)) == NULL)
995             rc = FALSE;
996          }
997       else // get cell and server handles
998          {
999          LPSERVER lpServer;
1000          if ((lpServer = lpiRefServer->OpenServer()) == NULL)
1001             rc = FALSE;
1002          else
1003             {
1004             if ((hServer = lpServer->OpenVosObject (&hCell, &status)) == NULL)
1005                rc = FALSE;
1006             lpServer->Close();
1007             }
1008          }
1009       }
1010
1011    // Go get that list of filesets, and use it to update our knowledge
1012    // of the cell. Remember that, if {pvidRefFileset}, we only want
1013    // one VLDB entry.
1014    //
1015    if (rc)
1016       {
1017       if (pvidRefFileset)
1018          {
1019          WORKERPACKET wpGet;
1020          wpGet.wpVosVLDBGet.hCell = hCell;
1021          wpGet.wpVosVLDBGet.idVolume = *pvidRefFileset;
1022
1023          if (!Worker_DoTask (wtaskVosVLDBGet, &wpGet, &status))
1024             rc = FALSE;
1025          else
1026             RefreshVLDB_OneEntry (&wpGet.wpVosVLDBGet.Data, lpiRefServer, &ssRefServer, lpiRefAggregate, &asRefAggregate, lpiRefFileset, pvidRefFileset, fNotify);
1027          }
1028       else
1029          {
1030          WORKERPACKET wpBegin;
1031          wpBegin.wpVosVLDBGetBegin.hCell = hCell;
1032          wpBegin.wpVosVLDBGetBegin.hServer = hServer;
1033          wpBegin.wpVosVLDBGetBegin.idPartition = idPartition;
1034
1035          if (!Worker_DoTask (wtaskVosVLDBGetBegin, &wpBegin, &status))
1036             rc = FALSE;
1037          else
1038             {
1039             for (;;)
1040                {
1041                WORKERPACKET wpNext;
1042                wpNext.wpVosVLDBGetNext.hEnum = wpBegin.wpVosVLDBGetBegin.hEnum;
1043                if (!Worker_DoTask (wtaskVosVLDBGetNext, &wpNext, &status))
1044                   {
1045                   if (status == ADMITERATORDONE)
1046                      status = 0;
1047                   else
1048                      rc = FALSE;
1049                   break;
1050                   }
1051
1052                RefreshVLDB_OneEntry (&wpNext.wpVosVLDBGetNext.Data, lpiRefServer, &ssRefServer, lpiRefAggregate, &asRefAggregate, lpiRefFileset, pvidRefFileset, fNotify);
1053                }
1054
1055             WORKERPACKET wpDone;
1056             wpDone.wpVosVLDBGetDone.hEnum = wpBegin.wpVosVLDBGetBegin.hEnum;
1057             Worker_DoTask (wtaskVosVLDBGetDone, &wpDone);
1058             }
1059          }
1060       }
1061
1062    // We've finished the update. If we were asked to send notifications
1063    // about our progress, do so.
1064    //
1065    if (fNotify)
1066       {
1067       LPIDENT lpiNotify = (lpiRef) ? lpiRef : GetIdentifier();
1068
1069       if (!lpiNotify->fIsFileset())
1070          {
1071          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshFilesetsBegin, lpiNotify);
1072          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshFilesetsEnd, lpiNotify, status);
1073          }
1074
1075       if (lpiNotify->fIsCell() || lpiNotify->fIsServer())
1076          {
1077          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAggregatesBegin, lpiNotify);
1078          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAggregatesEnd, lpiNotify, status);
1079          }
1080       }
1081
1082    if (hServer)
1083       {
1084       LPSERVER lpServer;
1085       if ((lpServer = lpiRefServer->OpenServer()) != NULL)
1086          {
1087          lpServer->CloseVosObject();
1088          lpServer->Close();
1089          }
1090       }
1091
1092    if (pStatus && !rc)
1093       *pStatus = status;
1094    return rc;
1095 }
1096
1097
1098 void CELL::RefreshVLDB_RemoveReferences (LPIDENT lpiRefServer, LPIDENT lpiRefAggregate, LPIDENT lpiRefFileset, LPVOLUMEID pvidRefFileset)
1099 {
1100    // Zip through the current list of objects that we're about to refresh.
1101    // On each such object, remove the GHOST_HAS_VLDB_ENTRY flag,
1102    // and delete objects entirely if that's the only ghost flag they have.
1103    // (e.g., If we went through this routine earlier and created a ghost
1104    // aggregate because VLDB referenced it and we couldn't find mention
1105    // of it on the server, delete that aggregate.  We'll recreate it here
1106    // if necessary; otherwise, it needs to be gone.)
1107    //
1108    // Note that by specifying {lpiRefServer} to start the enumeration,
1109    // we'll either enumerate only lpiRefServer, or all servers if it's NULL.
1110    //
1111    HENUM heServer;
1112    for (LPSERVER lpServer = ServerFindFirst (&heServer, lpiRefServer); lpServer; lpServer = ServerFindNext (&heServer))
1113       {
1114
1115       if (!pvidRefFileset)
1116          {
1117          // Since we're about to check VLDB for references to this server,
1118          // remove the GHOST_HAS_VLDB_ENTRY flag from its SERVER object.
1119          // If that's the only thing keeping the object around, remove the
1120          // object entirely.
1121          //
1122          if ((lpServer->m_wGhost &= (~GHOST_HAS_VLDB_ENTRY)) == 0)
1123             {
1124             lpServer->Close();
1125             lpServer->SendDeleteNotifications();
1126             m_lServers->Remove (lpServer);
1127             Delete (lpServer);
1128             continue;
1129             }
1130          }
1131
1132       // Check each of the server's aggregates, and deal with them the same
1133       // way.
1134       //
1135       HENUM heAggregate;
1136       for (LPAGGREGATE lpAggregate = lpServer->AggregateFindFirst (&heAggregate, lpiRefAggregate); lpAggregate; lpAggregate = lpServer->AggregateFindNext (&heAggregate))
1137          {
1138
1139          if (!pvidRefFileset)
1140             {
1141             // Since we're about to check VLDB for references to this aggregate,
1142             // remove the GHOST_HAS_VLDB_ENTRY flag from its AGGREGATE object.
1143             // If that's the only thing keeping the object around, remove the
1144             // object entirely.
1145             //
1146             if ((lpAggregate->m_wGhost &= (~GHOST_HAS_VLDB_ENTRY)) == 0)
1147                {
1148                lpAggregate->Close();
1149                lpAggregate->SendDeleteNotifications();
1150                lpServer->m_lAggregates->Remove (lpAggregate);
1151                Delete (lpAggregate);
1152                continue;
1153                }
1154             }
1155
1156          // Check each of the aggregate's filesets, and deal with them the same
1157          // way.
1158          //
1159          HENUM heFileset;
1160          for (LPFILESET lpFileset = lpAggregate->FilesetFindFirst (&heFileset, lpiRefFileset); lpFileset; lpFileset = lpAggregate->FilesetFindNext (&heFileset))
1161             {
1162             if ((!pvidRefFileset) || (*pvidRefFileset == lpFileset->m_fs.idReadWrite))
1163                {
1164                // Since we're about to check VLDB for references to this fileset,
1165                // remove the GHOST_HAS_VLDB_ENTRY flag from its FILESET object.
1166                // If that's the only thing keeping the object around, remove the
1167                // object entirely.
1168                //
1169                if ((lpFileset->m_wGhost &= (~GHOST_HAS_VLDB_ENTRY)) == 0)
1170                   {
1171                   lpFileset->Close();
1172                   lpFileset->SendDeleteNotifications();
1173                   lpAggregate->m_lFilesets->Remove (lpFileset);
1174                   Delete (lpFileset);
1175                   continue;
1176                   }
1177                }
1178
1179             lpFileset->Close();
1180             }
1181
1182          lpAggregate->Close();
1183          }
1184
1185       lpServer->Close();
1186       }
1187 }
1188
1189
1190 void CELL::RefreshVLDB_OneEntry (PVOID pp, LPIDENT lpiRefServer, LPSERVERSTATUS pssRefServer, LPIDENT lpiRefAggregate, LPAGGREGATESTATUS pasRefAggregate, LPIDENT lpiRefFileset, LPVOLUMEID pvidRefFileset, BOOL fNotify)
1191 {
1192    vos_vldbEntry_p pEntry = (vos_vldbEntry_p)pp;
1193
1194    // If we were asked to update all the replicas of a particular
1195    // fileset, then we set {pvidRefFileset} above to that fileset's
1196    // ID. Check this VLDB entry to see if it refers to that fileset;
1197    // if not, we're not interested in it.
1198    //
1199    if (pvidRefFileset)
1200       {
1201       if (memcmp (&pEntry->volumeId[ VOS_READ_WRITE_VOLUME ], pvidRefFileset, sizeof(VOLUMEID)))
1202          return;
1203       }
1204
1205    for (int iRepSite = 0; iRepSite < pEntry->numServers; ++iRepSite)
1206       {
1207       SOCKADDR_IN RepSiteAddr;
1208       AfsClass_IntToAddress (&RepSiteAddr, pEntry->volumeSites[ iRepSite ].serverAddress);
1209
1210       // Every fileset replication site which VLDB knows about
1211       // passes through this point, within {pEntry->volumeSites[ iRepSite ]}.
1212
1213       // Are we going to be refreshing the server/aggregate on which
1214       // this repsite lives?  If not, there's no need to process this
1215       // entry any further.
1216       //
1217       if (lpiRefServer)
1218          {
1219          BOOL fFilesetLivesOnThisServer = FALSE;
1220
1221          for (size_t iAddress = 0; !fFilesetLivesOnThisServer && (iAddress < pssRefServer->nAddresses); ++iAddress)
1222             {
1223             if (!memcmp (&pssRefServer->aAddresses[ iAddress ], &RepSiteAddr, sizeof(SOCKADDR_IN)))
1224                {
1225                if (lpiRefAggregate)
1226                   {
1227                   if (pasRefAggregate->dwID != (DWORD)(pEntry->volumeSites[ iRepSite ].serverPartition))
1228                      continue;
1229                   }
1230                fFilesetLivesOnThisServer = TRUE;
1231                }
1232             }
1233
1234          if (!fFilesetLivesOnThisServer)
1235             continue;
1236          }
1237
1238       // Do we know about the server mentioned by this replication
1239       // site?
1240       //
1241       LPSERVER lpServer;
1242       if (lpiRefServer != NULL)
1243          lpServer = lpiRefServer->OpenServer();
1244       else
1245          lpServer = OpenServer (&RepSiteAddr);
1246
1247       // If we found the server but aren't monitoring it,
1248       // forget about this fileset.
1249       //
1250       if (lpServer && !lpServer->fIsMonitored())
1251          {
1252          lpServer->Close();
1253          lpServer = NULL;
1254          continue;
1255          }
1256
1257       // If we have no record of the server mentioned by this
1258       // replication site, we have to create a SERVER entry for
1259       // it before we can proceed.  The server will appear as
1260       // a "ghost".
1261       //
1262       if (!lpServer)
1263          {
1264          if (lpiRefAggregate || pvidRefFileset)
1265             continue;
1266
1267          LPTSTR pszServer = FormatString (TEXT("%1"), TEXT("%a"), &pEntry->volumeSites[ iRepSite ].serverAddress);
1268          lpServer = New2 (SERVER,(this, pszServer));
1269          AfsClass_Enter();
1270          FreeString (pszServer);
1271
1272          lpServer->m_fStatusOutOfDate = FALSE;
1273          lpServer->m_ss.nAddresses = 1;
1274          memcpy (&lpServer->m_ss.aAddresses[0], &RepSiteAddr, sizeof(SOCKADDR_IN));
1275
1276          m_lServers->Add (lpServer);
1277
1278          if (fNotify)
1279             NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpServer->GetIdentifier());
1280          }
1281
1282       lpServer->m_wGhost |= GHOST_HAS_VLDB_ENTRY;
1283
1284       // Great--we now have a replication site for a particular
1285       // fileset known to VLDB, and a pointer to the server
1286       // on which it resides.  Does that server contain the
1287       // aggregate which VLDB expects to find?
1288       //
1289       LPAGGREGATE lpAggregate;
1290       if (lpiRefAggregate != NULL)
1291          lpAggregate = lpiRefAggregate->OpenAggregate();
1292       else
1293          lpAggregate = lpServer->OpenAggregate (pEntry->volumeSites[ iRepSite ].serverPartition);
1294
1295       // If the server has no record of the aggregate mentioned
1296       // by this replication site, we have to create an
1297       // AGGREGATE entry for it before we can proceed.  The
1298       // aggregate will appear as a "ghost".  Note that we
1299       // can't update the list of aggregates on a server if
1300       // we've been asked to update a particular fileset,
1301       // because someone clearly has a pointer to the list.
1302       //
1303       if (!lpAggregate)
1304          {
1305          if (lpiRefFileset || pvidRefFileset)
1306             {
1307             lpServer->Close();
1308             lpServer = NULL;
1309             continue;
1310             }
1311
1312          // Even if the partition doesn't exist, we can still figger out
1313          // its name given its ID--'cause there's a 1:1 mapping between
1314          // allowed IDs and allowed partition names. I guess there's
1315          // something to be said for forcing partitions to be named "vicep*"
1316          //
1317          TCHAR szPartition[ cchNAME ];
1318          WORKERPACKET wp;
1319          wp.wpVosPartitionIdToName.idPartition = pEntry->volumeSites[ iRepSite ].serverPartition;
1320          wp.wpVosPartitionIdToName.pszPartition = szPartition;
1321          if (!Worker_DoTask (wtaskVosPartitionIdToName, &wp))
1322             wsprintf (szPartition, TEXT("#%lu"), pEntry->volumeSites[ iRepSite ].serverPartition);
1323
1324          lpAggregate = New2 (AGGREGATE,(lpServer, szPartition, TEXT("")));
1325          AfsClass_Enter();
1326
1327          lpAggregate->m_fStatusOutOfDate = FALSE;
1328          lpAggregate->m_as.dwID = pEntry->volumeSites[ iRepSite ].serverPartition;
1329          lpAggregate->m_as.ckStorageTotal = 0;
1330          lpAggregate->m_as.ckStorageFree = 0;
1331
1332          lpServer->m_lAggregates->Add (lpAggregate);
1333
1334          if (fNotify)
1335             NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpAggregate->GetIdentifier());
1336          }
1337
1338       lpAggregate->m_wGhost |= GHOST_HAS_VLDB_ENTRY;
1339
1340       // Great--we now have a replication site for a particular
1341       // fileset known to VLDB, and a pointer to the server
1342       // and aggregate on which it resides.  Does that aggregate
1343       // contain the fileset which VLDB expects to find?
1344       //
1345       // Remember that each iRepSite can represent up to three
1346       // filesets on that aggregate--a RW, a RO, and a BAK.
1347       //
1348       for (size_t iType = 0; iType < 3; ++iType)
1349          {
1350
1351          // Does this repsite entry mention having this type
1352          // of fileset on this aggregate?
1353          //
1354          if ((vos_volumeType_t)iType == VOS_READ_WRITE_VOLUME)
1355             {
1356             if (!((DWORD)pEntry->volumeSites[ iRepSite ].serverFlags & (DWORD)VOS_VLDB_READ_WRITE))
1357                continue;
1358             }
1359          else if ((vos_volumeType_t)iType == VOS_READ_ONLY_VOLUME)
1360             {
1361             if (!((DWORD)pEntry->volumeSites[ iRepSite ].serverFlags & (DWORD)VOS_VLDB_READ_ONLY))
1362                continue;
1363             }
1364          else if ((vos_volumeType_t)iType == VOS_BACKUP_VOLUME)
1365             {
1366             if (!((DWORD)pEntry->status & (DWORD)VOS_VLDB_ENTRY_BACKEXISTS))
1367                continue;
1368
1369             // Only look for the backup where the R/W exists
1370             if (!((DWORD)pEntry->volumeSites[ iRepSite ].serverFlags & (DWORD)VOS_VLDB_READ_WRITE))
1371                continue;
1372             }
1373
1374          LPFILESET lpFileset = lpAggregate->OpenFileset ((LPVOLUMEID)&pEntry->volumeId[ iType ]);
1375
1376          // If the aggregate has no record of the fileset mentioned
1377          // by this VLDB entry, we have to create a FILESET entry
1378          // for it.  The fileset will appear as a "ghost".
1379          //
1380          if (!lpFileset)
1381             {
1382             TCHAR szFilesetName[ cchNAME ];
1383             CopyAnsiToString (szFilesetName, pEntry->name);
1384             if ((vos_volumeType_t)iType == VOS_READ_ONLY_VOLUME)
1385                lstrcat (szFilesetName, TEXT(".readonly"));
1386             else if ((vos_volumeType_t)iType == VOS_BACKUP_VOLUME)
1387                lstrcat (szFilesetName, TEXT(".backup"));
1388
1389             lpFileset = New2 (FILESET,(lpAggregate, &pEntry->volumeId[ iType ], szFilesetName));
1390             AfsClass_Enter();
1391
1392             lpFileset->m_fs.id = pEntry->volumeId[ iType ];
1393             lpFileset->m_fs.idReadWrite = pEntry->volumeId[ VOS_READ_WRITE_VOLUME ];
1394             lpFileset->m_fs.idReplica = pEntry->volumeId[ VOS_READ_ONLY_VOLUME ];
1395             AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeCreation, 0);
1396             AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeLastUpdate, 0);
1397             AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeLastAccess, 0);
1398             AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeLastBackup, 0);
1399             AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeCopyCreation, 0);
1400             lpFileset->m_fs.nFiles = 0;
1401             lpFileset->m_fs.ckQuota = 0;
1402             lpFileset->m_fs.ckUsed = 0;
1403             lpFileset->m_fs.Type = (iType == 0) ? ftREADWRITE : (iType == 1) ? ftREPLICA : ftCLONE;
1404             lpFileset->m_fs.State = fsNORMAL;
1405
1406             lpAggregate->m_lFilesets->Add (lpFileset);
1407
1408             if (fNotify)
1409                NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpFileset->GetIdentifier());
1410             }
1411
1412          if (fNotify)
1413             NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, lpFileset->GetIdentifier());
1414
1415          lpFileset->m_wGhost |= GHOST_HAS_VLDB_ENTRY;
1416          lpFileset->m_fStatusOutOfDate = FALSE;
1417          lpFileset->m_fs.idClone = pEntry->cloneId;
1418          lpFileset->m_fs.State &= ~fsMASK_VLDB;
1419
1420          if (pEntry->status & VOS_VLDB_ENTRY_LOCKED)
1421             lpFileset->m_fs.State |= fsLOCKED;
1422
1423          lpFileset->Close();
1424
1425          if (fNotify)
1426             NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, lpFileset->GetIdentifier(), 0);
1427          }
1428
1429       if (lpServer && lpAggregate)
1430          {
1431          lpAggregate->InvalidateAllocation();
1432          lpAggregate->Close();
1433          lpAggregate = NULL;
1434          }
1435       if (lpServer)
1436          {
1437          lpServer->Close();
1438          lpServer = NULL;
1439          }
1440       }
1441 }
1442
1443
1444 BOOL CELL::RefreshAll (ULONG *pStatus)
1445 {
1446    BOOL rc = TRUE;
1447    ULONG status = 0;
1448
1449    BOOL fNotified = FALSE;
1450
1451    if (m_fServersOutOfDate && (dwWant & AFSCLASS_WANT_VOLUMES))
1452       {
1453       if (!fNotified)
1454          {
1455          fNotified = TRUE;
1456          if ((++cRefreshAllReq) == 1)
1457             {
1458             NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllBegin, GetIdentifier());
1459             }
1460          }
1461
1462       size_t nServersToRefresh = 0;
1463
1464       HENUM hEnum;
1465       for (LPSERVER lpServer = ServerFindFirst (&hEnum); lpServer; lpServer = ServerFindNext (&hEnum))
1466          {
1467          if (lpServer->fIsMonitored())
1468             ++nServersToRefresh;
1469          lpServer->Close();
1470          }
1471
1472       if (nServersToRefresh)
1473          {
1474          size_t iServer = 0;
1475          for (LPSERVER lpServer = ServerFindFirst (&hEnum); lpServer; lpServer = ServerFindNext (&hEnum))
1476             {
1477             if (lpServer->fIsMonitored())
1478                {
1479                ULONG perComplete = (ULONG)iServer * 100L / nServersToRefresh;
1480                NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllUpdate, lpServer->GetIdentifier(), NULL, NULL, NULL, perComplete, 0);
1481
1482                // intentionally ignore errors in refreshing individual
1483                // servers--we only want to return an error code if
1484                // we couldn't refresh the *cell*.
1485                //
1486                (void)lpServer->RefreshAll (NULL, ((double)iServer / (double)nServersToRefresh), (1.0 / (double)nServersToRefresh));
1487                ++iServer;
1488                }
1489             lpServer->Close();
1490             }
1491
1492          rc = RefreshVLDB (NULL, TRUE, &status);
1493          }
1494       }
1495
1496    if (rc && m_fUsersOutOfDate && (dwWant & AFSCLASS_WANT_USERS))
1497       {
1498       if (!fNotified)
1499          {
1500          fNotified = TRUE;
1501          if ((++cRefreshAllReq) == 1)
1502             {
1503             NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllBegin, GetIdentifier(), NULL, NULL, NULL, 0, 0);
1504             }
1505          }
1506
1507       rc = RefreshUsers (TRUE, &status);
1508       }
1509
1510    if (fNotified)
1511       {
1512       if ((--cRefreshAllReq) == 0)
1513          {
1514          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllEnd, GetIdentifier(), NULL, NULL, NULL, 100, status);
1515          }
1516       }
1517
1518    if (!rc && pStatus)
1519       *pStatus = status;
1520    return rc;
1521 }
1522
1523
1524 /*
1525  * USER/GROUP-LIST MANAGEMENT _________________________________________________
1526  *
1527  */
1528
1529 LPUSER CELL::OpenUser (LPTSTR pszName, LPTSTR pszInstance, ULONG *pStatus)
1530 {
1531    ULONG status = 0;
1532
1533    // First off, do we have a USER object for this guy already?
1534    //
1535    LPUSER lpUser = NULL;
1536    for (LPENUM pEnum = m_lkUserName->FindFirst (pszName); pEnum; pEnum = pEnum->FindNext())
1537       {
1538       LPUSER lpTest = (LPUSER)( pEnum->GetObject() );
1539       if (!pszInstance || !lstrcmpi (lpTest->m_szInstance, pszInstance))
1540          {
1541          lpUser = lpTest;
1542          AfsClass_Enter();
1543          Delete (pEnum);
1544          break;
1545          }
1546       }
1547
1548    // If not, see if we can create one...
1549    //
1550    if (!lpUser)
1551       {
1552       // See if there's a KAS or PTS entry for this user.
1553       //
1554       BOOL fHasKAS = FALSE;
1555       BOOL fHasPTS = FALSE;
1556
1557       WORKERPACKET wp;
1558       wp.wpKasPrincipalGet.hCell = GetCellObject (&status);
1559       wp.wpKasPrincipalGet.hServer = GetKasObject (&status);
1560       wp.wpKasPrincipalGet.pszPrincipal = pszName;
1561       wp.wpKasPrincipalGet.pszInstance = pszInstance;
1562       if (Worker_DoTask (wtaskKasPrincipalGet, &wp, &status))
1563          fHasKAS = TRUE;
1564
1565       if (!fHasKAS)
1566          {
1567          TCHAR szFullName[ cchNAME ];
1568          AfsClass_GenFullUserName (szFullName, pszName, pszInstance);
1569
1570          WORKERPACKET wp;
1571          wp.wpPtsUserGet.hCell = GetCellObject();
1572          wp.wpPtsUserGet.pszUser = szFullName;
1573          if (Worker_DoTask (wtaskPtsUserGet, &wp, &status))
1574             fHasPTS = TRUE;
1575          }
1576       if (fHasKAS || fHasPTS)
1577          {
1578          lpUser = New2 (USER,(this, pszName, pszInstance));
1579          m_lUsers->Add (lpUser);
1580          NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpUser->GetIdentifier());
1581          AfsClass_Enter();
1582          }
1583       }
1584
1585    if (!lpUser && pStatus)
1586       *pStatus = status;
1587    return lpUser;
1588 }
1589
1590
1591 LPUSER CELL::UserFindFirst (HENUM *phEnum, BOOL fNotify, ULONG *pStatus)
1592 {
1593    return UserFindFirst (phEnum, NULL, fNotify, pStatus);
1594 }
1595
1596
1597 LPUSER CELL::UserFindFirst (HENUM *phEnum, LPIDENT lpiFind, BOOL fNotify, ULONG *pStatus)
1598 {
1599    LPUSER lpUser = NULL;
1600
1601    if (!RefreshUsers (fNotify, pStatus))
1602       return NULL;
1603
1604    if (lpiFind != NULL)
1605       {
1606       lpUser = lpiFind->OpenUser();
1607       *phEnum = NULL;
1608       }
1609    else if ((*phEnum = m_lUsers->FindFirst()) != NULL)
1610       {
1611       lpUser = (LPUSER)( (*phEnum)->GetObject() );
1612       AfsClass_Enter();
1613       }
1614
1615    if (!lpUser && pStatus)
1616       *pStatus = ERROR_FILE_NOT_FOUND;
1617    return lpUser;
1618 }
1619
1620
1621 LPUSER CELL::UserFindNext (HENUM *phEnum)
1622 {
1623    LPUSER lpUser = NULL;
1624
1625    if (*phEnum)
1626       {
1627       if ((*phEnum = (*phEnum)->FindNext()) != NULL)
1628          {
1629          lpUser = (LPUSER)( (*phEnum)->GetObject() );
1630          AfsClass_Enter();
1631          }
1632       }
1633
1634    return lpUser;
1635 }
1636
1637
1638 void CELL::UserFindClose (HENUM *phEnum)
1639 {
1640    if (*phEnum)
1641       {
1642       Delete (*phEnum);
1643       *phEnum = NULL;
1644       }
1645 }
1646
1647
1648
1649 LPPTSGROUP CELL::OpenGroup (LPTSTR pszName, ULONG *pStatus)
1650 {
1651    ULONG status;
1652
1653    // First off, do we have a USER object for this guy already?
1654    //
1655    LPPTSGROUP lpGroup;
1656    if ((lpGroup = (LPPTSGROUP)(m_lkGroupName->GetFirstObject (pszName))) != NULL)
1657       AfsClass_Enter();
1658
1659    // If not, see if we can create one...
1660    //
1661    if (!lpGroup)
1662       {
1663       // See if there's a PTS entry for this group.
1664       //
1665       WORKERPACKET wp;
1666       wp.wpPtsGroupGet.hCell = GetCellObject();
1667       wp.wpPtsGroupGet.pszGroup = pszName;
1668       if (Worker_DoTask (wtaskPtsGroupGet, &wp, &status))
1669          {
1670          lpGroup = New2 (PTSGROUP,(this, pszName));
1671          m_lGroups->Add (lpGroup);
1672          NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpGroup->GetIdentifier());
1673          AfsClass_Enter();
1674          }
1675       }
1676
1677    if (!lpGroup && pStatus)
1678       *pStatus = status;
1679    return lpGroup;
1680 }
1681
1682
1683 LPPTSGROUP CELL::GroupFindFirst (HENUM *phEnum, BOOL fNotify, ULONG *pStatus)
1684 {
1685    return GroupFindFirst (phEnum, NULL, fNotify, pStatus);
1686 }
1687
1688
1689 LPPTSGROUP CELL::GroupFindFirst (HENUM *phEnum, LPIDENT lpiFind, BOOL fNotify, ULONG *pStatus)
1690 {
1691    LPPTSGROUP lpGroup = NULL;
1692
1693    if (!RefreshUsers (fNotify, pStatus))
1694       return NULL;
1695
1696    if (lpiFind != NULL)
1697       {
1698       lpGroup = lpiFind->OpenGroup();
1699       *phEnum = NULL;
1700       }
1701    else if ((*phEnum = m_lGroups->FindFirst()) != NULL)
1702       {
1703       lpGroup = (LPPTSGROUP)( (*phEnum)->GetObject() );
1704       AfsClass_Enter();
1705       }
1706
1707    if (!lpGroup && pStatus)
1708       *pStatus = ERROR_FILE_NOT_FOUND;
1709    return lpGroup;
1710 }
1711
1712
1713 LPPTSGROUP CELL::GroupFindNext (HENUM *phEnum)
1714 {
1715    LPPTSGROUP lpGroup = NULL;
1716
1717    if (*phEnum)
1718       {
1719       if ((*phEnum = (*phEnum)->FindNext()) != NULL)
1720          {
1721          lpGroup = (LPPTSGROUP)( (*phEnum)->GetObject() );
1722          AfsClass_Enter();
1723          }
1724       }
1725
1726    return lpGroup;
1727 }
1728
1729
1730 void CELL::GroupFindClose (HENUM *phEnum)
1731 {
1732    if (*phEnum)
1733       {
1734       Delete (*phEnum);
1735       *phEnum = NULL;
1736       }
1737 }
1738
1739
1740 BOOL CELL::RefreshUsers (BOOL fNotify, ULONG *pStatus)
1741 {
1742    BOOL rc = TRUE;
1743    ULONG status = 0;
1744
1745    if (m_fUsersOutOfDate)
1746       {
1747       m_fUsersOutOfDate = FALSE;
1748
1749       if (fNotify)
1750          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshUsersBegin, GetIdentifier());
1751
1752       // First, forget everything we think we know about all users and groups;
1753       // we wouldn't be here if someone didn't think it was all out of date.
1754       //
1755       FreeUsers (TRUE);
1756
1757       // Then zip through KAS to build a list of all user entries;
1758       // we'll need hCell and hKAS for that.
1759       //
1760       WORKERPACKET wpBegin;
1761       wpBegin.wpKasPrincipalGetBegin.hCell = GetCellObject();
1762       wpBegin.wpKasPrincipalGetBegin.hServer = GetKasObject (&status);
1763
1764       if (!Worker_DoTask (wtaskKasPrincipalGetBegin, &wpBegin, &status))
1765          rc = FALSE;
1766       else
1767          {
1768          for (;;)
1769             {
1770             TCHAR szPrincipal[ cchNAME ];
1771             TCHAR szInstance[ cchNAME ];
1772
1773             WORKERPACKET wpNext;
1774             wpNext.wpKasPrincipalGetNext.hEnum = wpBegin.wpKasPrincipalGetBegin.hEnum;
1775             wpNext.wpKasPrincipalGetNext.pszPrincipal = szPrincipal;
1776             wpNext.wpKasPrincipalGetNext.pszInstance = szInstance;
1777             if (!Worker_DoTask (wtaskKasPrincipalGetNext, &wpNext, &status))
1778                {
1779                if (status == ADMITERATORDONE)
1780                   status = 0;
1781                else
1782                   rc = FALSE;
1783                break;
1784                }
1785
1786             // Okay, we got a user from kas. Create a USER object for it.
1787             //
1788             LPUSER lpUser = New2 (USER,(this, szPrincipal, szInstance));
1789             m_lUsers->Add (lpUser);
1790
1791             // That was easy, wasn't it? Now check this user's groups,
1792             // both the ones it owns and the ones to which it belongs,
1793             // so we can build a full list-o-groups.
1794             //
1795             LPTSTR mszGroups;
1796             if (lpUser->GetMemberOf (&mszGroups))
1797                {
1798                BuildGroups (mszGroups);
1799                FreeString (mszGroups);
1800                }
1801
1802             if (lpUser->GetOwnerOf (&mszGroups))
1803                {
1804                BuildGroups (mszGroups);
1805                FreeString (mszGroups);
1806                }
1807             }
1808
1809          WORKERPACKET wpDone;
1810          wpDone.wpKasPrincipalGetDone.hEnum = wpBegin.wpKasPrincipalGetBegin.hEnum;
1811          Worker_DoTask (wtaskKasPrincipalGetDone, &wpDone);
1812          }
1813
1814 #ifdef FIND_PTS_DEBRIS
1815       // Icky horrible painful part: to catch entries which exist on PTS
1816       // but not in KAS, we need to zip back through our list of groups
1817       // and check thier memberships.
1818       //
1819       for (LPENUM pe = m_lGroups->FindFirst(); pe; pe = pe->FindNext())
1820          {
1821          LPPTSGROUP lpGroup = (LPPTSGROUP)(pe->GetObject());
1822
1823          LPTSTR mszMembers;
1824          if (lpGroup->GetMembers (&mszMembers))
1825             {
1826             for (LPTSTR pszMember = mszMembers; pszMember && *pszMember; pszMember += 1+lstrlen(pszMember))
1827                {
1828                // Make sure we have a user or group account for this guy.
1829                // Remember that the member name may have both a name and
1830                // an instance.
1831                //
1832                if (m_lkGroupName->GetFirstObject (pszMember))
1833                   continue;
1834
1835                TCHAR szNameMatch[ cchNAME ];
1836                TCHAR szInstanceMatch[ cchNAME ];
1837                USER::SplitUserName (pszMember, szNameMatch, szInstanceMatch);
1838
1839                LPUSER lpFound = NULL;
1840                for (LPENUM pEnum = m_lkUserName->FindFirst (szNameMatch); pEnum; pEnum = pEnum->FindNext())
1841                   {
1842                   LPUSER lpTest = (LPUSER)( pEnum->GetObject() );
1843                   if (!lstrcmpi (lpTest->m_szInstance, szInstanceMatch))
1844                      {
1845                      lpFound = lpTest;
1846                      Delete (pEnum);
1847                      break;
1848                      }
1849                   }
1850                if (lpFound)
1851                   continue;
1852
1853                // Uh oh. Is this thing a user or a group? We're really only
1854                // interested in finding user-account debris here...
1855                //
1856                WORKERPACKET wpGet;
1857                wpGet.wpPtsUserGet.hCell = GetCellObject();
1858                wpGet.wpPtsUserGet.pszUser = pszMember;
1859                if (Worker_DoTask (wtaskPtsUserGet, &wpGet))
1860                   {
1861                   if (wpGet.wpPtsUserGet.Entry.nameUid > 0)
1862                      {
1863                      LPUSER lpUser = New2 (USER,(this, pszMember, TEXT("")));
1864                      m_lUsers->Add (lpUser);
1865                      }
1866                   }
1867                }
1868             }
1869          }
1870 #endif
1871
1872       // We've finally generated a complete list of the users and groups in
1873       // this cell. If we've been asked to, send out notifications for all
1874       // the things we found.
1875       //
1876       if (fNotify)
1877          {
1878          LPENUM pEnum;
1879          for (pEnum = m_lGroups->FindFirst(); pEnum; pEnum = pEnum->FindNext())
1880             {
1881             LPPTSGROUP lpGroup = (LPPTSGROUP)(pEnum->GetObject());
1882             NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpGroup->GetIdentifier());
1883             }
1884
1885          for (pEnum = m_lUsers->FindFirst(); pEnum; pEnum = pEnum->FindNext())
1886             {
1887             LPUSER lpUser = (LPUSER)(pEnum->GetObject());
1888             NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpUser->GetIdentifier());
1889             }
1890
1891          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshUsersEnd, GetIdentifier(), ((rc) ? 0 : status));
1892          }
1893       }
1894
1895    if (!rc && pStatus)
1896       *pStatus = status;
1897    return rc;
1898 }
1899
1900
1901 void CELL::BuildGroups (LPTSTR mszGroups)
1902 {
1903    for (LPTSTR pszGroup = mszGroups; pszGroup && *pszGroup; pszGroup += 1+lstrlen(pszGroup))
1904       {
1905       // Make sure we have this group in our list-of-groups
1906       //
1907       LPPTSGROUP lpGroup;
1908       if ((lpGroup = (LPPTSGROUP)m_lkGroupName->GetFirstObject (pszGroup)) == NULL)
1909          {
1910          lpGroup = New2 (PTSGROUP,(this, pszGroup));
1911          m_lGroups->Add (lpGroup);
1912          }
1913       }
1914 }
1915
1916
1917 BOOL CELL::RefreshAccount (LPTSTR pszAccount, LPTSTR pszInstance, OP_CELL_REFRESH_ACCOUNT Op, LPIDENT *plpi)
1918 {
1919    BOOL rc = TRUE;
1920
1921    // See if we can find this thing
1922    //
1923    LPIDENT lpi;
1924    if ((lpi = IDENT::FindUser (m_lpiThis, pszAccount, pszInstance)) != NULL)
1925       {
1926       if (lpi->m_cRef == 0)
1927          lpi = NULL;
1928       }
1929    if (!lpi)
1930       {
1931       if ((lpi = IDENT::FindGroup (m_lpiThis, pszAccount)) != NULL)
1932          if (lpi->m_cRef == 0)
1933             lpi = NULL;
1934       }
1935
1936    // If we couldn't find it, and Op is _CREATED_*, then make a new account
1937    //
1938    if ((!lpi) && (Op == CELL_REFRESH_ACCOUNT_CREATED_USER))
1939       {
1940       LPUSER lpUser = New2 (USER,(this, pszAccount, pszInstance));
1941       m_lUsers->Add (lpUser);
1942       lpi = lpUser->GetIdentifier();
1943       NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpi);
1944       }
1945    else if ((!lpi) && (Op == CELL_REFRESH_ACCOUNT_CREATED_GROUP))
1946       {
1947       LPPTSGROUP lpGroup = New2 (PTSGROUP,(this, pszAccount));
1948       m_lGroups->Add (lpGroup);
1949       lpi = lpGroup->GetIdentifier();
1950       NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpi);
1951       }
1952
1953    // If we did find it, and Op is _DELETED, then remove the account
1954    //
1955    if (lpi && (Op == CELL_REFRESH_ACCOUNT_DELETED))
1956       {
1957       if (lpi && (lpi->GetType() == itUSER))
1958          {
1959          LPUSER lpUser;
1960          if ((lpUser = lpi->OpenUser()) == NULL)
1961             rc = FALSE;
1962          else
1963             {
1964             lpUser->SendDeleteNotifications();
1965             lpUser->Close();
1966             m_lUsers->Remove (lpUser);
1967             Delete (lpUser);
1968             lpi = NULL;
1969             }
1970          }
1971       else if (lpi && (lpi->GetType() == itGROUP))
1972          {
1973          LPPTSGROUP lpGroup;
1974          if ((lpGroup = lpi->OpenGroup()) == NULL)
1975             rc = FALSE;
1976          else
1977             {
1978             lpGroup->SendDeleteNotifications();
1979             lpGroup->Close();
1980             m_lGroups->Remove (lpGroup);
1981             Delete (lpGroup);
1982             lpi = NULL;
1983             }
1984          }
1985       else
1986          {
1987          rc = FALSE;
1988          }
1989       }
1990
1991    // If we still have an ident, refresh the account's properties
1992    //
1993    if (lpi && (lpi->GetType() == itUSER))
1994       {
1995       LPUSER lpUser;
1996       if ((lpUser = lpi->OpenUser()) == NULL)
1997          rc = FALSE;
1998       else
1999          {
2000          lpUser->Invalidate();
2001          lpUser->RefreshStatus();
2002          lpUser->Close();
2003          }
2004       }
2005    else if (lpi && (lpi->GetType() == itGROUP))
2006       {
2007       LPPTSGROUP lpGroup;
2008       if ((lpGroup = lpi->OpenGroup()) == NULL)
2009          rc = FALSE;
2010       else
2011          {
2012          lpGroup->Invalidate();
2013          lpGroup->RefreshStatus();
2014          lpGroup->Close();
2015          }
2016       }
2017
2018    if (plpi)
2019       *plpi = lpi;
2020    return rc;
2021 }
2022
2023
2024 BOOL CELL::RefreshAccounts (LPTSTR mszAccounts, OP_CELL_REFRESH_ACCOUNT Op)
2025 {
2026    BOOL rc = TRUE;
2027    for (LPTSTR psz = mszAccounts; psz && *psz; psz += 1+lstrlen(psz))
2028       {
2029       if (!RefreshAccount (psz, NULL, Op))
2030          rc = FALSE;
2031       }
2032    return rc;
2033 }
2034