[USRMGR]
[reactos.git] / reactos / dll / cpl / usrmgr / groupprops.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS User Manager Control Panel
4 * FILE: dll/cpl/usrmgr/groupprops.c
5 * PURPOSE: Group property sheet
6 *
7 * PROGRAMMERS: Eric Kohl
8 */
9
10 #include "usrmgr.h"
11
12 typedef struct _GENERAL_GROUP_DATA
13 {
14 TCHAR szGroupName[1];
15 } GENERAL_GROUP_DATA, *PGENERAL_GROUP_DATA;
16
17
18 static VOID
19 GetTextSid(PSID pSid,
20 LPTSTR pTextSid)
21 {
22 PSID_IDENTIFIER_AUTHORITY psia;
23 DWORD dwSubAuthorities;
24 DWORD dwSidRev = SID_REVISION;
25 DWORD dwCounter;
26 DWORD dwSidSize;
27
28 psia = GetSidIdentifierAuthority(pSid);
29
30 dwSubAuthorities = *GetSidSubAuthorityCount(pSid);
31
32 dwSidSize = wsprintf(pTextSid, TEXT("S-%lu-"), dwSidRev);
33
34 if ((psia->Value[0] != 0) || (psia->Value[1] != 0))
35 {
36 dwSidSize += wsprintf(pTextSid + lstrlen(pTextSid),
37 TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
38 (USHORT)psia->Value[0],
39 (USHORT)psia->Value[1],
40 (USHORT)psia->Value[2],
41 (USHORT)psia->Value[3],
42 (USHORT)psia->Value[4],
43 (USHORT)psia->Value[5]);
44 }
45 else
46 {
47 dwSidSize += wsprintf(pTextSid + lstrlen(pTextSid),
48 TEXT("%lu"),
49 (ULONG)(psia->Value[5]) +
50 (ULONG)(psia->Value[4] << 8) +
51 (ULONG)(psia->Value[3] << 16) +
52 (ULONG)(psia->Value[2] << 24));
53 }
54
55 for (dwCounter = 0 ; dwCounter < dwSubAuthorities ; dwCounter++)
56 {
57 dwSidSize += wsprintf(pTextSid + dwSidSize, TEXT("-%lu"),
58 *GetSidSubAuthority(pSid, dwCounter));
59 }
60 }
61
62
63 static VOID
64 InitGroupMembersList(HWND hwndDlg,
65 PGENERAL_GROUP_DATA pGroupData)
66 {
67 HWND hwndLV;
68 LV_COLUMN column;
69 RECT rect;
70 TCHAR szStr[32];
71 HIMAGELIST hImgList;
72 HICON hIcon;
73
74 NET_API_STATUS netStatus;
75 PUSER_INFO_20 pUserBuffer;
76 DWORD entriesread;
77 DWORD totalentries;
78 DWORD resume_handle = 0;
79 DWORD i;
80 LV_ITEM lvi;
81 INT iItem;
82
83 hwndLV = GetDlgItem(hwndDlg, IDC_USER_ADD_MEMBERSHIP_LIST);
84 GetClientRect(hwndLV, &rect);
85
86 hImgList = ImageList_Create(16,16,ILC_COLOR8 | ILC_MASK,5,5);
87 hIcon = LoadImage(hApplet,MAKEINTRESOURCE(IDI_GROUP),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
88 ImageList_AddIcon(hImgList,hIcon);
89 DestroyIcon(hIcon);
90 hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_USER), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
91 ImageList_AddIcon(hImgList, hIcon);
92 DestroyIcon(hIcon);
93 hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_LOCKED_USER), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
94 ImageList_AddIcon(hImgList, hIcon);
95 DestroyIcon(hIcon);
96
97 (void)ListView_SetImageList(hwndLV, hImgList, LVSIL_SMALL);
98 (void)ListView_SetExtendedListViewStyle(hwndLV, LVS_EX_FULLROWSELECT);
99
100 memset(&column, 0x00, sizeof(column));
101 column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
102 column.fmt = LVCFMT_LEFT;
103 column.cx = (INT)((rect.right - rect.left) * 0.40);
104 column.iSubItem = 0;
105 LoadString(hApplet, IDS_NAME, szStr, sizeof(szStr) / sizeof(szStr[0]));
106 column.pszText = szStr;
107 (void)ListView_InsertColumn(hwndLV, 0, &column);
108
109 column.cx = (INT)((rect.right - rect.left) * 0.60);
110 column.iSubItem = 1;
111 LoadString(hApplet, IDS_DESCRIPTION, szStr, sizeof(szStr) / sizeof(szStr[0]));
112 column.pszText = szStr;
113 (void)ListView_InsertColumn(hwndLV, 1, &column);
114
115 /* TODO: Enumerate global groups and add them to the list! */
116
117 for (;;)
118 {
119 netStatus = NetUserEnum(NULL, 20, FILTER_NORMAL_ACCOUNT,
120 (LPBYTE*)&pUserBuffer,
121 1024, &entriesread,
122 &totalentries, &resume_handle);
123 if (netStatus != NERR_Success && netStatus != ERROR_MORE_DATA)
124 break;
125
126 for (i = 0; i < entriesread; i++)
127 {
128 memset(&lvi, 0x00, sizeof(lvi));
129 lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
130 lvi.pszText = pUserBuffer[i].usri20_name;
131 lvi.state = 0;
132 lvi.iImage = (pUserBuffer[i].usri20_flags & UF_ACCOUNTDISABLE) ? 2 : 1;
133 iItem = ListView_InsertItem(hwndLV, &lvi);
134
135 ListView_SetItemText(hwndLV, iItem, 1,
136 pUserBuffer[i].usri20_full_name);
137
138 ListView_SetItemText(hwndLV, iItem, 2,
139 pUserBuffer[i].usri20_comment);
140 }
141
142 NetApiBufferFree(pUserBuffer);
143
144 /* No more data left */
145 if (netStatus != ERROR_MORE_DATA)
146 break;
147 }
148 }
149
150
151 static BOOL
152 AddSelectedUsersToGroup(HWND hwndDlg,
153 PGENERAL_GROUP_DATA pGroupData)
154 {
155 HWND hwndLV;
156 INT nSelectedItems;
157 INT nItem;
158 TCHAR szUserName[UNLEN];
159 BOOL bResult = FALSE;
160 LOCALGROUP_MEMBERS_INFO_3 memberInfo;
161 NET_API_STATUS status;
162
163 hwndLV = GetDlgItem(hwndDlg, IDC_USER_ADD_MEMBERSHIP_LIST);
164
165 nSelectedItems = ListView_GetSelectedCount(hwndLV);
166 if (nSelectedItems > 0)
167 {
168 nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
169 while (nItem != -1)
170 {
171 /* Get the new user name */
172 ListView_GetItemText(hwndLV,
173 nItem, 0,
174 szUserName,
175 UNLEN);
176
177 DebugPrintf(_TEXT("Selected user: %s"), szUserName);
178
179 memberInfo.lgrmi3_domainandname = szUserName;
180
181 status = NetLocalGroupAddMembers(NULL, pGroupData->szGroupName, 3,
182 (LPBYTE)&memberInfo, 1);
183 if (status != NERR_Success && status != ERROR_MEMBER_IN_ALIAS)
184 {
185 TCHAR szText[256];
186 wsprintf(szText, TEXT("Error: %u"), status);
187 MessageBox(NULL, szText, TEXT("NetLocalGroupAddMembers"), MB_ICONERROR | MB_OK);
188 }
189 else
190 {
191 bResult = TRUE;
192 }
193
194 nItem = ListView_GetNextItem(hwndLV, nItem, LVNI_SELECTED);
195 }
196 }
197
198 return bResult;
199 }
200
201
202 INT_PTR CALLBACK
203 AddUsersToGroupDlgProc(HWND hwndDlg,
204 UINT uMsg,
205 WPARAM wParam,
206 LPARAM lParam)
207 {
208 PGENERAL_GROUP_DATA pGroupData;
209
210 UNREFERENCED_PARAMETER(wParam);
211
212 pGroupData = (PGENERAL_GROUP_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
213
214 switch (uMsg)
215 {
216 case WM_INITDIALOG:
217 pGroupData = (PGENERAL_GROUP_DATA)lParam;
218 SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pGroupData);
219 InitGroupMembersList(hwndDlg, pGroupData);
220 break;
221
222 case WM_COMMAND:
223 switch (LOWORD(wParam))
224 {
225 case IDOK:
226 if (AddSelectedUsersToGroup(hwndDlg, pGroupData))
227 EndDialog(hwndDlg, IDOK);
228 else
229 EndDialog(hwndDlg, IDCANCEL);
230 break;
231
232 case IDCANCEL:
233 EndDialog(hwndDlg, IDCANCEL);
234 break;
235 }
236 break;
237
238 default:
239 return FALSE;
240 }
241
242 return TRUE;
243 }
244
245
246 static VOID
247 AddUsersToGroup(HWND hwndDlg,
248 PGENERAL_GROUP_DATA pGroupData)
249 {
250 HWND hwndLV;
251 // NET_API_STATUS status;
252 PLOCALGROUP_MEMBERS_INFO_1 membersInfo = NULL;
253 DWORD dwRead;
254 DWORD dwTotal;
255 DWORD_PTR resumeHandle = 0;
256 DWORD i;
257 LV_ITEM lvi;
258 TCHAR szGroupName[256];
259
260 if (DialogBoxParam(hApplet,
261 MAKEINTRESOURCE(IDD_USER_ADD_MEMBERSHIP),
262 hwndDlg,
263 AddUsersToGroupDlgProc,
264 (LPARAM)pGroupData) == IDOK)
265 {
266 hwndLV = GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_MEMBERS);
267
268 (void)ListView_DeleteAllItems(hwndLV);
269
270 // DebugPrintf(_T("Removed all users from the list!"));
271
272 /* Set group members */
273 NetLocalGroupGetMembers(NULL, pGroupData->szGroupName, 1, (LPBYTE*)&membersInfo,
274 MAX_PREFERRED_LENGTH, &dwRead, &dwTotal,
275 &resumeHandle);
276
277 for (i = 0; i < dwRead; i++)
278 {
279 ZeroMemory(&lvi, sizeof(lvi));
280 lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
281 lvi.pszText = membersInfo[i].lgrmi1_name;
282 lvi.state = 0;
283 lvi.iImage = (membersInfo[i].lgrmi1_sidusage == SidTypeGroup ||
284 membersInfo[i].lgrmi1_sidusage == SidTypeWellKnownGroup) ? 1 : 0;
285
286 if (membersInfo[i].lgrmi1_sidusage == SidTypeWellKnownGroup)
287 {
288 TCHAR szSid[256];
289
290 GetTextSid(membersInfo[i].lgrmi1_sid, szSid);
291
292 wsprintf(szGroupName,
293 TEXT("%s (%s)"),
294 membersInfo[i].lgrmi1_name,
295 szSid);
296
297 lvi.pszText = szGroupName;
298 }
299
300
301 (void)ListView_InsertItem(hwndLV, &lvi);
302 }
303
304 NetApiBufferFree(membersInfo);
305 }
306 }
307
308
309 static VOID
310 RemoveUserFromGroup(HWND hwndDlg,
311 PGENERAL_GROUP_DATA pGroupData)
312 {
313 TCHAR szUserName[UNLEN];
314 TCHAR szText[256];
315 LOCALGROUP_MEMBERS_INFO_3 memberInfo;
316 HWND hwndLV;
317 INT nItem;
318 NET_API_STATUS status;
319
320 hwndLV = GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_MEMBERS);
321 nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
322 if (nItem == -1)
323 return;
324
325 /* Get the new user name */
326 ListView_GetItemText(hwndLV,
327 nItem, 0,
328 szUserName,
329 UNLEN);
330
331 /* Display a warning message because the remove operation cannot be reverted */
332 wsprintf(szText, TEXT("Do you really want to remove the user \"%s\" from the group \"%s\"?"),
333 szUserName, pGroupData->szGroupName);
334 if (MessageBox(NULL, szText, TEXT("User Accounts"), MB_ICONWARNING | MB_YESNO) == IDNO)
335 return;
336
337 memberInfo.lgrmi3_domainandname = szUserName;
338
339 status = NetLocalGroupDelMembers(NULL, pGroupData->szGroupName,
340 3, (LPBYTE)&memberInfo, 1);
341 if (status != NERR_Success)
342 {
343 TCHAR szText[256];
344 wsprintf(szText, TEXT("Error: %u"), status);
345 MessageBox(NULL, szText, TEXT("NetLocalGroupDelMembers"), MB_ICONERROR | MB_OK);
346 return;
347 }
348
349 (void)ListView_DeleteItem(hwndLV, nItem);
350
351 if (ListView_GetItemCount(hwndLV) == 0)
352 EnableWindow(GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_REMOVE), FALSE);
353 }
354
355
356 static BOOL
357 OnNotify(HWND hwndDlg,
358 PGENERAL_GROUP_DATA pGroupData,
359 LPARAM lParam)
360 {
361 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
362
363 switch (((LPNMHDR)lParam)->idFrom)
364 {
365 case IDC_GROUP_GENERAL_MEMBERS:
366 switch (((LPNMHDR)lParam)->code)
367 {
368 case NM_CLICK:
369 EnableWindow(GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_REMOVE), (lpnmlv->iItem != -1));
370 break;
371
372 case LVN_KEYDOWN:
373 if (((LPNMLVKEYDOWN)lParam)->wVKey == VK_DELETE)
374 {
375 RemoveUserFromGroup(hwndDlg, pGroupData);
376 }
377 break;
378
379 }
380 break;
381 }
382
383 return FALSE;
384 }
385
386
387 static VOID
388 GetGeneralGroupData(HWND hwndDlg,
389 PGENERAL_GROUP_DATA pGroupData)
390 {
391 PLOCALGROUP_INFO_1 groupInfo = NULL;
392 PLOCALGROUP_MEMBERS_INFO_1 membersInfo = NULL;
393 DWORD dwRead;
394 DWORD dwTotal;
395 DWORD_PTR resumeHandle = 0;
396 DWORD i;
397 LV_ITEM lvi;
398 HWND hwndLV;
399 LV_COLUMN column;
400 RECT rect;
401 HIMAGELIST hImgList;
402 HICON hIcon;
403 TCHAR szGroupName[256];
404
405
406 hwndLV = GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_MEMBERS);
407
408 /* Create the image list */
409 hImgList = ImageList_Create(16, 16, ILC_COLOR8 | ILC_MASK, 5, 5);
410 hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_GROUP), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
411 ImageList_AddIcon(hImgList, hIcon);
412 DestroyIcon(hIcon);
413 hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_USER), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
414 ImageList_AddIcon(hImgList, hIcon);
415 DestroyIcon(hIcon);
416
417 (void)ListView_SetImageList(hwndLV, hImgList, LVSIL_SMALL);
418
419 /* Set the list column */
420 GetClientRect(hwndLV, &rect);
421
422 memset(&column, 0x00, sizeof(column));
423 column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
424 column.fmt = LVCFMT_LEFT;
425 column.cx = (INT)(rect.right - rect.left);
426 column.iSubItem = 0;
427 (void)ListView_InsertColumn(hwndLV, 0, &column);
428
429 /* Set group name */
430 SetDlgItemText(hwndDlg, IDC_GROUP_GENERAL_NAME, pGroupData->szGroupName);
431
432 /* Set group description */
433 NetLocalGroupGetInfo(NULL, pGroupData->szGroupName, 1, (LPBYTE*)&groupInfo);
434 SetDlgItemText(hwndDlg, IDC_GROUP_GENERAL_DESCRIPTION, groupInfo->lgrpi1_comment);
435 NetApiBufferFree(groupInfo);
436
437 /* Set group members */
438 NetLocalGroupGetMembers(NULL, pGroupData->szGroupName, 1, (LPBYTE*)&membersInfo,
439 MAX_PREFERRED_LENGTH, &dwRead, &dwTotal,
440 &resumeHandle);
441
442 for (i = 0; i < dwRead; i++)
443 {
444 ZeroMemory(&lvi, sizeof(lvi));
445 lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
446 lvi.pszText = membersInfo[i].lgrmi1_name;
447 lvi.state = 0;
448 if (membersInfo[i].lgrmi1_sidusage == SidTypeGroup ||
449 membersInfo[i].lgrmi1_sidusage == SidTypeWellKnownGroup)
450 {
451 lvi.iImage = 0;
452 }
453 else if (membersInfo[i].lgrmi1_sidusage == SidTypeUser)
454 {
455 /* FIXME: handle locked user properly! */
456 lvi.iImage = 1;
457 }
458
459 if (membersInfo[i].lgrmi1_sidusage == SidTypeWellKnownGroup)
460 {
461 TCHAR szSid[256];
462
463 GetTextSid(membersInfo[i].lgrmi1_sid, szSid);
464
465 wsprintf(szGroupName,
466 TEXT("%s (%s)"),
467 membersInfo[i].lgrmi1_name,
468 szSid);
469
470 lvi.pszText = szGroupName;
471 }
472
473 (void)ListView_InsertItem(hwndLV, &lvi);
474 }
475
476 NetApiBufferFree(membersInfo);
477 }
478
479
480 static BOOL
481 SetGeneralGroupData(HWND hwndDlg,
482 PGENERAL_GROUP_DATA pGroupData)
483 {
484 LOCALGROUP_INFO_1 groupInfo;
485 LPTSTR pszComment = NULL;
486 INT nLength;
487 NET_API_STATUS status;
488 DWORD dwIndex;
489
490 /* Get the group description */
491 nLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_DESCRIPTION));
492 if (nLength == 0)
493 {
494 groupInfo.lgrpi1_comment = NULL;
495 }
496 else
497 {
498 pszComment = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
499 GetDlgItemText(hwndDlg, IDC_GROUP_GENERAL_DESCRIPTION, pszComment, nLength + 1);
500 groupInfo.lgrpi1_comment = pszComment;
501 }
502
503 status = NetLocalGroupSetInfo(NULL, pGroupData->szGroupName, 1, (LPBYTE)&groupInfo, &dwIndex);
504 if (status != NERR_Success)
505 {
506 DebugPrintf(_T("Status: %lu Index: %lu"), status, dwIndex);
507 }
508
509 if (pszComment)
510 HeapFree(GetProcessHeap(), 0, pszComment);
511
512 return TRUE;
513 }
514
515
516 INT_PTR CALLBACK
517 GroupGeneralPageProc(HWND hwndDlg,
518 UINT uMsg,
519 WPARAM wParam,
520 LPARAM lParam)
521 {
522 PGENERAL_GROUP_DATA pGroupData;
523
524 UNREFERENCED_PARAMETER(lParam);
525 UNREFERENCED_PARAMETER(wParam);
526 UNREFERENCED_PARAMETER(hwndDlg);
527
528 pGroupData= (PGENERAL_GROUP_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
529
530 switch (uMsg)
531 {
532 case WM_INITDIALOG:
533 pGroupData = (PGENERAL_GROUP_DATA)HeapAlloc(GetProcessHeap(),
534 HEAP_ZERO_MEMORY,
535 sizeof(GENERAL_GROUP_DATA) +
536 lstrlen((LPTSTR)((PROPSHEETPAGE *)lParam)->lParam) * sizeof(TCHAR));
537 lstrcpy(pGroupData->szGroupName, (LPTSTR)((PROPSHEETPAGE *)lParam)->lParam);
538
539 SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pGroupData);
540
541 GetGeneralGroupData(hwndDlg,
542 pGroupData);
543 break;
544
545 case WM_COMMAND:
546 switch (LOWORD(wParam))
547 {
548 case IDC_GROUP_GENERAL_DESCRIPTION:
549 if (HIWORD(wParam) == EN_CHANGE)
550 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
551 break;
552
553 case IDC_GROUP_GENERAL_ADD:
554 AddUsersToGroup(hwndDlg, pGroupData);
555 break;
556
557 case IDC_GROUP_GENERAL_REMOVE:
558 RemoveUserFromGroup(hwndDlg, pGroupData);
559 break;
560 }
561 break;
562
563 case WM_NOTIFY:
564 if (((LPPSHNOTIFY)lParam)->hdr.code == PSN_APPLY)
565 {
566 SetGeneralGroupData(hwndDlg, pGroupData);
567 return TRUE;
568 }
569 else
570 {
571 return OnNotify(hwndDlg, pGroupData, lParam);
572 }
573 break;
574
575 case WM_DESTROY:
576 HeapFree(GetProcessHeap(), 0, pGroupData);
577 break;
578 }
579
580 return FALSE;
581 }
582
583
584 static VOID
585 InitPropSheetPage(PROPSHEETPAGE *psp, WORD idDlg, DLGPROC DlgProc, LPTSTR pszGroup)
586 {
587 ZeroMemory(psp, sizeof(PROPSHEETPAGE));
588 psp->dwSize = sizeof(PROPSHEETPAGE);
589 psp->dwFlags = PSP_DEFAULT;
590 psp->hInstance = hApplet;
591 psp->pszTemplate = MAKEINTRESOURCE(idDlg);
592 psp->pfnDlgProc = DlgProc;
593 psp->lParam = (LPARAM)pszGroup;
594 }
595
596
597 BOOL
598 GroupProperties(HWND hwndDlg)
599 {
600 PROPSHEETPAGE psp[1];
601 PROPSHEETHEADER psh;
602 TCHAR szGroupName[UNLEN];
603 INT nItem;
604 HWND hwndLV;
605
606 hwndLV = GetDlgItem(hwndDlg, IDC_GROUPS_LIST);
607 nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
608 if (nItem == -1)
609 return FALSE;
610
611 /* Get the new user name */
612 ListView_GetItemText(hwndLV,
613 nItem, 0,
614 szGroupName,
615 UNLEN);
616
617 ZeroMemory(&psh, sizeof(PROPSHEETHEADER));
618 psh.dwSize = sizeof(PROPSHEETHEADER);
619 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_PROPTITLE;
620 psh.hwndParent = hwndDlg;
621 psh.hInstance = hApplet;
622 psh.hIcon = NULL;
623 psh.pszCaption = szGroupName;
624 psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
625 psh.nStartPage = 0;
626 psh.ppsp = psp;
627
628 InitPropSheetPage(&psp[0], IDD_GROUP_GENERAL, (DLGPROC)GroupGeneralPageProc, szGroupName);
629
630 return (PropertySheet(&psh) == IDOK);
631 }