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