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