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