Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / WINNT / afsusrmgr / task.cpp
1 extern "C" {
2 #include <afs/param.h>
3 #include <afs/stds.h>
4 }
5
6 #include "TaAfsUsrMgr.h"
7 #include "messages.h"
8 #include "creds.h"
9 #include "action.h"
10 #include "usr_col.h"
11
12
13 /*
14  * DEFINITIONS ________________________________________________________________
15  *
16  */
17
18 #ifndef iswhite
19 #define iswhite(_ch) ( ((_ch) == TEXT(' ')) || ((_ch) == TEXT('\t')) )
20 #endif
21
22 /*
23  * PROTOTYPES _________________________________________________________________
24  *
25  */
26
27 DWORD WINAPI Task_ThreadProc (PVOID lp);
28
29 void Task_Perform (LPTASKPACKET ptp);
30
31 void Task_OpenCell (LPTASKPACKET ptp);
32 void Task_UpdCreds (LPTASKPACKET ptp);
33 void Task_UpdUsers (LPTASKPACKET ptp);
34 void Task_UpdGroups (LPTASKPACKET ptp);
35 void Task_UpdMachines (LPTASKPACKET ptp);
36 void Task_Refresh (LPTASKPACKET ptp);
37 void Task_RefreshMult (LPTASKPACKET ptp);
38 void Task_Get_Actions (LPTASKPACKET ptp);
39 void Task_Get_Random_Key (LPTASKPACKET ptp);
40 void Task_User_Change (LPTASKPACKET ptp);
41 void Task_User_Find (LPTASKPACKET ptp);
42 void Task_User_Enum (LPTASKPACKET ptp);
43 void Task_User_GroupList_Set (LPTASKPACKET ptp);
44 BOOL Task_User_GroupList_Set_Do (LPUSER_GROUPLIST_SET_PARAMS lpp, ULONG *pStatus);
45 void Task_User_CPW (LPTASKPACKET ptp);
46 void Task_User_Unlock (LPTASKPACKET ptp);
47 void Task_User_Create (LPTASKPACKET ptp);
48 void Task_User_Delete (LPTASKPACKET ptp);
49 void Task_Group_Change (LPTASKPACKET ptp);
50 void Task_Group_Search (LPTASKPACKET ptp);
51 void Task_Group_Members_Get (LPTASKPACKET ptp);
52 void Task_Group_Members_Set (LPTASKPACKET ptp);
53 BOOL Task_Group_Members_Set_Do (LPGROUP_MEMBERS_SET_PARAMS lpp, ULONG *pStatus);
54 void Task_Group_Enum (LPTASKPACKET ptp);
55 void Task_Group_Rename (LPTASKPACKET ptp);
56 void Task_Group_Owned_Get (LPTASKPACKET ptp);
57 void Task_Group_Owned_Set (LPTASKPACKET ptp);
58 BOOL Task_Group_Owned_Set_Do (LPGROUP_OWNED_SET_PARAMS lpp, ULONG *pStatus);
59 void Task_Group_Create (LPTASKPACKET ptp);
60 void Task_Group_Delete (LPTASKPACKET ptp);
61 void Task_Cell_Change (LPTASKPACKET ptp);
62 void Task_List_Translate (LPTASKPACKET ptp);
63 void Task_Object_Listen (LPTASKPACKET ptp);
64 void Task_Object_Get (LPTASKPACKET ptp);
65 void Task_Set_Refresh (LPTASKPACKET ptp);
66 void Task_Expired_Creds (LPTASKPACKET ptp);
67
68 void WeedAsidList (LPASIDLIST *ppList, BOOL fWantMachines);
69 void TranslateRegExp (LPTSTR pszTarget, LPCTSTR pszSource);
70 BOOL PerformRefresh (LPTASKPACKET ptp, ASID idScope, ULONG *pStatus);
71
72
73 /*
74  * ROUTINES ___________________________________________________________________
75  *
76  */
77
78 LPTASKPACKET CreateTaskPacket (int idTask, HWND hReply, PVOID lpUser)
79 {
80    LPTASKPACKET ptp;
81
82    if ((ptp = New (TASKPACKET)) != NULL)
83       {
84       memset (ptp, 0x00, sizeof(TASKPACKET));
85
86       ptp->idTask = idTask;
87       ptp->hReply = hReply;
88       ptp->lpUser = lpUser;
89       ptp->rc = TRUE;
90       ptp->status = 0;
91
92       if ((ptp->pReturn = New (TASKPACKETDATA)) != NULL)
93          {
94          memset (ptp->pReturn, 0x00, sizeof(TASKPACKETDATA));
95          }
96       }
97
98    return ptp;
99 }
100
101
102 void FreeTaskPacket (LPTASKPACKET ptp)
103 {
104    if (ptp)
105       {
106       if (ptp->pReturn)
107          {
108          if (TASKDATA(ptp)->pAsidList)
109             asc_AsidListFree (&TASKDATA(ptp)->pAsidList);
110          if (TASKDATA(ptp)->pActionList)
111             asc_ActionListFree (&TASKDATA(ptp)->pActionList);
112          Delete (ptp->pReturn);
113          }
114       Delete (ptp);
115       }
116 }
117
118
119 void PerformTask (LPTASKPACKET ptp)
120 {
121    switch (ptp->idTask)
122       {
123       case taskOPENCELL:
124          Task_OpenCell (ptp);
125          break;
126
127       case taskUPD_CREDS:
128          Task_UpdCreds (ptp);
129          break;
130
131       case taskUPD_USERS:
132          Task_UpdUsers (ptp);
133          break;
134
135       case taskUPD_GROUPS:
136          Task_UpdGroups (ptp);
137          break;
138
139       case taskUPD_MACHINES:
140          Task_UpdMachines (ptp);
141          break;
142
143       case taskREFRESH:
144          Task_Refresh (ptp);
145          break;
146
147       case taskREFRESHMULT:
148          Task_RefreshMult (ptp);
149          break;
150
151       case taskGET_ACTIONS:
152          Task_Get_Actions (ptp);
153          break;
154
155       case taskGET_RANDOM_KEY:
156          Task_Get_Random_Key (ptp);
157          break;
158
159       case taskUSER_CHANGE:
160          Task_User_Change (ptp);
161          break;
162
163       case taskUSER_FIND:
164          Task_User_Find (ptp);
165          break;
166
167       case taskUSER_ENUM:
168          Task_User_Enum (ptp);
169          break;
170
171       case taskUSER_GROUPLIST_SET:
172          Task_User_GroupList_Set (ptp);
173          break;
174
175       case taskUSER_CPW:
176          Task_User_CPW (ptp);
177          break;
178
179       case taskUSER_UNLOCK:
180          Task_User_Unlock (ptp);
181          break;
182
183       case taskUSER_CREATE:
184          Task_User_Create (ptp);
185          break;
186
187       case taskUSER_DELETE:
188          Task_User_Delete (ptp);
189          break;
190
191       case taskGROUP_CHANGE:
192          Task_Group_Change (ptp);
193          break;
194
195       case taskGROUP_SEARCH:
196          Task_Group_Search (ptp);
197          break;
198
199       case taskGROUP_MEMBERS_GET:
200          Task_Group_Members_Get (ptp);
201          break;
202
203       case taskGROUP_MEMBERS_SET:
204          Task_Group_Members_Set (ptp);
205          break;
206
207       case taskGROUP_ENUM:
208          Task_Group_Enum (ptp);
209          break;
210
211       case taskGROUP_RENAME:
212          Task_Group_Rename (ptp);
213          break;
214
215       case taskGROUP_OWNED_GET:
216          Task_Group_Owned_Get (ptp);
217          break;
218
219       case taskGROUP_OWNED_SET:
220          Task_Group_Owned_Set (ptp);
221          break;
222
223       case taskGROUP_CREATE:
224          Task_Group_Create (ptp);
225          break;
226
227       case taskGROUP_DELETE:
228          Task_Group_Delete (ptp);
229          break;
230
231       case taskCELL_CHANGE:
232          Task_Cell_Change (ptp);
233          break;
234
235       case taskLIST_TRANSLATE:
236          Task_List_Translate (ptp);
237          break;
238
239       case taskOBJECT_LISTEN:
240          Task_Object_Listen (ptp);
241          break;
242
243       case taskOBJECT_GET:
244          Task_Object_Get (ptp);
245          break;
246
247       case taskSET_REFRESH:
248          Task_Set_Refresh (ptp);
249          break;
250
251       case taskEXPIRED_CREDS:
252          Task_Expired_Creds (ptp);
253          break;
254
255       default:
256          ptp->rc = FALSE;
257          ptp->status = ERROR_INVALID_FUNCTION;
258          break;
259       }
260 }
261
262
263 /*
264  * KEYS _______________________________________________________________________
265  *
266  */
267
268 HASHVALUE CALLBACK Key_Asid_HashData (LPHASHLISTKEY pKey, PVOID pData)
269 {
270    return *(ASID*)pData;
271 }
272
273 HASHVALUE CALLBACK Key_Asid_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
274 {
275    return Key_Asid_HashData (pKey, (ASID*)pObject);
276 }
277
278 BOOL CALLBACK Key_Asid_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
279 {
280    return ((*(ASID*)pObject) == (*(ASID*)pData));
281 }
282
283
284 /*
285  * TASKS ______________________________________________________________________
286  *
287  */
288
289 void Task_OpenCell (LPTASKPACKET ptp)
290 {
291    LPOPENCELL_PARAMS lpp = (LPOPENCELL_PARAMS)( ptp->lpUser );
292
293    Display_StartWorking();
294
295    // Try to open the cell for administration
296    //
297    ptp->rc = asc_CellOpen (g.idClient, lpp->hCreds, lpp->szCell, AFSADMSVR_SCOPE_USERS, &TASKDATA(ptp)->idCell, &ptp->status);
298
299    if (ptp->rc)
300       {
301       PostMessage (g.hMain, WM_SHOW_YOURSELF, 0, 1);
302       }
303    else if ((!ptp->rc) && (!IsWindow(ptp->hReply)))
304       {
305       if (lpp->fCloseAppOnFail)
306          FatalErrorDialog (ptp->status, IDS_ERROR_CANT_OPEN_CELL, TEXT("%s"), lpp->szCell);
307       else
308          ErrorDialog (ptp->status, IDS_ERROR_CANT_OPEN_CELL, TEXT("%s"), lpp->szCell);
309       }
310
311    // If we previously had another cell open, close it.
312    //
313    if (ptp->rc)
314       {
315       asc_Enter();
316
317       if (g.idCell)
318          {
319          ULONG status;
320          (void)asc_CellClose (g.idClient, g.idCell, &status);
321          }
322       g.idCell = TASKDATA(ptp)->idCell;
323
324       asc_Leave();
325       }
326
327    // Update the "Selected Cell:" text on the main window
328    //
329    TCHAR szCell[ cchNAME ];
330    if (!g.idCell)
331       GetString (szCell, IDS_CELL_NONE);
332    else if (!asc_CellNameGet_Fast (g.idClient, g.idCell, szCell))
333       GetString (szCell, IDS_CELL_NONE);
334    SetDlgItemText (g.hMain, IDC_CELL, szCell);
335    ShowCurrentCredentials();
336
337    // Oh--also, set the refresh rate for the newly-opened cell
338    //
339    ULONG dummy;
340    asc_CellRefreshRateSet (g.idClient, g.idCell, gr.cminRefreshRate, &dummy);
341
342    // Start re-populating the Users or Groups tab (whichever is showing)
343    //
344    Display_PopulateList();
345
346    Display_StopWorking();
347
348    // When we've opened a new cell, it's time to open the Actions window.
349    //
350    if (gr.fShowActions)
351       PostMessage (g.hMain, WM_SHOW_ACTIONS, 0, 0);
352
353    Delete (lpp);
354    ptp->lpUser = 0; // we freed this; don't let the caller use it again
355 }
356
357
358 void Task_UpdCreds (LPTASKPACKET ptp)
359 {
360    // Update the display to report our new credentials
361    //
362    ShowCurrentCredentials();
363
364    // Tell the admin server to use our new credentials, and refresh everything
365    //
366    if (!asc_CredentialsPush (g.idClient, g.hCreds, g.idCell, &ptp->status))
367       ptp->rc = FALSE;
368    else
369       ptp->rc = PerformRefresh (ptp, g.idCell, &ptp->status);
370 }
371
372
373 void Task_UpdUsers (LPTASKPACKET ptp)
374 {
375    // First we'll query the admin server to find a list of all users which
376    // match our search pattern.
377    //
378    lstrcpy (TASKDATA(ptp)->szPattern, g.szPatternUsers);
379
380    TCHAR szRegExp[ cchNAME ];
381    TranslateRegExp (szRegExp, TASKDATA(ptp)->szPattern);
382    if (!asc_ObjectFindMultiple (g.idClient, g.idCell, TYPE_USER, szRegExp, &gr.SearchUsers, &TASKDATA(ptp)->pAsidList, &ptp->status))
383       ptp->rc = FALSE;
384
385    // If we got a result back, weed out any entries that look like machines.
386    //
387    if (ptp->rc)
388       {
389       WeedAsidList (&TASKDATA(ptp)->pAsidList, FALSE);
390       }
391
392    // Wow, that was easy. Okay, next step: ensure that we have properties
393    // for all these guys in the local cache--to do that, we'll query their
394    // properties, then free the result.
395    //
396    if (ptp->rc)
397       {
398       LPASOBJPROPLIST pPropList = NULL;
399       if (!asc_ObjectPropertiesGetMultiple (g.idClient, GET_ALL_DATA, g.idCell, TASKDATA(ptp)->pAsidList, &pPropList, &ptp->status))
400          ptp->rc = FALSE;
401       if (pPropList)
402          asc_ObjPropListFree (&pPropList);
403       }
404 }
405
406
407 void Task_UpdGroups (LPTASKPACKET ptp)
408 {
409    // First we'll query the admin server to find a list of all groups which
410    // match our search pattern.
411    //
412    lstrcpy (TASKDATA(ptp)->szPattern, g.szPatternGroups);
413
414    TCHAR szRegExp[ cchNAME ];
415    TranslateRegExp (szRegExp, TASKDATA(ptp)->szPattern);
416    if (!asc_ObjectFindMultiple (g.idClient, g.idCell, TYPE_GROUP, szRegExp, NULL, &TASKDATA(ptp)->pAsidList, &ptp->status))
417       ptp->rc = FALSE;
418
419    // Wow, that was easy. Okay, next step: ensure that we have properties
420    // for all these guys in the local cache--to do that, we'll query their
421    // properties, then free the result.
422    //
423    if (ptp->rc)
424       {
425       LPASOBJPROPLIST pPropList = NULL;
426       if (!asc_ObjectPropertiesGetMultiple (g.idClient, GET_ALL_DATA, g.idCell, TASKDATA(ptp)->pAsidList, &pPropList, &ptp->status))
427          ptp->rc = FALSE;
428       if (pPropList)
429          asc_ObjPropListFree (&pPropList);
430       }
431 }
432
433
434 void Task_UpdMachines (LPTASKPACKET ptp)
435 {
436    // First we'll query the admin server to find a list of all users which
437    // match our search pattern.
438    //
439    TCHAR szRegExp[ cchNAME ];
440    if (g.szPatternMachines[0])
441       TranslateRegExp (szRegExp, g.szPatternMachines);
442    else
443       lstrcpy (szRegExp, TEXT("^[0-9.]*$"));
444
445    if (!asc_ObjectFindMultiple (g.idClient, g.idCell, TYPE_USER, szRegExp, NULL, &TASKDATA(ptp)->pAsidList, &ptp->status))
446       ptp->rc = FALSE;
447
448    lstrcpy (TASKDATA(ptp)->szPattern, g.szPatternMachines);
449
450    // If we got a result back, weed out any entries that don't look
451    // like machines.
452    //
453    if (ptp->rc)
454       {
455       WeedAsidList (&TASKDATA(ptp)->pAsidList, TRUE);
456       }
457
458    // Wow, that was easy. Okay, next step: ensure that we have properties
459    // for all these guys in the local cache--to do that, we'll query their
460    // properties, then free the result.
461    //
462    if (ptp->rc)
463       {
464       LPASOBJPROPLIST pPropList = NULL;
465       if (!asc_ObjectPropertiesGetMultiple (g.idClient, GET_ALL_DATA, g.idCell, TASKDATA(ptp)->pAsidList, &pPropList, &ptp->status))
466          ptp->rc = FALSE;
467       if (pPropList)
468          asc_ObjPropListFree (&pPropList);
469       }
470 }
471
472
473
474 void Task_Refresh (LPTASKPACKET ptp)
475 {
476    ASID idScope = (ASID)( ptp->lpUser );
477
478    ptp->rc = PerformRefresh (ptp, idScope, &ptp->status);
479 }
480
481
482 void Task_RefreshMult (LPTASKPACKET ptp)
483 {
484    LPASIDLIST pAsidList = (LPASIDLIST)( ptp->lpUser );
485
486    // Invalidate the admin server's cached information about the specified
487    // object. Remember that this is recursive hierarchically: if you pass
488    // in a cell's ID, for instance, information about all users, groups,
489    // servers, services, partitions and volumes anywhere in that cell will
490    // be discarded.
491    //
492    ptp->rc = asc_ObjectRefreshMultiple (g.idClient, g.idCell, pAsidList, &ptp->status);
493
494    // The Refresh call above just made us invalidate the status for one or
495    // more objects; to get the display to reflect the changes, we'll have to
496    // query the server for the latest properties for those objects. Once that's
497    // done, we'll just redraw the main window and it will pick up the changes.
498    //
499    if (ptp->rc)
500       {
501       ULONG status;
502       LPASOBJPROPLIST pPropList = NULL;
503       if (asc_ObjectPropertiesGetMultiple (g.idClient, GET_ALL_DATA, g.idCell, pAsidList, &pPropList, &status))
504          {
505          // That call returned properties for the objects; we don't need
506          // the properties here--we just wanted to get them in the cache.
507          // Now that they're in the cache, redrawing the main window will
508          // cause the latest data to be displayed.
509          //
510          if (pPropList)
511             asc_ObjPropListFree (&pPropList);
512          Display_RefreshView_Fast();
513          }
514       }
515
516    asc_AsidListFree (&pAsidList);
517    ptp->lpUser = 0; // we freed this; don't let the caller use it again
518 }
519
520
521 void Task_Get_Actions (LPTASKPACKET ptp)
522 {
523    // Query the admin server to get a current list of operations-in-progress.
524    // We'll limit our search to operations being performed on this cell.
525    //
526    ptp->rc = asc_ActionGetMultiple (g.idClient, 0, g.idCell, &TASKDATA(ptp)->pActionList, &ptp->status);
527 }
528
529
530 void Task_Get_Random_Key (LPTASKPACKET ptp)
531 {
532    ptp->rc = asc_RandomKeyGet (g.idClient, g.idCell, TASKDATA(ptp)->key, &ptp->status);
533
534    if (!ptp->rc && !IsWindow(ptp->hReply))
535       {
536       ErrorDialog (ptp->status, IDS_ERROR_CANT_GET_RANDOM_KEY);
537       }
538 }
539
540
541 void Task_User_Change (LPTASKPACKET ptp)
542 {
543    LPUSER_CHANGE_PARAMS lpp = (LPUSER_CHANGE_PARAMS)( ptp->lpUser );
544
545    if ((ptp->rc = asc_UserChange (g.idClient, g.idCell, lpp->idUser, &lpp->NewProperties, &ptp->status)) == TRUE)
546       {
547       Display_RefreshView_Fast();
548       }
549
550    if (!ptp->rc && !IsWindow (ptp->hReply))
551       {
552       TCHAR szUser[ cchNAME ];
553       User_GetDisplayName (szUser, lpp->idUser);
554       ErrorDialog (ptp->status, IDS_ERROR_CANT_CHANGE_USER, TEXT("%s"), szUser);
555       }
556
557    Delete (lpp);
558    ptp->lpUser = 0; // we freed this; don't let the caller use it again
559 }
560
561
562 void Task_User_Find (LPTASKPACKET ptp)
563 {
564    LPTSTR pszName = (LPTSTR)( ptp->lpUser );
565
566    if ((ptp->rc = asc_ObjectFind (g.idClient, g.idCell, TYPE_USER, pszName, &TASKDATA(ptp)->idObject, &ptp->status)) == TRUE)
567       {
568       ptp->rc = asc_ObjectPropertiesGet (g.idClient, GET_ALL_DATA, g.idCell, TASKDATA(ptp)->idObject, &TASKDATA(ptp)->Properties, &ptp->status);
569       }
570
571    FreeString (pszName);
572    ptp->lpUser = 0; // we freed this; don't let the caller use it again
573 }
574
575
576 void Task_User_Enum (LPTASKPACKET ptp)
577 {
578    LPCTSTR pszPattern = (LPCTSTR)( ptp->lpUser );
579
580    TCHAR szRegExp[ cchNAME ];
581    TranslateRegExp (szRegExp, pszPattern);
582    if ((ptp->rc = asc_ObjectFindMultiple (g.idClient, g.idCell, TYPE_USER, szRegExp, NULL, &TASKDATA(ptp)->pAsidList, &ptp->status)) == TRUE)
583       {
584       LPASOBJPROPLIST pPropList = NULL;
585
586       ptp->rc = asc_ObjectPropertiesGetMultiple (g.idClient, GET_RUDIMENTARY_DATA, g.idCell, TASKDATA(ptp)->pAsidList, &pPropList, &ptp->status);
587
588       if (pPropList)
589          asc_ObjPropListFree (&pPropList);
590       }
591
592    FreeString (pszPattern);
593    ptp->lpUser = 0; // we freed this; don't let the caller use it again
594 }
595
596
597 void Task_User_GroupList_Set (LPTASKPACKET ptp)
598 {
599    LPUSER_GROUPLIST_SET_PARAMS lpp = (LPUSER_GROUPLIST_SET_PARAMS)( ptp->lpUser );
600
601    ptp->rc = Task_User_GroupList_Set_Do (lpp, &ptp->status);
602
603    asc_AsidListFree (&lpp->pUsers);
604    asc_AsidListFree (&lpp->pGroups);
605    Delete (lpp);
606    ptp->lpUser = 0; // we freed this; don't let the caller use it again
607 }
608
609
610 BOOL Task_User_GroupList_Set_Do (LPUSER_GROUPLIST_SET_PARAMS lpp, ULONG *pStatus)
611 {
612    BOOL rc = TRUE;
613
614    // Maintain a structure describing any errors we encounter, so we can give
615    // a reasonable error dialog if need be.
616    //
617    LPERRORDATA ped = ED_Create (IDS_ERROR_CANT_SET_GROUPS, IDS_ERROR_CANT_SET_GROUPS_MULTIPLE);
618
619    // We'll need the supplied group-list in a hashlist, so we can quickly
620    // test it for inclusion of a particular group.
621    //
622    LPHASHLIST pGroupsAllow = New (HASHLIST);
623    for (size_t iGroup = 0; iGroup < lpp->pGroups->cEntries; ++iGroup)
624       pGroupsAllow->AddUnique ((PVOID)(lpp->pGroups->aEntries[ iGroup ].idObject));
625
626    // We'll have to do this next bit for each user in the supplied user-list
627    //
628    for (size_t iUser = 0; iUser < lpp->pUsers->cEntries; ++iUser)
629       {
630       ULONG status;
631
632       // Obtain the appropriate current list of groups for this user
633       //
634       LPASIDLIST pGroupsOld = NULL;
635       if (lpp->fMembership)
636          {
637          if (!asc_GroupMembershipGet (g.idClient, g.idCell, lpp->pUsers->aEntries[ iUser ].idObject, &pGroupsOld, &status))
638             {
639             ED_RegisterStatus (ped, lpp->pUsers->aEntries[ iUser ].idObject, FALSE, status);
640             continue;
641             }
642          }
643       else // (!lpp->fMembership)
644          {
645          if (!asc_GroupOwnershipGet (g.idClient, g.idCell, lpp->pUsers->aEntries[ iUser ].idObject, &pGroupsOld, &status))
646             {
647             ED_RegisterStatus (ped, lpp->pUsers->aEntries[ iUser ].idObject, FALSE, status);
648             continue;
649             }
650          }
651       if (!pGroupsOld)
652          continue;
653
654       // Test each group in that current list to see if it's also on our
655       // pGroupsAllow list. If not, remove it.
656       //
657       for (iGroup = 0; iGroup < pGroupsOld->cEntries; ++iGroup)
658          {
659          if (pGroupsAllow->fIsInList ((PVOID)(pGroupsOld->aEntries[iGroup].idObject)))
660             continue;
661
662          if (lpp->fMembership)
663             {
664             if (!asc_GroupMemberRemove (g.idClient, g.idCell, pGroupsOld->aEntries[iGroup].idObject, lpp->pUsers->aEntries[iUser].idObject, &status))
665                ED_RegisterStatus (ped, lpp->pUsers->aEntries[ iUser ].idObject, FALSE, status);
666             }
667          else // (!lpp->fMembership)
668             {
669             ASOBJPROP Properties;
670             if (asc_ObjectPropertiesGet (g.idClient, GET_ALL_DATA, g.idCell, pGroupsOld->aEntries[iGroup].idObject, &Properties, &status))
671                {
672                AFSADMSVR_CHANGEGROUP_PARAMS pp;
673                memset (&pp, 0x00, sizeof(pp));
674                lstrcpy (pp.szOwner, Properties.szName); // make group self-owned
675                pp.aaListStatus = Properties.u.GroupProperties.aaListStatus;
676                pp.aaListGroupsOwned = Properties.u.GroupProperties.aaListGroupsOwned;
677                pp.aaListMembers = Properties.u.GroupProperties.aaListMembers;
678                pp.aaAddMember = Properties.u.GroupProperties.aaAddMember;
679                pp.aaDeleteMember = Properties.u.GroupProperties.aaDeleteMember;
680                if (!asc_GroupChange (g.idClient, g.idCell, pGroupsOld->aEntries[iGroup].idObject, &pp, &status))
681                   ED_RegisterStatus (ped, lpp->pUsers->aEntries[ iUser ].idObject, FALSE, status);
682                }
683             }
684          }
685
686       // Now the more complex part: see if there are any groups in the
687       // supplied group-list which are marked as mandatory, but which
688       // aren't in our pGroupsOld list. We'll need to put the latter in a
689       // hashlist for this...
690       //
691       LPHASHLIST pGroupsOldList = New (HASHLIST);
692       for (iGroup = 0; iGroup < pGroupsOld->cEntries; ++iGroup)
693          pGroupsOldList->AddUnique ((PVOID)(pGroupsOld->aEntries[ iGroup ].idObject));
694
695       for (iGroup = 0; iGroup < lpp->pGroups->cEntries; ++iGroup)
696          {
697          if (!lpp->pGroups->aEntries[ iGroup ].lParam)
698             continue; // group not mandatory
699          if (pGroupsOldList->fIsInList ((PVOID)(lpp->pGroups->aEntries[ iGroup ].idObject)))
700             continue; // already a member
701
702          if (lpp->fMembership)
703             {
704             if (!asc_GroupMemberAdd (g.idClient, g.idCell, lpp->pGroups->aEntries[iGroup].idObject, lpp->pUsers->aEntries[iUser].idObject, &status))
705                ED_RegisterStatus (ped, lpp->pUsers->aEntries[ iUser ].idObject, FALSE, status);
706             }
707          else // (!lpp->fMembership)
708             {
709             ASOBJPROP Properties;
710             if (asc_ObjectPropertiesGet (g.idClient, GET_ALL_DATA, g.idCell, lpp->pGroups->aEntries[iGroup].idObject, &Properties, &status))
711                {
712                AFSADMSVR_CHANGEGROUP_PARAMS pp;
713                memset (&pp, 0x00, sizeof(pp));
714                pp.aaListStatus = Properties.u.GroupProperties.aaListStatus;
715                pp.aaListGroupsOwned = Properties.u.GroupProperties.aaListGroupsOwned;
716                pp.aaListMembers = Properties.u.GroupProperties.aaListMembers;
717                pp.aaAddMember = Properties.u.GroupProperties.aaAddMember;
718                pp.aaDeleteMember = Properties.u.GroupProperties.aaDeleteMember;
719
720                if (asc_ObjectNameGet_Fast (g.idClient, g.idCell, lpp->pUsers->aEntries[iUser].idObject, pp.szOwner, &status))
721                   {
722                   if (!asc_GroupChange (g.idClient, g.idCell, lpp->pGroups->aEntries[iGroup].idObject, &pp, &status))
723                      ED_RegisterStatus (ped, lpp->pUsers->aEntries[ iUser ].idObject, FALSE, status);
724                   }
725                }
726             }
727          }
728
729       Delete (pGroupsOldList);
730       asc_AsidListFree (&pGroupsOld);
731       }
732
733    // If there were any errors, report them.
734    //
735    if ((*pStatus = ED_GetFinalStatus(ped)) != 0)
736       {
737       rc = FALSE;
738       ED_ShowErrorDialog(ped);
739       }
740    ED_Free(ped);
741
742    // Done; clean up
743    //
744    Delete (pGroupsAllow);
745    return rc;
746 }
747
748
749 void Task_User_CPW (LPTASKPACKET ptp)
750 {
751    LPUSER_CPW_PARAMS lpp = (LPUSER_CPW_PARAMS)( ptp->lpUser );
752
753    ptp->rc = asc_UserPasswordSet (g.idClient, g.idCell, lpp->idUser, lpp->keyVersion, lpp->keyString, lpp->keyData, &ptp->status);
754
755    if ((!ptp->rc) && (!IsWindow(ptp->hReply)))
756       {
757       TCHAR szName[ cchNAME ];
758       User_GetDisplayName (szName, lpp->idUser);
759       ErrorDialog (ptp->status, IDS_ERROR_CANT_CHANGE_PASSWORD, TEXT("%s"), szName);
760       }
761
762    Delete (lpp);
763    ptp->lpUser = 0; // we freed this; don't let the caller use it again
764 }
765
766
767 void Task_User_Unlock (LPTASKPACKET ptp)
768 {
769    LPASIDLIST pUserList = (LPASIDLIST)( ptp->lpUser );
770
771    // Maintain a structure describing any errors we encounter, so we can give
772    // a reasonable error dialog if need be.
773    //
774    LPERRORDATA ped = ED_Create (IDS_ERROR_CANT_UNLOCK, IDS_ERROR_CANT_UNLOCK_MULTIPLE);
775
776    // Try to unlock the users' accounts...
777    //
778    for (size_t iUser = 0; iUser < pUserList->cEntries; ++iUser)
779       {
780       if (!asc_UserUnlock (g.idClient, g.idCell, pUserList->aEntries[ iUser ].idObject, &ptp->status))
781          ED_RegisterStatus (ped, pUserList->aEntries[ iUser ].idObject, FALSE, ptp->status);
782       }
783
784    // If there were any errors, report them.
785    //
786    if (ED_GetFinalStatus(ped) && !IsWindow(ptp->hReply))
787       ED_ShowErrorDialog(ped);
788    ED_Free(ped);
789
790    asc_AsidListFree (&pUserList);
791    ptp->lpUser = 0; // we freed this; don't let the caller use it again
792 }
793
794
795 void Task_User_Create (LPTASKPACKET ptp)
796 {
797    LPUSER_CREATE_PARAMS lpp = (LPUSER_CREATE_PARAMS)(ptp->lpUser);
798
799    // We may actually have been asked to create more than one user here;
800    // the {lpp->mszNames} parameter is a multi-string. So everything we
801    // do, we'll do for each new user-name...
802    //
803    for (LPTSTR pszName = lpp->mszNames; pszName && *pszName; pszName += 1+lstrlen(pszName))
804       {
805       // First create this new user account
806       //
807       ASID idUser;
808
809       AFSADMSVR_CREATEUSER_PARAMS pp;
810       memset (&pp, 0x00, sizeof(AFSADMSVR_CREATEUSER_PARAMS));
811       User_SplitDisplayName (pszName, pp.szName, pp.szInstance);
812       lstrcpy (pp.szPassword, lpp->szPassword);
813       pp.idUser = lpp->idUser;
814       pp.fCreateKAS = lpp->fCreateKAS;
815       pp.fCreatePTS = lpp->fCreatePTS;
816       if ((ptp->rc = asc_UserCreate (g.idClient, g.idCell, &pp, &idUser, &ptp->status)) == FALSE)
817          {
818          if (!IsWindow (ptp->hReply))
819             ErrorDialog (ptp->status, IDS_ERROR_CANT_CREATE_USER, TEXT("%s"), pszName);
820          continue;
821          }
822
823       // Then change its properties to be what we want
824       //
825       if ((ptp->rc = asc_UserChange (g.idClient, g.idCell, idUser, &lpp->Properties, &ptp->status)) == FALSE)
826          {
827          if (!IsWindow (ptp->hReply))
828             ErrorDialog (ptp->status, IDS_ERROR_CANT_CHANGE_USER, TEXT("%s"), pszName);
829          }
830       if (!ptp->rc)
831          continue;
832
833       // Finally update its lists of groups
834       //
835       if (lpp->pGroupsMember)
836          {
837          USER_GROUPLIST_SET_PARAMS pp;
838          memset (&pp, 0x00, sizeof(USER_GROUPLIST_SET_PARAMS));
839          asc_AsidListCreate (&pp.pUsers);
840          asc_AsidListAddEntry (&pp.pUsers, idUser, 0);
841          asc_AsidListCopy (&pp.pGroups, &lpp->pGroupsMember);
842          pp.fMembership = TRUE;
843          ptp->rc = Task_User_GroupList_Set_Do (&pp, &ptp->status);
844          asc_AsidListFree (&pp.pUsers);
845          asc_AsidListFree (&pp.pGroups);
846          }
847       if (lpp->pGroupsOwner)
848          {
849          USER_GROUPLIST_SET_PARAMS pp;
850          memset (&pp, 0x00, sizeof(USER_GROUPLIST_SET_PARAMS));
851          asc_AsidListCreate (&pp.pUsers);
852          asc_AsidListAddEntry (&pp.pUsers, idUser, 0);
853          asc_AsidListCopy (&pp.pGroups, &lpp->pGroupsOwner);
854          pp.fMembership = FALSE;
855          ptp->rc = Task_User_GroupList_Set_Do (&pp, &ptp->status);
856          asc_AsidListFree (&pp.pUsers);
857          asc_AsidListFree (&pp.pGroups);
858          }
859       }
860
861    // And we're done!
862    //
863    Display_PopulateList();
864
865    if (lpp->pGroupsOwner)
866       asc_AsidListFree (&lpp->pGroupsOwner);
867    if (lpp->pGroupsMember)
868       asc_AsidListFree (&lpp->pGroupsMember);
869    if (lpp->mszNames)
870       FreeString (lpp->mszNames);
871    Delete (lpp);
872    ptp->lpUser = 0; // we freed this; don't let the caller use it again
873 }
874
875
876 void Task_User_Delete (LPTASKPACKET ptp)
877 {
878    LPUSER_DELETE_PARAMS lpp = (LPUSER_DELETE_PARAMS)(ptp->lpUser);
879
880    // Maintain a structure describing any errors we encounter, so we can give
881    // a reasonable error dialog if need be.
882    //
883    LPERRORDATA ped = ED_Create (IDS_ERROR_CANT_DELETE_USER, IDS_ERROR_CANT_DELETE_USER_MULTIPLE);
884
885    // Go through and delete these users
886    //
887    for (size_t iUser = 0; iUser < lpp->pUserList->cEntries; ++iUser)
888       {
889       AFSADMSVR_DELETEUSER_PARAMS pp;
890       memset (&pp, 0x00, sizeof(pp));
891       pp.fDeleteKAS = lpp->fDeleteKAS;
892       pp.fDeletePTS = lpp->fDeletePTS;
893
894       ULONG status;
895       if (!asc_UserDelete (g.idClient, g.idCell, lpp->pUserList->aEntries[iUser].idObject, &pp, &status))
896          ED_RegisterStatus (ped, lpp->pUserList->aEntries[iUser].idObject, FALSE, status);
897       }
898
899    // If there were any errors, report them.
900    //
901    if (ED_GetFinalStatus(ped) && !IsWindow(ptp->hReply))
902       ED_ShowErrorDialog(ped);
903    ED_Free(ped);
904
905    // And we're done!
906    //
907    Display_PopulateList();
908
909    if (lpp->pUserList)
910       asc_AsidListFree (&lpp->pUserList);
911    Delete (lpp);
912    ptp->lpUser = 0; // we freed this; don't let the caller use it again
913 }
914
915
916 void Task_Group_Change (LPTASKPACKET ptp)
917 {
918    LPGROUP_CHANGE_PARAMS lpp = (LPGROUP_CHANGE_PARAMS)( ptp->lpUser );
919
920    if ((ptp->rc = asc_GroupChange (g.idClient, g.idCell, lpp->idGroup, &lpp->NewProperties, &ptp->status)) == TRUE)
921       {
922       Display_RefreshView_Fast();
923       }
924
925    if (!ptp->rc && !IsWindow (ptp->hReply))
926       {
927       TCHAR szGroup[ cchNAME ];
928       asc_ObjectNameGet_Fast (g.idClient, g.idCell, lpp->idGroup, szGroup);
929       ErrorDialog (ptp->status, IDS_ERROR_CANT_CHANGE_GROUP, TEXT("%s"), szGroup);
930       }
931
932    Delete (lpp);
933    ptp->lpUser = 0; // we freed this; don't let the caller use it again
934 }
935
936
937 void Task_Group_Search (LPTASKPACKET ptp)
938 {
939    LPGROUP_SEARCH_PARAMS lpp = (LPGROUP_SEARCH_PARAMS)( ptp->lpUser );
940
941    // Prepare an intermediate place for us to put the results of our search.
942    // We'll be doing lots of work in this intermediate list, so it'll be
943    // implemented as a hashlist with a key over the objects' ASIDs.
944    //
945    typedef struct
946       {
947       ASID idGroup;
948       size_t cUsers;
949       } GROUP_SEARCH_FOUND, *LPGROUP_SEARCH_FOUND;
950
951    LPHASHLIST pListGroups = New (HASHLIST);
952    LPHASHLISTKEY pListKeyAsid = pListGroups->CreateKey ("ASID", Key_Asid_Compare, Key_Asid_HashObject, Key_Asid_HashData);
953
954    // Search through the appropriate groups for each user in the list
955    //
956    for (size_t iUser = 0; iUser < lpp->pUserList->cEntries; ++iUser)
957       {
958       LPASIDLIST pGroups = NULL;
959       ULONG status;
960
961       if (lpp->fMembership)
962          {
963          if (!asc_GroupMembershipGet (g.idClient, g.idCell, lpp->pUserList->aEntries[ iUser ].idObject, &pGroups, &status))
964             continue;
965          }
966       else // (!lpp->fMembership)
967          {
968          if (!asc_GroupOwnershipGet (g.idClient, g.idCell, lpp->pUserList->aEntries[ iUser ].idObject, &pGroups, &status))
969             continue;
970          }
971
972       if (pGroups)
973          {
974          // For each group we found, make sure the group is in the big
975          // list we'll be returning. Use the {lParam} field of the
976          // list's entry as a counter, to remember how many users have
977          // this group
978          //
979          for (size_t iGroup = 0; iGroup < pGroups->cEntries; ++iGroup)
980             {
981             // Is it in the list already? If not, add it.
982             //
983             LPGROUP_SEARCH_FOUND pFind;
984             if ((pFind = (LPGROUP_SEARCH_FOUND)pListKeyAsid->GetFirstObject (&pGroups->aEntries[ iGroup ].idObject)) != NULL)
985                {
986                pFind->cUsers ++;
987                }
988             else
989                {
990                pFind = New (GROUP_SEARCH_FOUND);
991                pFind->idGroup = pGroups->aEntries[ iGroup ].idObject;
992                pFind->cUsers = 1;
993                pListGroups->Add (pFind);
994                }
995             }
996
997          asc_AsidListFree (&pGroups);
998          }
999       }
1000
1001    // Now that we have a list of groups that match our search criteria,
1002    // stick it in an ASID list. The lParam field for each ASID will be set
1003    // to 1 if all users have that group, or 0 if only some have it.
1004    //
1005    if (!asc_AsidListCreate (&TASKDATA(ptp)->pAsidList))
1006       {
1007       ptp->rc = FALSE;
1008       ptp->status = ERROR_NOT_ENOUGH_MEMORY;
1009       }
1010
1011    if (ptp->rc)
1012       {
1013       for (LPENUM pEnum = pListGroups->FindFirst(); pEnum; pEnum = pEnum->FindNext())
1014          {
1015          LPGROUP_SEARCH_FOUND pFind = (LPGROUP_SEARCH_FOUND)( pEnum->GetObject() );
1016
1017          asc_AsidListAddEntry (&TASKDATA(ptp)->pAsidList, pFind->idGroup, (LPARAM)( (pFind->cUsers == lpp->pUserList->cEntries) ? 1 : 0 ));
1018
1019          pListGroups->Remove (pFind);
1020          Delete (pFind);
1021          }
1022
1023       TASKDATA(ptp)->fMembership = lpp->fMembership;
1024       }
1025
1026    // Now that we have an ASID list full of groups to return, make sure we
1027    // have rudimentary properties for all those groups.
1028    //
1029    if (ptp->rc)
1030       {
1031       LPASIDLIST pList;
1032       asc_AsidListCopy (&pList, &TASKDATA(ptp)->pAsidList);
1033
1034       LPASOBJPROPLIST pPropList = NULL;
1035       if (!asc_ObjectPropertiesGetMultiple (g.idClient, GET_RUDIMENTARY_DATA, g.idCell, pList, &pPropList, &ptp->status))
1036          ptp->rc = FALSE;
1037       if (pPropList)
1038          asc_ObjPropListFree (&pPropList);
1039
1040       asc_AsidListFree (&pList);
1041       }
1042
1043    if (pListGroups)
1044       Delete (pListGroups);
1045    asc_AsidListFree (&lpp->pUserList);
1046    Delete (lpp);
1047    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1048 }
1049
1050
1051 void Task_Group_Members_Get (LPTASKPACKET ptp)
1052 {
1053    LPASIDLIST pGroups = (LPASIDLIST)( ptp->lpUser );
1054
1055    // Prepare an intermediate place for us to put the results of our search.
1056    // We'll be doing lots of work in this intermediate list, so it'll be
1057    // implemented as a hashlist with a key over the objects' ASIDs.
1058    //
1059    typedef struct
1060       {
1061       ASID idUser;
1062       size_t cGroups;
1063       } GROUP_MEMBER_FOUND, *LPGROUP_MEMBER_FOUND;
1064
1065    LPHASHLIST pListUsers = New (HASHLIST);
1066    LPHASHLISTKEY pListKeyAsid = pListUsers->CreateKey ("ASID", Key_Asid_Compare, Key_Asid_HashObject, Key_Asid_HashData);
1067
1068    // For each group in the list, find all of that group's members
1069    //
1070    for (size_t iGroup = 0; iGroup < pGroups->cEntries; ++iGroup)
1071       {
1072       LPASIDLIST pMembers = NULL;
1073       ULONG status;
1074
1075       if (!asc_GroupMembersGet (g.idClient, g.idCell, pGroups->aEntries[ iGroup ].idObject, &pMembers, &status))
1076          continue;
1077
1078       if (pMembers)
1079          {
1080          // For each member we found, make sure the member is in the big
1081          // list we'll be returning. Use the {lParam} field of the
1082          // list's entry as a counter, to remember how many groups have
1083          // this member.
1084          //
1085          for (size_t iMember = 0; iMember < pMembers->cEntries; ++iMember)
1086             {
1087             // Is it in the list already? If not, add it.
1088             //
1089             LPGROUP_MEMBER_FOUND pFind;
1090             if ((pFind = (LPGROUP_MEMBER_FOUND)pListKeyAsid->GetFirstObject (&pMembers->aEntries[ iMember ].idObject)) != NULL)
1091                {
1092                pFind->cGroups ++;
1093                }
1094             else
1095                {
1096                pFind = New (GROUP_MEMBER_FOUND);
1097                pFind->idUser = pMembers->aEntries[ iMember ].idObject;
1098                pFind->cGroups = 1;
1099                pListUsers->Add (pFind);
1100                }
1101             }
1102
1103          asc_AsidListFree (&pMembers);
1104          }
1105       }
1106
1107    // Now that we have a list of users that match our search criteria,
1108    // stick it in an ASID list. The lParam field for each ASID will be set
1109    // to 1 if all groups have that member, or 0 if only some have it.
1110    //
1111    if (!asc_AsidListCreate (&TASKDATA(ptp)->pAsidList))
1112       {
1113       ptp->rc = FALSE;
1114       ptp->status = ERROR_NOT_ENOUGH_MEMORY;
1115       }
1116
1117    if (ptp->rc)
1118       {
1119       for (LPENUM pEnum = pListUsers->FindFirst(); pEnum; pEnum = pEnum->FindNext())
1120          {
1121          LPGROUP_MEMBER_FOUND pFind = (LPGROUP_MEMBER_FOUND)( pEnum->GetObject() );
1122
1123          asc_AsidListAddEntry (&TASKDATA(ptp)->pAsidList, pFind->idUser, (LPARAM)( (pFind->cGroups == pGroups->cEntries) ? 1 : 0 ));
1124
1125          pListUsers->Remove (pFind);
1126          Delete (pFind);
1127          }
1128       }
1129
1130    // Now that we have an ASID list full of users to return, make sure we
1131    // have rudimentary properties for all those users.
1132    //
1133    if (ptp->rc)
1134       {
1135       LPASIDLIST pList;
1136       asc_AsidListCopy (&pList, &TASKDATA(ptp)->pAsidList);
1137
1138       LPASOBJPROPLIST pPropList = NULL;
1139       if (!asc_ObjectPropertiesGetMultiple (g.idClient, GET_RUDIMENTARY_DATA, g.idCell, pList, &pPropList, &ptp->status))
1140          ptp->rc = FALSE;
1141       if (pPropList)
1142          asc_ObjPropListFree (&pPropList);
1143
1144       asc_AsidListFree (&pList);
1145       }
1146
1147    if (pListUsers)
1148       Delete (pListUsers);
1149    asc_AsidListFree (&pGroups);
1150    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1151 }
1152
1153
1154 void Task_Group_Members_Set (LPTASKPACKET ptp)
1155 {
1156    LPGROUP_MEMBERS_SET_PARAMS lpp = (LPGROUP_MEMBERS_SET_PARAMS)( ptp->lpUser );
1157
1158    ptp->rc = Task_Group_Members_Set_Do (lpp, &ptp->status);
1159
1160    asc_AsidListFree (&lpp->pGroups);
1161    asc_AsidListFree (&lpp->pMembers);
1162    Delete (lpp);
1163    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1164 }
1165
1166
1167 BOOL Task_Group_Members_Set_Do (LPGROUP_MEMBERS_SET_PARAMS lpp, ULONG *pStatus)
1168 {
1169    BOOL rc = TRUE;
1170
1171    // Maintain a structure describing any errors we encounter, so we can give
1172    // a reasonable error dialog if need be.
1173    //
1174    LPERRORDATA ped = ED_Create (IDS_ERROR_CANT_SET_MEMBERS, IDS_ERROR_CANT_SET_MEMBERS_MULTIPLE);
1175
1176    // We'll need the supplied member-list in a hashlist, so we can quickly
1177    // test it to see if a particular member should remain in a group.
1178    //
1179    LPHASHLIST pMembersAllow = New (HASHLIST);
1180    for (size_t iMember = 0; iMember < lpp->pMembers->cEntries; ++iMember)
1181       pMembersAllow->AddUnique ((PVOID)(lpp->pMembers->aEntries[ iMember ].idObject));
1182
1183    // We'll have to do this next bit for each group in the supplied group-list
1184    //
1185    for (size_t iGroup = 0; iGroup < lpp->pGroups->cEntries; ++iGroup)
1186       {
1187       ULONG status;
1188
1189       // Obtain the current list of members for this group
1190       //
1191       LPASIDLIST pMembersOld = NULL;
1192       if (!asc_GroupMembersGet (g.idClient, g.idCell, lpp->pGroups->aEntries[ iGroup ].idObject, &pMembersOld, &status))
1193          {
1194          ED_RegisterStatus (ped, lpp->pGroups->aEntries[ iGroup ].idObject, FALSE, status);
1195          continue;
1196          }
1197       if (!pMembersOld)
1198          continue;
1199
1200       // Test each member in that current list to see if it's also on our
1201       // pMembersAllow list. If not, remove it.
1202       //
1203       for (iMember = 0; iMember < pMembersOld->cEntries; ++iMember)
1204          {
1205          if (pMembersAllow->fIsInList ((PVOID)(pMembersOld->aEntries[iMember].idObject)))
1206             continue;
1207
1208          if (!asc_GroupMemberRemove (g.idClient, g.idCell, lpp->pGroups->aEntries[iGroup].idObject, pMembersOld->aEntries[iMember].idObject, &status))
1209             ED_RegisterStatus (ped, lpp->pGroups->aEntries[ iGroup ].idObject, FALSE, status);
1210          }
1211
1212       // Now the more complex part: see if there are any members in the
1213       // supplied member-list which are marked as mandatory, but which
1214       // aren't in our pMembersOld list. We'll need to put the latter in a
1215       // hashlist for this...
1216       //
1217       LPHASHLIST pMembersOldList = New (HASHLIST);
1218       for (iMember = 0; iMember < pMembersOld->cEntries; ++iMember)
1219          pMembersOldList->AddUnique ((PVOID)(pMembersOld->aEntries[ iMember ].idObject));
1220
1221       for (iMember = 0; iMember < lpp->pMembers->cEntries; ++iMember)
1222          {
1223          if (!lpp->pMembers->aEntries[ iMember ].lParam)
1224             continue; // member not mandatory
1225          if (pMembersOldList->fIsInList ((PVOID)(lpp->pMembers->aEntries[ iMember ].idObject)))
1226             continue; // already a member
1227
1228          if (!asc_GroupMemberAdd (g.idClient, g.idCell, lpp->pGroups->aEntries[iGroup].idObject, lpp->pMembers->aEntries[iMember].idObject, &status))
1229             ED_RegisterStatus (ped, lpp->pGroups->aEntries[ iGroup ].idObject, FALSE, status);
1230          }
1231
1232       Delete (pMembersOldList);
1233
1234       asc_AsidListFree (&pMembersOld);
1235       }
1236
1237    // If there were any errors, report them.
1238    //
1239    if ((*pStatus = ED_GetFinalStatus(ped)) != 0)
1240       {
1241       rc = FALSE;
1242       ED_ShowErrorDialog(ped);
1243       }
1244    ED_Free(ped);
1245
1246    // Done; clean up
1247    //
1248    Delete (pMembersAllow);
1249    return rc;
1250 }
1251
1252
1253 void Task_Group_Enum (LPTASKPACKET ptp)
1254 {
1255    LPCTSTR pszPattern = (LPCTSTR)( ptp->lpUser );
1256
1257    TCHAR szRegExp[ cchNAME ];
1258    TranslateRegExp (szRegExp, pszPattern);
1259    if ((ptp->rc = asc_ObjectFindMultiple (g.idClient, g.idCell, TYPE_GROUP, szRegExp, NULL, &TASKDATA(ptp)->pAsidList, &ptp->status)) == TRUE)
1260       {
1261       LPASOBJPROPLIST pPropList = NULL;
1262
1263       ptp->rc = asc_ObjectPropertiesGetMultiple (g.idClient, GET_RUDIMENTARY_DATA, g.idCell, TASKDATA(ptp)->pAsidList, &pPropList, &ptp->status);
1264
1265       if (pPropList)
1266          asc_ObjPropListFree (&pPropList);
1267       }
1268
1269    FreeString (pszPattern);
1270    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1271 }
1272
1273
1274 void Task_Group_Rename (LPTASKPACKET ptp)
1275 {
1276    LPGROUP_RENAME_PARAMS lpp = (LPGROUP_RENAME_PARAMS)( ptp->lpUser );
1277
1278    if ((ptp->rc = asc_GroupRename (g.idClient, g.idCell, lpp->idGroup, lpp->szNewName, &ptp->status)) != FALSE)
1279       {
1280       Display_RefreshView_Fast();
1281       }
1282
1283    if (!ptp->rc && !IsWindow (ptp->hReply))
1284       {
1285       TCHAR szOldName[ cchNAME ];
1286       asc_ObjectNameGet_Fast (g.idClient, g.idCell, lpp->idGroup, szOldName);
1287       ErrorDialog (ptp->status, IDS_ERROR_CANT_RENAME_GROUP, TEXT("%s%s"), szOldName, lpp->szNewName);
1288       }
1289
1290    Delete (lpp);
1291    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1292 }
1293
1294
1295 void Task_Group_Owned_Get (LPTASKPACKET ptp)
1296 {
1297    ASID idGroup = (ASID)( ptp->lpUser );
1298
1299    ptp->rc = asc_GroupOwnershipGet (g.idClient, g.idCell, idGroup, &TASKDATA(ptp)->pAsidList, &ptp->status);
1300
1301    // Modify the ASID list to have lParams of 1. (This indicates that all
1302    // our supplied groups own this thing; silly but consistent with the
1303    // other such interfaces.)
1304    //
1305    if (TASKDATA(ptp)->pAsidList)
1306       {
1307       for (size_t ii = 0; ii < TASKDATA(ptp)->pAsidList->cEntries; ++ii)
1308          TASKDATA(ptp)->pAsidList->aEntries[ ii ].lParam = 1;
1309       }
1310
1311    // Now that we have an ASID list full of groups to return, make sure we
1312    // have rudimentary properties for all those groups.
1313    //
1314    if (ptp->rc)
1315       {
1316       LPASIDLIST pList;
1317       asc_AsidListCopy (&pList, &TASKDATA(ptp)->pAsidList);
1318
1319       LPASOBJPROPLIST pPropList = NULL;
1320       if (!asc_ObjectPropertiesGetMultiple (g.idClient, GET_RUDIMENTARY_DATA, g.idCell, pList, &pPropList, &ptp->status))
1321          ptp->rc = FALSE;
1322       if (pPropList)
1323          asc_ObjPropListFree (&pPropList);
1324
1325       asc_AsidListFree (&pList);
1326       }
1327 }
1328
1329
1330 void Task_Group_Owned_Set (LPTASKPACKET ptp)
1331 {
1332    LPGROUP_OWNED_SET_PARAMS lpp = (LPGROUP_OWNED_SET_PARAMS)( ptp->lpUser );
1333
1334    ptp->rc = Task_Group_Owned_Set_Do (lpp, &ptp->status);
1335
1336    asc_AsidListFree (&lpp->pOwnedGroups);
1337    Delete (lpp);
1338    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1339 }
1340
1341
1342 BOOL Task_Group_Owned_Set_Do (LPGROUP_OWNED_SET_PARAMS lpp, ULONG *pStatus)
1343 {
1344    BOOL rc = TRUE;
1345
1346    // Maintain a structure describing any errors we encounter, so we can give
1347    // a reasonable error dialog if need be.
1348    //
1349    LPERRORDATA ped = ED_Create (IDS_ERROR_CANT_CHANGE_OWNER, IDS_ERROR_CANT_CHANGE_OWNER_MULTIPLE);
1350
1351    // We'll need the supplied groups-to-own list in a hashlist, so we can
1352    // quickly test it for inclusion of a particular group.
1353    //
1354    LPHASHLIST pGroupsAllow = New (HASHLIST);
1355    for (size_t iGroup = 0; iGroup < lpp->pOwnedGroups->cEntries; ++iGroup)
1356       pGroupsAllow->AddUnique ((PVOID)(lpp->pOwnedGroups->aEntries[ iGroup ].idObject));
1357
1358    // Obtain the current list-of-groups-owned for this group
1359    //
1360    LPASIDLIST pGroupsOld = NULL;
1361    if ((rc = asc_GroupOwnershipGet (g.idClient, g.idCell, lpp->idGroup, &pGroupsOld, pStatus)) == FALSE)
1362       pGroupsOld = NULL;
1363
1364    if (!pGroupsOld)
1365       {
1366       for (size_t ii = 0; ii < lpp->pOwnedGroups->cEntries; ++ii)
1367          ED_RegisterStatus (ped, lpp->pOwnedGroups->aEntries[ii].idObject, FALSE, *pStatus);
1368       }
1369    else
1370       {
1371       // Test each group in that current list to see if it's also on our
1372       // pGroupsAllow list. If not, remove it.
1373       //
1374       for (iGroup = 0; iGroup < pGroupsOld->cEntries; ++iGroup)
1375          {
1376          if (pGroupsAllow->fIsInList ((PVOID)(pGroupsOld->aEntries[iGroup].idObject)))
1377             continue;
1378
1379          ULONG status;
1380          ASOBJPROP Properties;
1381          if (!asc_ObjectPropertiesGet (g.idClient, GET_ALL_DATA, g.idCell, pGroupsOld->aEntries[iGroup].idObject, &Properties, &status))
1382             ED_RegisterStatus (ped, pGroupsOld->aEntries[iGroup].idObject, FALSE, status);
1383          else
1384             {
1385             AFSADMSVR_CHANGEGROUP_PARAMS pp;
1386             memset (&pp, 0x00, sizeof(pp));
1387             lstrcpy (pp.szOwner, Properties.szName); // make group self-owned
1388             pp.aaListStatus = Properties.u.GroupProperties.aaListStatus;
1389             pp.aaListGroupsOwned = Properties.u.GroupProperties.aaListGroupsOwned;
1390             pp.aaListMembers = Properties.u.GroupProperties.aaListMembers;
1391             pp.aaAddMember = Properties.u.GroupProperties.aaAddMember;
1392             pp.aaDeleteMember = Properties.u.GroupProperties.aaDeleteMember;
1393
1394             if (!asc_GroupChange (g.idClient, g.idCell, pGroupsOld->aEntries[iGroup].idObject, &pp, &status))
1395                ED_RegisterStatus (ped, pGroupsOld->aEntries[iGroup].idObject, FALSE, status);
1396             }
1397          }
1398
1399       // Now the more complex part: see if there are any groups in the
1400       // supplied group-list which are marked as mandatory, but which
1401       // aren't in our pGroupsOld list. We'll need to put the latter in a
1402       // hashlist for this...
1403       //
1404       LPHASHLIST pGroupsOldList = New (HASHLIST);
1405       for (iGroup = 0; iGroup < pGroupsOld->cEntries; ++iGroup)
1406          pGroupsOldList->AddUnique ((PVOID)(pGroupsOld->aEntries[ iGroup ].idObject));
1407
1408       for (iGroup = 0; iGroup < lpp->pOwnedGroups->cEntries; ++iGroup)
1409          {
1410          if (!lpp->pOwnedGroups->aEntries[ iGroup ].lParam)
1411             continue; // group not mandatory
1412          if (pGroupsOldList->fIsInList ((PVOID)(lpp->pOwnedGroups->aEntries[ iGroup ].idObject)))
1413             continue; // already a member
1414
1415          ULONG status;
1416          ASOBJPROP Properties;
1417          if (!asc_ObjectPropertiesGet (g.idClient, GET_ALL_DATA, g.idCell, lpp->pOwnedGroups->aEntries[iGroup].idObject, &Properties, &status))
1418             ED_RegisterStatus (ped, lpp->pOwnedGroups->aEntries[iGroup].idObject, FALSE, status);
1419          else
1420             {
1421             AFSADMSVR_CHANGEGROUP_PARAMS pp;
1422             memset (&pp, 0x00, sizeof(pp));
1423             pp.aaListStatus = Properties.u.GroupProperties.aaListStatus;
1424             pp.aaListGroupsOwned = Properties.u.GroupProperties.aaListGroupsOwned;
1425             pp.aaListMembers = Properties.u.GroupProperties.aaListMembers;
1426             pp.aaAddMember = Properties.u.GroupProperties.aaAddMember;
1427             pp.aaDeleteMember = Properties.u.GroupProperties.aaDeleteMember;
1428
1429             if (asc_ObjectNameGet_Fast (g.idClient, g.idCell, lpp->idGroup, pp.szOwner, &status))
1430                {
1431                if (!asc_GroupChange (g.idClient, g.idCell, lpp->pOwnedGroups->aEntries[iGroup].idObject, &pp, &status))
1432                   ED_RegisterStatus (ped, lpp->pOwnedGroups->aEntries[iGroup].idObject, FALSE, status);
1433                }
1434             }
1435          }
1436
1437       Delete (pGroupsOldList);
1438       asc_AsidListFree (&pGroupsOld);
1439       }
1440
1441    // If there were any errors, report them.
1442    //
1443    if ((*pStatus = ED_GetFinalStatus(ped)) != 0)
1444       {
1445       rc = FALSE;
1446       ED_ShowErrorDialog(ped);
1447       }
1448    ED_Free(ped);
1449
1450    // Done; clean up
1451    //
1452    Delete (pGroupsAllow);
1453    return rc;
1454 }
1455
1456
1457 void Task_Group_Create (LPTASKPACKET ptp)
1458 {
1459    LPGROUP_CREATE_PARAMS lpp = (LPGROUP_CREATE_PARAMS)(ptp->lpUser);
1460
1461    // If no owner was specified, use our current credentials' ID. If
1462    // we can't get that, we'll make each group self-owned.
1463    //
1464    if (!lpp->szOwner[0])
1465       {
1466       TCHAR szCell[ cchNAME ];
1467       if (!AfsAppLib_CrackCredentials (g.hCreds, szCell, lpp->szOwner))
1468          lpp->szOwner[0] = TEXT('\0');
1469       }
1470
1471    // We may actually have been asked to create more than one group here;
1472    // the {lpp->mszNames} parameter is a multi-string. So everything we
1473    // do, we'll do for each new group-name...
1474    //
1475    for (LPTSTR pszName = lpp->mszNames; pszName && *pszName; pszName += 1+lstrlen(pszName))
1476       {
1477       // First create this new group account
1478       //
1479       ASID idGroup;
1480
1481       AFSADMSVR_CREATEGROUP_PARAMS pp;
1482       memset (&pp, 0x00, sizeof(AFSADMSVR_CREATEGROUP_PARAMS));
1483       pp.idGroup = lpp->idGroup;
1484       lstrcpy (pp.szName, pszName);
1485       lstrcpy (pp.szOwner, lpp->szOwner);
1486       if (!pp.szOwner[0])
1487          lstrcpy (pp.szOwner, pszName);
1488
1489       if ((ptp->rc = asc_GroupCreate (g.idClient, g.idCell, &pp, &idGroup, &ptp->status)) == FALSE)
1490          {
1491          if (!IsWindow (ptp->hReply))
1492             ErrorDialog (ptp->status, IDS_ERROR_CANT_CREATE_GROUP, TEXT("%s"), pszName);
1493          continue;
1494          }
1495
1496       // Then change its properties to be what we want
1497       //
1498       if ((ptp->rc = asc_GroupChange (g.idClient, g.idCell, idGroup, &lpp->Properties, &ptp->status)) == FALSE)
1499          {
1500          if (!IsWindow (ptp->hReply))
1501             ErrorDialog (ptp->status, IDS_ERROR_CANT_CHANGE_GROUP, TEXT("%s"), pszName);
1502          }
1503       if (!ptp->rc)
1504          continue;
1505
1506       // Finally update its lists of groups
1507       //
1508       if (lpp->pMembers)
1509          {
1510          GROUP_MEMBERS_SET_PARAMS pp;
1511          memset (&pp, 0x00, sizeof(GROUP_MEMBERS_SET_PARAMS));
1512          asc_AsidListCreate (&pp.pGroups);
1513          asc_AsidListAddEntry (&pp.pGroups, idGroup, 0);
1514          asc_AsidListCopy (&pp.pMembers, &lpp->pMembers);
1515          ptp->rc = Task_Group_Members_Set_Do (&pp, &ptp->status);
1516          asc_AsidListFree (&pp.pGroups);
1517          asc_AsidListFree (&pp.pMembers);
1518          }
1519       if (lpp->pGroupsOwner)
1520          {
1521          GROUP_OWNED_SET_PARAMS pp;
1522          memset (&pp, 0x00, sizeof(GROUP_OWNED_SET_PARAMS));
1523          pp.idGroup = idGroup;
1524          asc_AsidListCopy (&pp.pOwnedGroups, &lpp->pGroupsOwner);
1525          ptp->rc = Task_Group_Owned_Set_Do (&pp, &ptp->status);
1526          asc_AsidListFree (&pp.pOwnedGroups);
1527          }
1528       }
1529
1530    // And we're done!
1531    //
1532    Display_PopulateList();
1533
1534    if (lpp->pGroupsOwner)
1535       asc_AsidListFree (&lpp->pGroupsOwner);
1536    if (lpp->pMembers)
1537       asc_AsidListFree (&lpp->pMembers);
1538    if (lpp->mszNames)
1539       FreeString (lpp->mszNames);
1540    Delete (lpp);
1541    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1542 }
1543
1544
1545 void Task_Group_Delete (LPTASKPACKET ptp)
1546 {
1547    LPASIDLIST pGroupList = (LPASIDLIST)(ptp->lpUser);
1548
1549    // Maintain a structure describing any errors we encounter, so we can give
1550    // a reasonable error dialog if need be.
1551    //
1552    LPERRORDATA ped = ED_Create (IDS_ERROR_CANT_DELETE_GROUP, IDS_ERROR_CANT_DELETE_GROUP_MULTIPLE);
1553
1554    // Go through and delete these users
1555    //
1556    for (size_t iGroup = 0; iGroup < pGroupList->cEntries; ++iGroup)
1557       {
1558       ULONG status;
1559       if (!asc_GroupDelete (g.idClient, g.idCell, pGroupList->aEntries[iGroup].idObject, &status))
1560          ED_RegisterStatus (ped, pGroupList->aEntries[iGroup].idObject, FALSE, status);
1561       }
1562
1563    // If there were any errors, report them.
1564    //
1565    if (ED_GetFinalStatus(ped) && !IsWindow(ptp->hReply))
1566       ED_ShowErrorDialog(ped);
1567    ED_Free(ped);
1568
1569    // And we're done!
1570    //
1571    Display_PopulateList();
1572
1573    if (pGroupList)
1574       asc_AsidListFree (&pGroupList);
1575    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1576 }
1577
1578
1579 void Task_Cell_Change (LPTASKPACKET ptp)
1580 {
1581    LPCELL_CHANGE_PARAMS lpp = (LPCELL_CHANGE_PARAMS)(ptp->lpUser);
1582
1583    AFSADMSVR_CHANGECELL_PARAMS Change;
1584    memset (&Change, 0x00, sizeof(Change));
1585    Change.idUserMax = (DWORD)(lpp->idUserMax);
1586    Change.idGroupMax = (DWORD)(lpp->idGroupMax);
1587    ptp->rc = asc_CellChange (g.idClient, lpp->idCell, &Change, &ptp->status);
1588
1589    if (!ptp->rc && !IsWindow (ptp->hReply))
1590       {
1591       TCHAR szCell[ cchNAME ];
1592       asc_CellNameGet_Fast (g.idClient, lpp->idCell, szCell);
1593       ErrorDialog (ptp->status, IDS_ERROR_CANT_CHANGE_CELL, TEXT("%s"), szCell);
1594       }
1595
1596    Delete (lpp);
1597    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1598 }
1599
1600
1601 void Task_List_Translate (LPTASKPACKET ptp)
1602 {
1603    LPLIST_TRANSLATE_PARAMS lpp = (LPLIST_TRANSLATE_PARAMS)( ptp->lpUser );
1604    TASKDATA(ptp)->Type = lpp->Type;
1605
1606    // We'll need a hashlist into which to dump our results as we build
1607    // them (we use a hashlist so we can quickly detect and ignore duplicates).
1608    //
1609    LPHASHLIST pList = New (HASHLIST);
1610
1611    // Now split the input string up into a series of names. To do that,
1612    // we'll valid separator characters are; since names can't have whitespace,
1613    // we'll include that.
1614    //
1615    TCHAR szSeparators[ cchRESOURCE ];
1616    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SLIST, szSeparators, cchRESOURCE))
1617       szSeparators[0] = TEXT(',');
1618    GetString (&szSeparators[1], IDS_SEPARATORS);
1619
1620    if (lpp->pszNames)
1621       {
1622       LPCTSTR pszStart = lpp->pszNames;
1623       while (iswhite(*pszStart) || lstrchr (szSeparators, *pszStart))
1624          ++pszStart;
1625
1626       while (*pszStart)
1627          {
1628          // Find the first non-name character
1629          //
1630          LPCTSTR pszEnd = pszStart;
1631          while (*pszEnd && !iswhite(*pszEnd) && !lstrchr(szSeparators, *pszEnd))
1632             ++pszEnd;
1633
1634          // Copy off this particular name
1635          //
1636          TCHAR szName[ cchNAME ];
1637          lstrcpy (szName, pszStart);
1638          szName[ pszEnd - pszStart ] = TEXT('\0');
1639
1640          // Find the next valid-name character
1641          //
1642          pszStart = pszEnd;
1643          while (iswhite(*pszStart) || lstrchr(szSeparators, *pszStart))
1644             ++pszStart;
1645
1646          // Translate this particular name. We'll also handle wildcards
1647          // here: if we don't get any wildcard characters, treat it as
1648          // a direct lookup; if we do, treat it as a regexp match.
1649          //
1650          if (lstrchr (szName, TEXT('*')) || lstrchr (szName, TEXT('?')) ||
1651              lstrchr (szName, TEXT('^')) || lstrchr (szName, TEXT('!')))
1652             {
1653             TCHAR szRegExp[ cchNAME ];
1654             TranslateRegExp (szRegExp, szName);
1655
1656             LPASIDLIST pAsidList;
1657             if (asc_ObjectFindMultiple (g.idClient, g.idCell, lpp->Type, szRegExp, NULL, &pAsidList, &ptp->status))
1658                {
1659                if (pAsidList)
1660                   {
1661                   for (size_t ii = 0; ii < pAsidList->cEntries; ++ii)
1662                      pList->AddUnique ((PVOID)(pAsidList->aEntries[ii].idObject));
1663                   asc_AsidListFree (&pAsidList);
1664                   }
1665                }
1666             }
1667          else // No wildcards; just look up the name directly
1668             {
1669             ASID idObject;
1670             if (asc_ObjectFind (g.idClient, g.idCell, lpp->Type, szName, &idObject, &ptp->status))
1671                pList->AddUnique ((PVOID)idObject);
1672             }
1673          }
1674       }
1675
1676    // Finally, build an ASIDLIST from our hashlist's contents.
1677    //
1678    if (!asc_AsidListCreate (&TASKDATA(ptp)->pAsidList))
1679       {
1680       ptp->rc = FALSE;
1681       ptp->status = ERROR_NOT_ENOUGH_MEMORY;
1682       }
1683    else
1684       {
1685       for (LPENUM pEnum = pList->FindFirst(); pEnum; pEnum = pEnum->FindNext())
1686          {
1687          ASID idObject = (ASID)(pEnum->GetObject());
1688          asc_AsidListAddEntry (&TASKDATA(ptp)->pAsidList, idObject, 0);
1689          }
1690       }
1691
1692    // Done! Clean up.
1693    //
1694    Delete (pList);
1695    FreeString (lpp->pszNames);
1696    Delete (lpp);
1697    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1698 }
1699
1700
1701 void Task_Object_Listen (LPTASKPACKET ptp)
1702 {
1703    LPOBJECT_LISTEN_PARAMS lpp = (LPOBJECT_LISTEN_PARAMS)( ptp->lpUser );
1704
1705    if (IsWindow(lpp->hNotify) && (lpp->pAsidList))
1706       ptp->rc = asc_ObjectListenMultiple (g.idClient, g.idCell, lpp->pAsidList, lpp->hNotify, &ptp->status);
1707    else
1708       ptp->rc = asc_ObjectListenClear (g.idClient, lpp->hNotify, &ptp->status);
1709
1710    if (lpp->pAsidList)
1711       asc_AsidListFree (&lpp->pAsidList);
1712    Delete (lpp);
1713    ptp->lpUser = 0; // we freed this; don't let the caller use it again
1714 }
1715
1716
1717 void Task_Object_Get (LPTASKPACKET ptp)
1718 {
1719    ASID idObject = (ASID)(ptp->lpUser);
1720    ptp->rc = asc_ObjectPropertiesGet (g.idClient, GET_ALL_DATA, g.idCell, idObject, &TASKDATA(ptp)->Properties, &ptp->status);
1721 }
1722
1723
1724 void Task_Set_Refresh (LPTASKPACKET ptp)
1725 {
1726    if (g.idCell)
1727       {
1728       ptp->rc = asc_CellRefreshRateSet (g.idClient, g.idCell, gr.cminRefreshRate, &ptp->status);
1729       }
1730 }
1731
1732
1733 void Task_Expired_Creds (LPTASKPACKET ptp)
1734 {
1735    if (g.idCell)
1736       {
1737       CheckForExpiredCredentials();
1738       }
1739 }
1740
1741
1742 void WeedAsidList (LPASIDLIST *ppList, BOOL fWantMachines)
1743 {
1744    ULONG status;
1745
1746    // First off, we can't do anything unless we have these guys' names.
1747    //
1748    if (ppList && (*ppList) && (*ppList)->cEntries)
1749       {
1750       LPASOBJPROPLIST pPropList = NULL;
1751       asc_ObjectPropertiesGetMultiple (g.idClient, GET_RUDIMENTARY_DATA, g.idCell, *ppList, &pPropList, &status);
1752       if (pPropList)
1753          asc_ObjPropListFree (&pPropList);
1754       }
1755
1756    for (size_t ii = 0; ppList && (*ppList) && (ii < (*ppList)->cEntries); )
1757       {
1758       TCHAR szName[ cchRESOURCE ];
1759       if (!asc_ObjectNameGet_Fast (g.idClient, g.idCell, (*ppList)->aEntries[ ii ].idObject, szName, &status))
1760          {
1761          ++ii;
1762          continue;
1763          }
1764
1765       if (fIsMachineAccount(szName) == fWantMachines)
1766          ++ii;
1767       else
1768          asc_AsidListRemoveEntryByIndex (ppList, ii);
1769       }
1770 }
1771
1772
1773 void TranslateRegExp (LPTSTR pszTarget, LPCTSTR pszSource)
1774 {
1775    if (pszSource)
1776       {
1777       if (!gr.fWindowsRegexp)
1778          {
1779          lstrcpy (pszTarget, pszSource);
1780          }
1781       else
1782          {
1783          for ( ; *pszSource; pszSource++)
1784             {
1785             switch (*pszSource)
1786                {
1787                case TEXT('['):
1788                case TEXT(']'):
1789                case TEXT('.'):
1790                   *pszTarget++ = TEXT('\\');
1791                   *pszTarget++ = *pszSource;
1792                   break;
1793
1794                case TEXT('?'):
1795                   *pszTarget++ = TEXT('.');
1796                   break;
1797
1798                case TEXT('*'):
1799                   *pszTarget++ = TEXT('.');
1800                   *pszTarget++ = TEXT('*');
1801                   break;
1802
1803                default:
1804                   *pszTarget++ = *pszSource;
1805                   break;
1806                }
1807             }
1808          }
1809       }
1810
1811    *pszTarget++ = TEXT('\0');
1812 }
1813
1814
1815 BOOL PerformRefresh (LPTASKPACKET ptp, ASID idScope, ULONG *pStatus)
1816 {
1817    // Invalidate the admin server's cached information about the specified
1818    // object. Remember that this is recursive hierarchically: if you pass
1819    // in a cell's ID, for instance, information about all users, groups,
1820    // servers, services, partitions and volumes anywhere in that cell will
1821    // be discarded.
1822    //
1823    if (!idScope)
1824       idScope = g.idCell;
1825
1826    if (!asc_ObjectRefresh (g.idClient, g.idCell, idScope, pStatus))
1827       return FALSE;
1828
1829    // The Refresh call above is a misnomer; it's really an Invalidate request,
1830    // in that it only causes the admin server to dump its cached information
1831    // over the specified scope. At this point we need to do something to
1832    // trigger the server to re-query that information, so that it will send
1833    // an ACTION_REFRESH notification (indicating it's doing that requery),
1834    // which we'll pick up on and use as a trigger to refresh our display.
1835    // A convenient way to get the server to do that re-query is to ask it
1836    // to perform some simple search among the users in the cell--we'll ask
1837    // it to find, oh, say, "JoeBobUser", and in order to do that, it will
1838    // have to re-fill its cache.
1839    //
1840    LPASIDLIST pListDummy;
1841    ULONG statusDummy;
1842    (void)asc_ObjectFindMultiple (g.idClient, g.idCell, TYPE_USER, TEXT("JoeBobUser"), NULL, &pListDummy, &statusDummy);
1843    if (pListDummy != NULL)
1844       asc_AsidListFree (&pListDummy);
1845
1846    return TRUE;
1847 }
1848