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