1 /* 2 * ReactOS kernel 3 * Copyright (C) 2003 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 /* 20 * COPYRIGHT: See COPYING in the top level directory 21 * PROJECT: ReactOS system libraries 22 * PURPOSE: Computer name functions 23 * FILE: dll/win32/kernel32/client/compname.c 24 * PROGRAMER: Eric Kohl 25 */ 26 27 /* INCLUDES ******************************************************************/ 28 29 #include <k32.h> 30 31 #define NDEBUG 32 #include <debug.h> 33 34 35 /* FUNCTIONS *****************************************************************/ 36 37 static 38 BOOL 39 GetComputerNameFromRegistry(LPWSTR RegistryKey, 40 LPWSTR ValueNameStr, 41 LPWSTR lpBuffer, 42 LPDWORD nSize) 43 { 44 PKEY_VALUE_PARTIAL_INFORMATION KeyInfo; 45 OBJECT_ATTRIBUTES ObjectAttributes; 46 UNICODE_STRING KeyName; 47 UNICODE_STRING ValueName; 48 HANDLE KeyHandle; 49 ULONG KeyInfoSize; 50 ULONG ReturnSize; 51 NTSTATUS Status; 52 53 if (lpBuffer != NULL && *nSize > 0) 54 lpBuffer[0] = 0; 55 56 RtlInitUnicodeString(&KeyName, RegistryKey); 57 InitializeObjectAttributes(&ObjectAttributes, 58 &KeyName, 59 OBJ_CASE_INSENSITIVE, 60 NULL, 61 NULL); 62 63 Status = NtOpenKey(&KeyHandle, 64 KEY_READ, 65 &ObjectAttributes); 66 if (!NT_SUCCESS(Status)) 67 { 68 BaseSetLastNTError (Status); 69 return FALSE; 70 } 71 72 KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + *nSize * sizeof(WCHAR); 73 KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, KeyInfoSize); 74 if (KeyInfo == NULL) 75 { 76 NtClose(KeyHandle); 77 SetLastError(ERROR_OUTOFMEMORY); 78 return FALSE; 79 } 80 81 RtlInitUnicodeString(&ValueName, ValueNameStr); 82 83 Status = NtQueryValueKey(KeyHandle, 84 &ValueName, 85 KeyValuePartialInformation, 86 KeyInfo, 87 KeyInfoSize, 88 &ReturnSize); 89 90 NtClose(KeyHandle); 91 92 if (!NT_SUCCESS(Status)) 93 { 94 *nSize = (ReturnSize - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) / sizeof(WCHAR); 95 goto failed; 96 } 97 98 if (KeyInfo->Type != REG_SZ) 99 { 100 Status = STATUS_UNSUCCESSFUL; 101 goto failed; 102 } 103 104 if (!lpBuffer || *nSize < (KeyInfo->DataLength / sizeof(WCHAR))) 105 { 106 *nSize = (ReturnSize - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) / sizeof(WCHAR); 107 Status = STATUS_BUFFER_OVERFLOW; 108 goto failed; 109 } 110 111 *nSize = KeyInfo->DataLength / sizeof(WCHAR) - 1; 112 RtlCopyMemory(lpBuffer, KeyInfo->Data, KeyInfo->DataLength); 113 lpBuffer[*nSize] = 0; 114 115 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo); 116 117 return TRUE; 118 119 failed: 120 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo); 121 BaseSetLastNTError(Status); 122 return FALSE; 123 } 124 125 126 static 127 BOOL 128 SetActiveComputerNameToRegistry(LPCWSTR RegistryKey, 129 LPCWSTR SubKey, 130 LPCWSTR ValueNameStr, 131 LPCWSTR lpBuffer) 132 { 133 OBJECT_ATTRIBUTES ObjectAttributes; 134 UNICODE_STRING KeyName; 135 UNICODE_STRING ValueName; 136 HANDLE KeyHandle, SubKeyHandle; 137 SIZE_T StringLength; 138 ULONG Disposition; 139 NTSTATUS Status; 140 141 StringLength = wcslen(lpBuffer); 142 if (StringLength > ((MAXULONG / sizeof(WCHAR)) - 1)) 143 { 144 return FALSE; 145 } 146 147 RtlInitUnicodeString(&KeyName, RegistryKey); 148 InitializeObjectAttributes(&ObjectAttributes, 149 &KeyName, 150 OBJ_CASE_INSENSITIVE, 151 NULL, 152 NULL); 153 154 Status = NtOpenKey(&KeyHandle, 155 KEY_WRITE, 156 &ObjectAttributes); 157 if (!NT_SUCCESS(Status)) 158 { 159 BaseSetLastNTError(Status); 160 return FALSE; 161 } 162 163 RtlInitUnicodeString(&KeyName, SubKey); 164 InitializeObjectAttributes(&ObjectAttributes, 165 &KeyName, 166 OBJ_CASE_INSENSITIVE, 167 KeyHandle, 168 NULL); 169 170 Status = NtCreateKey(&SubKeyHandle, 171 KEY_WRITE, 172 &ObjectAttributes, 173 0, 174 NULL, 175 REG_OPTION_VOLATILE, 176 &Disposition); 177 if (!NT_SUCCESS(Status)) 178 { 179 NtClose(KeyHandle); 180 BaseSetLastNTError(Status); 181 return FALSE; 182 } 183 184 RtlInitUnicodeString(&ValueName, ValueNameStr); 185 186 Status = NtSetValueKey(SubKeyHandle, 187 &ValueName, 188 0, 189 REG_SZ, 190 (PVOID)lpBuffer, 191 (StringLength + 1) * sizeof(WCHAR)); 192 if (!NT_SUCCESS(Status)) 193 { 194 NtClose(SubKeyHandle); 195 NtClose(KeyHandle); 196 BaseSetLastNTError(Status); 197 return FALSE; 198 } 199 200 NtFlushKey(SubKeyHandle); 201 NtClose(SubKeyHandle); 202 NtClose(KeyHandle); 203 204 return TRUE; 205 } 206 207 208 /* 209 * @implemented 210 */ 211 BOOL 212 WINAPI 213 GetComputerNameExW(COMPUTER_NAME_FORMAT NameType, 214 LPWSTR lpBuffer, 215 LPDWORD nSize) 216 { 217 UNICODE_STRING ResultString; 218 UNICODE_STRING DomainPart; 219 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 220 NTSTATUS Status; 221 BOOL ret = TRUE; 222 DWORD HostSize; 223 224 if ((nSize == NULL) || 225 (lpBuffer == NULL && *nSize > 0)) 226 { 227 SetLastError(ERROR_INVALID_PARAMETER); 228 return FALSE; 229 } 230 231 switch (NameType) 232 { 233 case ComputerNameNetBIOS: 234 ret = GetComputerNameFromRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 235 L"\\Control\\ComputerName\\ActiveComputerName", 236 L"ComputerName", 237 lpBuffer, 238 nSize); 239 if ((ret == FALSE) && 240 (GetLastError() != ERROR_MORE_DATA)) 241 { 242 ret = GetComputerNameFromRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 243 L"\\Control\\ComputerName\\ComputerName", 244 L"ComputerName", 245 lpBuffer, 246 nSize); 247 if (ret) 248 { 249 ret = SetActiveComputerNameToRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 250 L"\\Control\\ComputerName", 251 L"ActiveComputerName", 252 L"ComputerName", 253 lpBuffer); 254 } 255 } 256 return ret; 257 258 case ComputerNameDnsDomain: 259 return GetComputerNameFromRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 260 L"\\Services\\Tcpip\\Parameters", 261 L"Domain", 262 lpBuffer, 263 nSize); 264 265 case ComputerNameDnsFullyQualified: 266 ResultString.Length = 0; 267 ResultString.MaximumLength = (USHORT)*nSize * sizeof(WCHAR); 268 ResultString.Buffer = lpBuffer; 269 270 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 271 RtlInitUnicodeString(&DomainPart, NULL); 272 273 QueryTable[0].Name = L"HostName"; 274 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; 275 QueryTable[0].EntryContext = &DomainPart; 276 277 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 278 L"\\Registry\\Machine\\System" 279 L"\\CurrentControlSet\\Services\\Tcpip" 280 L"\\Parameters", 281 QueryTable, 282 NULL, 283 NULL); 284 285 if (NT_SUCCESS(Status)) 286 { 287 Status = RtlAppendUnicodeStringToString(&ResultString, &DomainPart); 288 HostSize = DomainPart.Length; 289 290 if (!NT_SUCCESS(Status)) 291 { 292 ret = FALSE; 293 } 294 295 RtlAppendUnicodeToString(&ResultString, L"."); 296 RtlFreeUnicodeString(&DomainPart); 297 298 RtlInitUnicodeString(&DomainPart, NULL); 299 QueryTable[0].Name = L"Domain"; 300 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; 301 QueryTable[0].EntryContext = &DomainPart; 302 303 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 304 L"\\Registry\\Machine\\System" 305 L"\\CurrentControlSet\\Services\\Tcpip" 306 L"\\Parameters", 307 QueryTable, 308 NULL, 309 NULL); 310 311 if (NT_SUCCESS(Status)) 312 { 313 Status = RtlAppendUnicodeStringToString(&ResultString, &DomainPart); 314 if ((!NT_SUCCESS(Status)) || (!ret)) 315 { 316 *nSize = HostSize + DomainPart.Length; 317 SetLastError(ERROR_MORE_DATA); 318 RtlFreeUnicodeString(&DomainPart); 319 return FALSE; 320 } 321 RtlFreeUnicodeString(&DomainPart); 322 *nSize = ResultString.Length / sizeof(WCHAR) - 1; 323 return TRUE; 324 } 325 } 326 return FALSE; 327 328 case ComputerNameDnsHostname: 329 return GetComputerNameFromRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 330 L"\\Services\\Tcpip\\Parameters", 331 L"Hostname", 332 lpBuffer, 333 nSize); 334 335 case ComputerNamePhysicalDnsDomain: 336 return GetComputerNameFromRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 337 L"\\Services\\Tcpip\\Parameters", 338 L"NV Domain", 339 lpBuffer, 340 nSize); 341 342 /* XXX Redo this */ 343 case ComputerNamePhysicalDnsFullyQualified: 344 return GetComputerNameExW(ComputerNameDnsFullyQualified, 345 lpBuffer, 346 nSize); 347 348 case ComputerNamePhysicalDnsHostname: 349 return GetComputerNameFromRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 350 L"\\Services\\Tcpip\\Parameters", 351 L"NV Hostname", 352 lpBuffer, 353 nSize); 354 355 /* XXX Redo this */ 356 case ComputerNamePhysicalNetBIOS: 357 return GetComputerNameExW(ComputerNameNetBIOS, 358 lpBuffer, 359 nSize); 360 361 case ComputerNameMax: 362 return FALSE; 363 } 364 365 return FALSE; 366 } 367 368 /* 369 * @implemented 370 */ 371 BOOL 372 WINAPI 373 GetComputerNameExA(COMPUTER_NAME_FORMAT NameType, 374 LPSTR lpBuffer, 375 LPDWORD nSize) 376 { 377 UNICODE_STRING UnicodeString; 378 ANSI_STRING AnsiString; 379 BOOL Result; 380 PWCHAR TempBuffer = NULL; 381 382 if ((nSize == NULL) || 383 (lpBuffer == NULL && *nSize > 0)) 384 { 385 SetLastError(ERROR_INVALID_PARAMETER); 386 return FALSE; 387 } 388 389 if (*nSize > 0) 390 { 391 TempBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, *nSize * sizeof(WCHAR)); 392 if (!TempBuffer) 393 { 394 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 395 return FALSE; 396 } 397 } 398 399 AnsiString.MaximumLength = (USHORT)*nSize; 400 AnsiString.Length = 0; 401 AnsiString.Buffer = lpBuffer; 402 403 Result = GetComputerNameExW(NameType, TempBuffer, nSize); 404 405 if (Result) 406 { 407 UnicodeString.MaximumLength = (USHORT)*nSize * sizeof(WCHAR) + sizeof(WCHAR); 408 UnicodeString.Length = (USHORT)*nSize * sizeof(WCHAR); 409 UnicodeString.Buffer = TempBuffer; 410 411 RtlUnicodeStringToAnsiString(&AnsiString, 412 &UnicodeString, 413 FALSE); 414 } 415 416 RtlFreeHeap(RtlGetProcessHeap(), 0, TempBuffer); 417 418 return Result; 419 } 420 421 /* 422 * @implemented 423 */ 424 BOOL 425 WINAPI 426 GetComputerNameA(LPSTR lpBuffer, LPDWORD lpnSize) 427 { 428 BOOL ret; 429 430 ret = GetComputerNameExA(ComputerNameNetBIOS, lpBuffer, lpnSize); 431 if (!ret && GetLastError() == ERROR_MORE_DATA) 432 SetLastError(ERROR_BUFFER_OVERFLOW); 433 434 return ret; 435 } 436 437 438 /* 439 * @implemented 440 */ 441 BOOL 442 WINAPI 443 GetComputerNameW(LPWSTR lpBuffer, LPDWORD lpnSize) 444 { 445 BOOL ret; 446 447 ret = GetComputerNameExW(ComputerNameNetBIOS, lpBuffer, lpnSize); 448 if (!ret && GetLastError() == ERROR_MORE_DATA) 449 SetLastError(ERROR_BUFFER_OVERFLOW); 450 451 return ret; 452 } 453 454 455 /* 456 * @implemented 457 */ 458 static 459 BOOL 460 IsValidComputerName(COMPUTER_NAME_FORMAT NameType, 461 LPCWSTR lpComputerName) 462 { 463 PWCHAR p; 464 ULONG Length; 465 466 /* FIXME: do verification according to NameType */ 467 468 Length = 0; 469 p = (PWCHAR)lpComputerName; 470 471 while (*p != 0) 472 { 473 if (!(iswctype(*p, _ALPHA | _DIGIT) || *p == L'!' || *p == L'@' || *p == L'#' || 474 *p == L'$' || *p == L'%' || *p == L'^' || *p == L'&' || *p == L'\'' || 475 *p == L')' || *p == L'(' || *p == L'.' || *p == L'-' || *p == L'_' || 476 *p == L'{' || *p == L'}' || *p == L'~')) 477 return FALSE; 478 479 Length++; 480 p++; 481 } 482 483 if (NameType == ComputerNamePhysicalDnsDomain) 484 return TRUE; 485 486 if (Length == 0) 487 return FALSE; 488 489 if (NameType == ComputerNamePhysicalNetBIOS && 490 Length > MAX_COMPUTERNAME_LENGTH) 491 return FALSE; 492 493 return TRUE; 494 } 495 496 497 static 498 BOOL 499 SetComputerNameToRegistry(LPCWSTR RegistryKey, 500 LPCWSTR ValueNameStr, 501 LPCWSTR lpBuffer) 502 { 503 OBJECT_ATTRIBUTES ObjectAttributes; 504 UNICODE_STRING KeyName; 505 UNICODE_STRING ValueName; 506 HANDLE KeyHandle; 507 SIZE_T StringLength; 508 NTSTATUS Status; 509 510 StringLength = wcslen(lpBuffer); 511 if (StringLength > ((MAXULONG / sizeof(WCHAR)) - 1)) 512 { 513 return FALSE; 514 } 515 516 RtlInitUnicodeString(&KeyName, RegistryKey); 517 InitializeObjectAttributes(&ObjectAttributes, 518 &KeyName, 519 OBJ_CASE_INSENSITIVE, 520 NULL, 521 NULL); 522 523 Status = NtOpenKey(&KeyHandle, 524 KEY_WRITE, 525 &ObjectAttributes); 526 if (!NT_SUCCESS(Status)) 527 { 528 BaseSetLastNTError(Status); 529 return FALSE; 530 } 531 532 RtlInitUnicodeString(&ValueName, ValueNameStr); 533 534 Status = NtSetValueKey(KeyHandle, 535 &ValueName, 536 0, 537 REG_SZ, 538 (PVOID)lpBuffer, 539 (StringLength + 1) * sizeof(WCHAR)); 540 if (!NT_SUCCESS(Status)) 541 { 542 NtClose(KeyHandle); 543 BaseSetLastNTError(Status); 544 return FALSE; 545 } 546 547 NtFlushKey(KeyHandle); 548 NtClose(KeyHandle); 549 550 return TRUE; 551 } 552 553 554 /* 555 * @implemented 556 */ 557 BOOL 558 WINAPI 559 SetComputerNameA(LPCSTR lpComputerName) 560 { 561 return SetComputerNameExA(ComputerNamePhysicalNetBIOS, lpComputerName); 562 } 563 564 565 /* 566 * @implemented 567 */ 568 BOOL 569 WINAPI 570 SetComputerNameW(LPCWSTR lpComputerName) 571 { 572 return SetComputerNameExW(ComputerNamePhysicalNetBIOS, lpComputerName); 573 } 574 575 576 /* 577 * @implemented 578 */ 579 BOOL 580 WINAPI 581 SetComputerNameExA(COMPUTER_NAME_FORMAT NameType, 582 LPCSTR lpBuffer) 583 { 584 UNICODE_STRING Buffer; 585 BOOL bResult; 586 587 RtlCreateUnicodeStringFromAsciiz(&Buffer, (LPSTR)lpBuffer); 588 589 bResult = SetComputerNameExW(NameType, Buffer.Buffer); 590 591 RtlFreeUnicodeString(&Buffer); 592 593 return bResult; 594 } 595 596 597 /* 598 * @implemented 599 */ 600 BOOL 601 WINAPI 602 SetComputerNameExW(COMPUTER_NAME_FORMAT NameType, 603 LPCWSTR lpBuffer) 604 { 605 BOOL ret1, ret2; 606 607 if (!IsValidComputerName(NameType, lpBuffer)) 608 { 609 SetLastError(ERROR_INVALID_PARAMETER); 610 return FALSE; 611 } 612 613 switch (NameType) 614 { 615 case ComputerNamePhysicalDnsDomain: 616 return SetComputerNameToRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 617 L"\\Services\\Tcpip\\Parameters", 618 L"NV Domain", 619 lpBuffer); 620 621 case ComputerNamePhysicalDnsHostname: 622 ret1 = SetComputerNameToRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 623 L"\\Services\\Tcpip\\Parameters", 624 L"NV Hostname", 625 lpBuffer); 626 627 ret2 = SetComputerNameToRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 628 L"\\Control\\ComputerName\\ComputerName", 629 L"ComputerName", 630 lpBuffer); 631 return (ret1 && ret2); 632 633 case ComputerNamePhysicalNetBIOS: 634 return SetComputerNameToRegistry(L"\\Registry\\Machine\\System\\CurrentControlSet" 635 L"\\Control\\ComputerName\\ComputerName", 636 L"ComputerName", 637 lpBuffer); 638 639 default: 640 SetLastError (ERROR_INVALID_PARAMETER); 641 return FALSE; 642 } 643 } 644 645 646 /* 647 * @implemented 648 */ 649 BOOL 650 WINAPI 651 DnsHostnameToComputerNameA(LPCSTR Hostname, 652 LPSTR ComputerName, 653 LPDWORD nSize) 654 { 655 DWORD len; 656 657 DPRINT("(%s, %p, %p)\n", Hostname, ComputerName, nSize); 658 659 if (!Hostname || !nSize) 660 return FALSE; 661 662 len = lstrlenA(Hostname); 663 664 if (len > MAX_COMPUTERNAME_LENGTH) 665 len = MAX_COMPUTERNAME_LENGTH; 666 667 if (*nSize < len) 668 { 669 *nSize = len; 670 return FALSE; 671 } 672 673 if (!ComputerName) return FALSE; 674 675 memcpy(ComputerName, Hostname, len); 676 ComputerName[len + 1] = 0; 677 return TRUE; 678 } 679 680 681 /* 682 * @implemented 683 */ 684 BOOL 685 WINAPI 686 DnsHostnameToComputerNameW(LPCWSTR hostname, 687 LPWSTR computername, 688 LPDWORD size) 689 { 690 DWORD len; 691 692 DPRINT("(%s, %p, %p): stub\n", hostname, computername, size); 693 694 if (!hostname || !size) return FALSE; 695 len = lstrlenW(hostname); 696 697 if (len > MAX_COMPUTERNAME_LENGTH) 698 len = MAX_COMPUTERNAME_LENGTH; 699 700 if (*size < len) 701 { 702 *size = len; 703 return FALSE; 704 } 705 if (!computername) return FALSE; 706 707 memcpy(computername, hostname, len * sizeof(WCHAR)); 708 computername[len + 1] = 0; 709 return TRUE; 710 } 711 712 DWORD 713 WINAPI 714 AddLocalAlternateComputerNameA(LPSTR lpName, PNTSTATUS Status) 715 { 716 STUB; 717 return 0; 718 } 719 720 DWORD 721 WINAPI 722 AddLocalAlternateComputerNameW(LPWSTR lpName, PNTSTATUS Status) 723 { 724 STUB; 725 return 0; 726 } 727 728 DWORD 729 WINAPI 730 EnumerateLocalComputerNamesA(PVOID pUnknown, DWORD Size, LPSTR lpBuffer, LPDWORD lpnSize) 731 { 732 STUB; 733 return ERROR_CALL_NOT_IMPLEMENTED; 734 } 735 736 DWORD 737 WINAPI 738 EnumerateLocalComputerNamesW(PVOID pUnknown, DWORD Size, LPWSTR lpBuffer, LPDWORD lpnSize) 739 { 740 STUB; 741 return ERROR_CALL_NOT_IMPLEMENTED; 742 } 743 744 DWORD 745 WINAPI 746 RemoveLocalAlternateComputerNameA(LPSTR lpName, DWORD Unknown) 747 { 748 STUB; 749 return ERROR_CALL_NOT_IMPLEMENTED; 750 } 751 752 DWORD 753 WINAPI 754 RemoveLocalAlternateComputerNameW(LPWSTR lpName, DWORD Unknown) 755 { 756 STUB; 757 return ERROR_CALL_NOT_IMPLEMENTED; 758 } 759 760 /* 761 * @unimplemented 762 */ 763 BOOL 764 WINAPI 765 SetLocalPrimaryComputerNameA(IN DWORD Unknown1, 766 IN DWORD Unknown2) 767 { 768 STUB; 769 return FALSE; 770 } 771 772 /* 773 * @unimplemented 774 */ 775 BOOL 776 WINAPI 777 SetLocalPrimaryComputerNameW(IN DWORD Unknown1, 778 IN DWORD Unknown2) 779 { 780 STUB; 781 return FALSE; 782 } 783 784 785 /* EOF */ 786