home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 May / W2KPRK.iso / compmgmt.cab / Chkusers.vbs < prev    next >
Text File  |  1999-11-04  |  61KB  |  1,462 lines

  1.  
  2. '********************************************************************
  3. '*
  4. '* File:        CHKUSERS.VBS
  5. '* Created:     August 1998
  6. '* Version:     1.0
  7. '*
  8. '* Main Function: Checks a domain for users satisfying a given criteria.
  9. '* Usage: CHKUSERS.VBS </A:adspath | /I:inputfile> [/P:properties] /C:criteria
  10. '*        [/O:outputfile] [/U:username] [/W:password] [/Q] [/M] [/NQ]
  11. '* Note: A brief description of how the code works can be found at the end of the file.
  12. '*
  13. '* Copyright (C) 1998 Microsoft Corporation
  14. '*
  15. '********************************************************************
  16.  
  17. OPTION EXPLICIT
  18. ON ERROR RESUME NEXT
  19.  
  20. 'Define constants
  21. CONST CONST_ERROR                   = 0
  22. CONST CONST_WSCRIPT                 = 1
  23. CONST CONST_CSCRIPT                 = 2
  24. CONST CONST_SHOW_USAGE              = 3
  25. CONST CONST_PROCEED                 = 4
  26. CONST CONST_FAILED                  = -2
  27.  
  28. 'Declare variables
  29. Dim strADsPath, strCriteria, strUserName, strPassword
  30. Dim strInputFile, strOutputFile, blnMultiFiles, blnQuestion, intOpMode, i
  31. ReDim strArgumentArray(0), strProperties(0), strPropertyValues(0)
  32. ReDim strOperators(0), strPropertiesOut(0)
  33.  
  34. 'Initialize variables
  35. strADsPath = ""
  36. strCriteria = ""
  37. strUserName = ""
  38. strPassword = ""
  39. strInputFile = ""
  40. strOutputFile = ""
  41. blnMultiFiles = False
  42. blnQuestion = True
  43. strArgumentArray(0) = ""
  44. strProperties(0) = ""
  45. strPropertyValues(0) = ""
  46. strOperators(0) = ""
  47. strPropertiesOut(0) = "ADsPath"        'Default
  48. intOpMode = 0
  49. i = 0
  50.  
  51. 'Get the command line arguments
  52. For i = 0 to Wscript.arguments.count - 1
  53.     ReDim Preserve strArgumentArray(i)
  54.     strArgumentArray(i) = Wscript.arguments.item(i)
  55. Next
  56.  
  57. 'Check whether the script is run using CScript
  58. Select Case intChkProgram()
  59.     Case CONST_CSCRIPT
  60.         'Do Nothing
  61.     Case CONST_WSCRIPT
  62.         WScript.Echo "Please run this script using CScript." & vbCRLF & _
  63.             "This can be achieved by" & vbCRLF & _
  64.             "1. Using ""CScript CHKUSERS.vbs arguments"" for Windows 95/98 or" & vbCRLF & _
  65.             "2. Changing the default Windows Scripting Host setting to CScript" & vbCRLF & _
  66.             "    using ""CScript //H:CScript //S"" and running the script using" & vbCRLF & _
  67.             "    ""CHKUSERS.vbs arguments"" for Windows NT."
  68.         WScript.Quit
  69.     Case Else
  70.         WScript.Quit
  71. End Select
  72.  
  73. 'Parse the command line
  74. intOpMode = intParseCmdLine(strArgumentArray, strADsPath, strCriteria, blnMultiFiles, _
  75.             strProperties, strPropertyValues, strOperators, strPropertiesOut, _
  76.             strUserName, strPassword, strInputFile, strOutputFile, blnQuestion)
  77. If Err.Number then
  78.     Print "Error 0x" & CStr(Hex(Err.Number)) & " occurred in parsing the command line."
  79.     If Err.Description <> "" Then
  80.         Print "Error description: " & Err.Description & "."
  81.     End If
  82.     WScript.Quit
  83. End If
  84.  
  85. Select Case intOpMode
  86.     Case CONST_SHOW_USAGE
  87.         call ShowUsage()
  88.     Case CONST_PROCEED
  89.         'First we need to conver the datatype of strPropertyValues.
  90.         Call ConvertPropertyValues(strProperties, strPropertyValues)
  91.         'Now we can call ChkUsers to do the rest of the job.
  92.         Call ChkUsers(strADsPath, strCriteria, blnMultiFiles, strProperties, _
  93.              strPropertyValues, strOperators, strPropertiesOut, strUserName, _
  94.              strPassword, strInputFile, strOutputFile, blnQuestion)
  95.     Case CONST_ERROR
  96.         'Do nothing.
  97.     Case Else                    'Default -- should never happen
  98.         Print "Error occurred in passing parameters."
  99. End Select
  100.  
  101. '********************************************************************
  102. '*
  103. '* Function intChkProgram()
  104. '* Purpose: Determines which program is used to run this script.
  105. '* Input:   None
  106. '* Output:  intChkProgram is set to one of CONST_ERROR, CONST_WSCRIPT,
  107. '*          and CONST_CSCRIPT.
  108. '*
  109. '********************************************************************
  110.  
  111. Private Function intChkProgram()
  112.  
  113.     ON ERROR RESUME NEXT
  114.  
  115.     Dim strFullName, strCommand, i, j
  116.  
  117.     'strFullName should be something like C:\WINDOWS\COMMAND\CSCRIPT.EXE
  118.     strFullName = WScript.FullName
  119.     If Err.Number then
  120.         Print "Error 0x" & CStr(Hex(Err.Number)) & " occurred."
  121.         If Err.Description <> "" Then
  122.             Print "Error description: " & Err.Description & "."
  123.         End If
  124.         intChkProgram =  CONST_ERROR
  125.         Exit Function
  126.     End If
  127.  
  128.     i = InStr(1, strFullName, ".exe", 1)
  129.     If i = 0 Then
  130.         intChkProgram =  CONST_ERROR
  131.         Exit Function
  132.     Else
  133.         j = InStrRev(strFullName, "\", i, 1)
  134.         If j = 0 Then
  135.             intChkProgram =  CONST_ERROR
  136.             Exit Function
  137.         Else
  138.             strCommand = Mid(strFullName, j+1, i-j-1)
  139.             Select Case LCase(strCommand)
  140.                 Case "cscript"
  141.                     intChkProgram = CONST_CSCRIPT
  142.                 Case "wscript"
  143.                     intChkProgram = CONST_WSCRIPT
  144.                 Case Else       'should never happen
  145.                     Print "An unexpected program is used to run this script."
  146.                     Print "Only CScript.Exe or WScript.Exe can be used to run this script."
  147.                     intChkProgram = CONST_ERROR
  148.             End Select
  149.         End If
  150.     End If
  151.  
  152. End Function
  153.  
  154. '********************************************************************
  155. '*
  156. '* Function intParseCmdLine()
  157. '* Purpose: Parses the command line.
  158. '* Input:   strArgumentArray    an array containing input from the command line
  159. '* Output:  strADsPath          ADsPath of a domain
  160. '*          strCriteria         the search criteria with each comparison replaced by
  161. '*                              a corresponding index
  162. '*          blnMultiFiles       specifies whether to save results to multiple files
  163. '*          strProperties       an array containing names of user properties to be checked
  164. '*          strPropertyValues   an array containing a set of target values of user properties
  165. '*          strOperators        a string array containing comparison operators,
  166. '*                              including >, < and =
  167. '*          strPropertiesOut    an array containing names of user properties to be retrieved
  168. '*          strUserName         name of the current user
  169. '*          strPassword         password of the current user
  170. '*          strInputFile        an input file name
  171. '*          strOutputFile       an output file name
  172. '*          blnQuestion         specifies whether to use message box to get info
  173. '*          intParseCmdLine     is set to one of CONST_ERROR, CONST_SHOW_USAGE, CONST_PROCEED.
  174. '*
  175. '********************************************************************
  176.  
  177. Private Function intParseCmdLine(strArgumentArray, strADsPath, strCriteria, blnMultiFiles, _
  178.     strProperties, strPropertyValues, strOperators, strPropertiesOut, _
  179.     strUserName, strPassword, strInputFile, strOutputFile, blnQuestion)
  180.  
  181.     ON ERROR RESUME NEXT
  182.  
  183.     Dim i, j, k, intUBound, strFlag
  184.  
  185.     strFlag = strArgumentArray(0)
  186.  
  187.     If strFlag = "" then                'No arguments have been received
  188.         Print "Arguments are required."
  189.         intParseCmdLine = CONST_ERROR
  190.         Exit Function
  191.     End If
  192.  
  193.     If (strFlag="help") OR (strFlag="/h") OR (strFlag="\h") OR (strFlag="-h") _
  194.         OR (strFlag = "\?") OR (strFlag = "/?") OR (strFlag = "?") OR (strFlag="h") Then
  195.         intParseCmdLine = CONST_SHOW_USAGE
  196.         Exit Function
  197.     End If
  198.  
  199.     'Get strADsPath, strUserName, strPassword, strOutputFile from the input.
  200.     intUBound = UBound(strArgumentArray)
  201.     For i = 0 To intUBound
  202.         strFlag = LCase(Left(strArgumentArray(i), InStr(1, strArgumentArray(i), ":")-1))
  203.         If Err.Number Then            'An error occurs if there is no : in the string
  204.             Err.Clear
  205.             Select Case LCase(strArgumentArray(i))
  206.                 Case "/m"
  207.                     blnMultiFiles = True
  208.                 Case "/nq"
  209.                     blnQuestion = False     'No input box. Answer Yes to it.
  210.                 Case Else
  211.                     Print "Invalid flag " & strArgumentArray(i) & "."
  212.                     Print "Please check the input and try again."
  213.                     intParseCmdLine = CONST_ERROR
  214.                     Exit Function
  215.             End Select
  216.         Else
  217.             Select Case strFlag
  218.                 Case "/a"
  219.                     strADsPath = FormatProvider(Right(strArgumentArray(i), Len(strArgumentArray(i))-3))
  220.                 Case "/p"
  221.                     j = 0
  222.                     strArgumentArray(i) = Right(strArgumentArray(i), _
  223.                         Len(strArgumentArray(i))-3)
  224.                     Do
  225.                         k = InStr(1, strArgumentArray(i), ";")
  226.                         If k Then
  227.                             ReDim Preserve strPropertiesOut(j)
  228.                             strPropertiesOut(j) = Trim(Left(strArgumentArray(i), k-1))
  229.                             strArgumentArray(i) = Trim(Right(strArgumentArray(i), _
  230.                                 Len(strArgumentArray(i))-k))
  231.                             j = j + 1
  232.                         End If
  233.                     Loop Until k = 0
  234.                     ReDim Preserve strPropertiesOut(j)
  235.                     strPropertiesOut(j) = strArgumentArray(i)
  236.                 Case "/i"
  237.                     strInputFile = Right(strArgumentArray(i), Len(strArgumentArray(i))-3)
  238.                 Case "/o"
  239.                     strOutputFile = Right(strArgumentArray(i), Len(strArgumentArray(i))-3)
  240.                 Case "/u"
  241.                     strUserName = Right(strArgumentArray(i), Len(strArgumentArray(i))-3)
  242.                 Case "/w"
  243.                     strPassword = Right(strArgumentArray(i), Len(strArgumentArray(i))-3)
  244.                 Case "/c"
  245.                     'Preserve the criteria for later treatment.
  246.                     strCriteria = Right(strArgumentArray(i), Len(strArgumentArray(i))-3)
  247.                 Case Else
  248.                     Print "Invalid flag " & strFlag & "."
  249.                     Print "Please check the input and try again."
  250.                     intParseCmdLine = CONST_ERROR
  251.                     Exit Function
  252.             End Select
  253.         End If
  254.     Next
  255.  
  256.     intParseCmdLine = CONST_PROCEED
  257.  
  258.     'Check whether strCriteria is empty.
  259.     If Trim(strCriteria) = "" Then
  260.         Print "Please enter a criteria."
  261.         intParseCmdLine = CONST_ERROR
  262.         Exit Function
  263.     Else
  264.         'Get strProperties, strPropertyValues, strOperators from the criteria.
  265.         If blnParseCriteria(strCriteria, strProperties, _
  266.             strPropertyValues, strOperators) Then
  267.             Print "An error occurred in parsing the criteria."
  268.             Print "Please check the syntax and try again."
  269.             intParseCmdLine = CONST_ERROR
  270.             Exit Function
  271.         End If
  272.         'Check whether strCriteria is empty now.
  273.         If Trim(strCriteria) = "" Then
  274.             Print "Please enter a criteria."
  275.             intParseCmdLine = CONST_ERROR
  276.         Else
  277.             'Check whether the syntx is correct.
  278.             i = intEvalCriteria(strCriteria)
  279.             If i = CONST_FAILED Then
  280.                 Print "Please check the syntax and try again."
  281.                 intParseCmdLine = CONST_ERROR
  282.             End If
  283.         End If
  284.     End If
  285.  
  286.     'The ADsPath is required.
  287.     If strADsPath = "" and strInputFile = "" Then
  288.         Print "Please enter either an ADsPath or a file name."
  289.         intParseCmdLine = CONST_ERROR
  290.     End If
  291.  
  292. End Function
  293.  
  294. '********************************************************************
  295. '*
  296. '* Sub ShowUsage()
  297. '* Purpose: Shows the correct usage to the user.
  298. '* Input:   None
  299. '* Output:  Help messages are displayed on screen.
  300. '*
  301. '********************************************************************
  302.  
  303. Sub ShowUsage()
  304.  
  305.     Wscript.Echo ""
  306.     Wscript.Echo "Checks a domain for users satisfying a given criteria." & vbCRLF
  307.     Wscript.Echo "CHKUSERS.VBS </A:adspath | /I:inputfile> [/P:properties] /C:criteria "
  308.     Wscript.Echo "             [/O:outputfile] [/U:username] [/W:password] [/M] [/NQ]"
  309.     Wscript.echo "              /A, /I, /P, /C, /U, /W, /O" & vbCRLF
  310.     Wscript.Echo "Parameter specifiers:"
  311.     Wscript.echo "   adspath       ADsPath of a user object container."
  312.     Wscript.Echo "   inputfile     A file containing ADsPaths of domains."
  313.     Wscript.Echo "                 It can be used to check many domains at once."
  314.     Wscript.Echo "   properties    Properties to be retrieved."
  315.     Wscript.Echo "   criteria      Specifies what kind of users to look for."
  316.     Wscript.Echo "   outputfile    The output file name."
  317.     Wscript.echo "   username      Username of the current user."
  318.     Wscript.echo "   password      Password of the current user."
  319.     Wscript.Echo "   /M            Specifies that the output is written to multiple"
  320.     Wscript.Echo "                 files to be created in the script."
  321.     Wscript.Echo "   /NQ           Specifies files named Users* under the current"
  322.     Wscript.Echo "                 directory be deleted without poping up a MsgBox."
  323.     Wscript.Echo ""
  324.     Wscript.Echo "EXAMPLE:"
  325.     Wscript.Echo "   CHKUSERS.VBS /A:WinNT://FooFoo /P:FullName;Description"
  326.     Wscript.Echo "   /C:""((LastLogin:>4/3/98 or LastLogin:<8/4/98)" _
  327.                & " and AccountDisabled:=False)""" & vbCRLF
  328.     Wscript.Echo "   gets the FullName and Description of all active users whose"
  329.     Wscript.Echo "   last login is between 4/10/98 and 8/4/98." & vbCRLF
  330.     Wscript.Echo "NOTES:"
  331.     Wscript.Echo "1. The property name and the operator in the criteria must be"
  332.     Wscript.Echo "   separated by a colon."
  333.     Wscript.Echo "2. The criteria and any string including spaces must be "
  334.     Wscript.Echo "   enclosed in quotes."
  335.     Wscript.Echo "3. Any string within the criteria including spaces must be"
  336.     Wscript.Echo "   enclosed in single quotes."
  337.  
  338. End Sub
  339.  
  340. '********************************************************************
  341. '*
  342. '* Function blnParseCriteria()
  343. '* Purpose: Gets strProperties, strPropertyValues, strOperators from the criteria.
  344. '* Input:   strCriteria         the search criteria
  345. '* Output:  strCriteria         the search criteria with each comparison replaced by
  346. '*                              a corresponding index
  347. '*          strProperties       an array containing names of user properties to be checked
  348. '*          strPropertyValues   an array containing a set of target values of user properties
  349. '*          strOperators        a string array containing comparison operators,
  350. '*                              including >, < and =
  351. '*
  352. '********************************************************************
  353.  
  354. Private Function blnParseCriteria(strCriteria, strProperties, _
  355.     strPropertyValues, strOperators)
  356.  
  357.     ON ERROR RESUME NEXT
  358.  
  359.     Dim i, j, intColon, intQuote, intSpace, intBracket, strLeft, strRight, strTemp
  360.  
  361.     blnParseCriteria = False     'No error.
  362.     strTemp = ""
  363.     j = 0
  364.  
  365.     If strCriteria = "" Then
  366.         Print "Please enter a criteria."
  367.         blnParseCriteria = True            'An error.
  368.         Exit Function
  369.     End If
  370.     intColon = InStr(1, strCriteria, ":")
  371.     'Replace each comparison(including a property name, a value, and an operator)
  372.     'with value of j and read property name, value and operators into corresponding arrays.
  373.     Do While intColon    'If there is a : in the criteria
  374.         If intColon = 1 Then    'This must be an error
  375.             blnParseCriteria = True
  376.             Exit Function
  377.         End If
  378.         ReDim Preserve strProperties(j), strPropertyValues(j), strOperators(j)
  379.         strLeft = Trim(Left(strCriteria, intColon-1))
  380.         strRight = Trim(Right(strCriteria, Len(strCriteria)-intColon))
  381.         If strLeft = "" or strRight = "" Then
  382.             Print "A property name or property value is missing."
  383.             blnParseCriteria = True
  384.             Exit Function
  385.         End If
  386.  
  387.         'Now treat the left side.
  388.         intBracket = InStrRev(strLeft, "(")
  389.         intSpace = InStrRev(strLeft, " ")        'The first appearance of a space.
  390.         If intSpace Then
  391.             If intBracket and intBracket > intSpace    Then
  392.                 'Then strProperties(j) is down to the bracket.
  393.                 strProperties(j) = Trim(Right(strLeft, Len(strLeft)-intBracket))
  394.                 strTemp = strTemp & Left(strLeft, intBracket) & j & " "
  395.             Else     'strProperties(j) is down to the space.
  396.                 strProperties(j) = Right(strLeft, Len(strLeft)-intSpace)
  397.                 strTemp = strTemp & Left(strLeft, intSpace) & j & " "
  398.             End If
  399.         Else    'If there is no space in strLeft
  400.             If intBracket Then
  401.                 strProperties(j) = Trim(Right(strLeft, Len(strLeft)-intBracket))
  402.                 strTemp = strTemp & Left(strLeft, intBracket) & j & " "
  403.             Else        'There is no space nor bracket.
  404.                 strProperties(j) = strLeft
  405.                 strTemp = strTemp & j & " "
  406.             End If
  407.         End If
  408.  
  409.         'Now treat the right side
  410.         intQuote = InStr(strRight, "'")        'The first appearance of '.
  411.         intSpace = InStr(strRight, " ")        'The first appearance of a space.
  412.         intBracket = InStr(strRight, ")")        'The first appearance of a ).
  413.         If intSpace Then    'If there is a space in strRight
  414.             'If there is a ' in the left most part of strRight then
  415.             'strPropertyValues(j) should be up to the next '.
  416.             If intQuote and intSpace > intQuote Then
  417.                 'Get the position of the next '.
  418.                 intQuote = InStr(intQuote+1, strRight, "'")
  419.                 'It is an error to have a bracket between two single quotes.
  420.                 If intBracket and intQuote > intBracket Then
  421.                     Print "A bracket is misplaced."
  422.                     blnParseCriteria = True
  423.                     Exit Function
  424.                 End If
  425.                 strPropertyValues(j) = Trim(Left(strRight, intQuote-1))
  426.                 'Get rid of the first '.
  427.                 strPropertyValues(j) = Replace(strPropertyValues(j), "'", "")
  428.                 strCriteria = Trim(Right(strRight, Len(strRight)-intQuote))
  429.             Else
  430.                 'If the left most string ends up with a bracket,
  431.                 'strPropertyValues(j) should be up to the bracket.
  432.                 If intBracket and intSpace > intBracket Then
  433.                     strPropertyValues(j) = Trim(Left(strRight, intBracket-1))
  434.                     strCriteria = Trim(Right(strRight, Len(strRight)-intBracket+1))
  435.                 Else        'strPropertyValues(j) should be up to the space.
  436.                     strPropertyValues(j) = Left(strRight, intSpace-1)
  437.                     strCriteria = Trim(Right(strRight, Len(strRight)-intSpace+1))
  438.                 End If
  439.             End If
  440.         Else    'If there is no space in strRight
  441.             If intQuote Then
  442.                 Print "A single quote is misplaced."
  443.                 blnParseCriteria = True
  444.                 Exit Function
  445.             End If
  446.             If intBracket Then
  447.                 strPropertyValues(j) = Trim(Left(strRight, intBracket-1))
  448.                 strCriteria = Trim(Right(strRight, Len(strRight)-intBracket+1))
  449.             Else    'If there is no bracket then strPropertyValues(j) should be up to the end.
  450.                 strPropertyValues(j) = strRight
  451.                 strCriteria = ""
  452.             End If
  453.         End If
  454.  
  455.         'Now take care of the operator in strPropertyValues
  456.         Select Case LCase(Left(strPropertyValues(j),1))
  457.             Case ">"
  458.                 strOperators(j) = ">"
  459.                 strPropertyValues(j) = Right(strPropertyValues(j), Len(strPropertyValues(j))-1)
  460.             Case "<"
  461.                 strOperators(j) = "<"
  462.                 strPropertyValues(j) = Right(strPropertyValues(j), Len(strPropertyValues(j))-1)
  463.             Case "="
  464.                 strOperators(j) = "="
  465.                 strPropertyValues(j) = Right(strPropertyValues(j), Len(strPropertyValues(j))-1)
  466.             Case Else    'Assume that an operator has been omitted.
  467.                 strOperators(j) = "="
  468.         End Select
  469.  
  470.         If strPropertyValues(j) = "" Then
  471.             Print "Warning: no value is entered for property """ & strProperties(j) & """."
  472.         End If
  473.         j = j + 1
  474.         If strCriteria <> "" Then
  475.             intColon = InStr(1, strCriteria, ":")
  476.         Else
  477.             intColon = 0
  478.         End If
  479.     Loop
  480.     strCriteria = strTemp & strCriteria
  481.  
  482. End Function
  483.  
  484. '********************************************************************
  485. '*
  486. '* Sub ConvertPropertyValues()
  487. '* Purpose: Converts elements of strPropertyValues to the right datatype.
  488. '* Input:   strProperties       an array holding names of user properties
  489. '*          strPropertyValues   an array holding the corresponding values of user properties
  490. '* Output:  Elements of strPropertyValues are converted to the appropriate datatypes.
  491. '*
  492. '********************************************************************
  493.  
  494. Private Sub ConvertPropertyValues(strProperties, strPropertyValues)
  495.  
  496.     ON ERROR RESUME NEXT
  497.  
  498.     Dim i, strProperty
  499.  
  500.     For i = 0 To UBound(strProperties)
  501.         strProperty = LCase(strProperties(i))
  502.         If strProperty="badpasswordattempts" or strProperty="maxlogins" or _
  503.             strProperty="maxstorage" or strProperty="maxpasswordage" or _
  504.             strProperty="minpasswordage" or strProperty="passwordhistorylength" or _
  505.             strProperty="userflags" or strProperty="codepage" or strProperty="countrycode" or _
  506.             strProperty="primarygroupid" or strProperty="samaccounttype" Then
  507.             strPropertyValues(i) = CLng(strPropertyValues(i))
  508.         ElseIf strProperty="lastlogin" or strProperty="lastlogoff" or _
  509.             strProperty="accountexpirationdate" Then
  510.             strPropertyValues(i) = CDate(strPropertyValues(i))
  511.         ElseIf strProperty="passwordneverexpires" or strProperty="usercannotchangepassword" _
  512.             or strProperty = "accountdisabled" Then
  513.             strPropertyValues(i) = CBool(strPropertyValues(i))
  514.         ElseIf strProperty="passwordexpired" Then
  515.             strPropertyValues(i) = CLng(-CBool(strPropertyValues(i)))
  516.         End If
  517.     Next
  518.     If Err.Number Then
  519.         Err.Clear
  520.         Print "Please check the input datatype and try again."
  521.         Wscript.Quit
  522.     End If
  523.  
  524. End Sub
  525.  
  526. '********************************************************************
  527. '*
  528. '* Sub ChkUsers()
  529. '* Purpose: Checks a domain for users against given criteria.
  530. '* Input:   strADsPath          ADsPath of a domain
  531. '*          strCriteria         the search criteria with each comparison replaced by
  532. '*                              a corresponding index
  533. '*          blnMultiFiles       specifies whether to save results to multiple files
  534. '*          strProperties       an array containing names of user properties to be checked
  535. '*          strPropertyValues   an array containing a set of target values of user properties
  536. '*          strOperators        a string array containing comparison operators,
  537. '*                              including >, < and =
  538. '*          strPropertiesOut    an array containing names of user properties to be retrieved
  539. '*          strUserName         name of the current user
  540. '*          strPassword         password of the current user
  541. '*          strInputFile        an input file name
  542. '*          strOutputFile       an output file name
  543. '*          blnQuestion         specifies whether to use message box to get info
  544. '* Output:  Specified properties of users satisfying the criteria are either printed
  545. '*          on screen or saved in file strOutputFile.
  546. '*
  547. '********************************************************************
  548.  
  549. Private Sub ChkUsers(strADsPath, strCriteria, blnMultiFiles, strProperties, _
  550.     strPropertyValues, strOperators, strPropertiesOut, strUserName, strPassword, _
  551.     strInputFile, strOutputFile, blnQuestion)
  552.  
  553.     ON ERROR RESUME NEXT
  554.  
  555.     Dim strProvider, objProvider, objDomain, objFileSystem, objInputFile, objOutputFile
  556.     Dim intFound, intFiles, objFolder, colFiles, strMessage, i
  557.  
  558.     intFound = 0
  559.     intFiles = 0
  560.  
  561.     'Check whether the Users series files exist in the current folder.
  562.     'If they do, ask for permission to delete them.
  563.     'The results will be saved into file Users* instead of strOutputFile.
  564.     If blnMultiFiles Then
  565.         'Create a filesystem object
  566.         Set objFileSystem = CreateObject("Scripting.FileSystemObject")
  567.         If Err.Number then
  568.             Print "Error 0x" & CStr(Hex(Err.Number)) & " opening a filesystem object."
  569.             If Err.Description <> "" Then
  570.                 Print "Error description: " & Err.Description & "."
  571.             End If
  572.             Exit Sub
  573.         End If
  574.  
  575.         'get the current folder
  576.         Set objFolder = objFileSystem.GetFolder(".")
  577.         Set colFiles = objFolder.Files
  578.         For Each objInputFile in colFiles
  579.             'Check whether the file name starts with "users"
  580.             If Left(LCase(objInputFile.name), 5) = "users" Then
  581.                 intFound = 1
  582.                 Exit For
  583.             End If
  584.         Next
  585.         If intFound Then
  586.             If blnQuestion Then
  587.                 strMessage = "All files named Users* in the current directory will be deleted."
  588.                 strMessage = strMessage & vbCRLF & "To save these files please move them"
  589.                 strMessage = strMessage & " to another directory before click the OK button."
  590.                 strMessage = strMessage & vbCRLF & "Click Cancel to quit the operation."
  591.                 'Ask the user for permission to delete files named Users*.
  592.                 i = MsgBox(strMessage, vbExclamation + vbOKCancel + vbDefaultButton2)
  593.             Else
  594.                 i = vbCancel + 1        'Assign a value to i so it is not vbCancel
  595.             End If
  596.             If i = vbCancel Then
  597.                 Wscript.quit
  598.             Else
  599.                 'Delete Users* files.
  600.                 For Each objInputFile in colFiles
  601.                     If Left(LCase(objInputFile.name), 5) = "users" Then
  602.                         objFileSystem.DeleteFile(objInputFile.name)        'Delete this file.
  603.                     End If
  604.                 Next
  605.             End If
  606.         End If
  607.         intFound = 0        'return it to zero.
  608.         intFiles = 1        'initializes intFiles for next output file name.
  609.         strOutputFile = "Users" & intFiles        'initializes the output file name.
  610.     End If
  611.  
  612.     If strOutputFile = "" Then
  613.         objFileSystem = ""
  614.         objOutputFile = ""
  615.     Else
  616.         If Not IsObject(objFileSystem) Then
  617.             'Create a filesystem object
  618.             Set objFileSystem = CreateObject("Scripting.FileSystemObject")
  619.             If Err.Number then
  620.                 Print "Error 0x" & CStr(Hex(Err.Number)) & " opening a filesystem object."
  621.                 If Err.Description <> "" Then
  622.                     Print "Error description: " & Err.Description & "."
  623.                 End If
  624.                 Exit Sub
  625.             End If
  626.         End If
  627.         'Open the file for output
  628.         Set objOutputFile = objFileSystem.OpenTextFile(strOutputFile, 8, True)
  629.         If Err.Number then
  630.             Print "Error 0x" & CStr(Hex(Err.Number)) & " opening file " & strOutputFile
  631.             If Err.Description <> "" Then
  632.                 Print "Error description: " & Err.Description & "."
  633.             End If
  634.             Exit Sub
  635.         End If
  636.     End If
  637.  
  638.     'Check the domain specified by /A:adspath.
  639.     If strADsPath <> "" Then
  640.         If strUserName = ""    then        'The current user is assumed
  641.             Set objDomain = GetObject(strADsPath)
  642.         Else                        'Credentials are passed
  643.             strProvider = Left(strADsPath, InStr(1, strADsPath, ":"))
  644.             Set objProvider = GetObject(strProvider)
  645.             'Use user authentication
  646.             Set objDomain = objProvider.OpenDsObject(strADsPath,strUserName,strPassword,1)
  647.         End If
  648.         If Err.Number then
  649.             If CStr(Hex(Err.Number)) = "80070035" Then
  650.                 Print "Object " & strADsPath & " does not exist."
  651.             Else
  652.                 Print "Error 0x" & CStr(Hex(Err.Number)) & " occurred in getting object " _
  653.                     & strADsPath & "."
  654.                 If Err.Description <> "" Then
  655.                     Print "Error description: " & Err.Description & "."
  656.                 End If
  657.             End If
  658.             Err.Clear
  659.             Exit Sub
  660.         End If
  661.         'intFound is the number of users found not logged in for intDays days.
  662.         intFound = intChkOneDomain(objDomain, strCriteria, strProperties,_
  663.             strPropertyValues, strOperators, strPropertiesOut, objOutputFile)
  664.         If blnMultiFiles Then        'close the output file
  665.             objOutputFile.Close
  666.             intFiles = intFiles + 1        'initializes intFiles for next output file name.
  667.         End If
  668.     End If
  669.  
  670.     'Check domains listed in /I:inputfile.
  671.     If strInputFile <> "" Then
  672.         If Not IsObject(objFileSystem) Then
  673.             'Create a filesystem object
  674.             Set objFileSystem = CreateObject("Scripting.FileSystemObject")
  675.             If Err.Number then
  676.                 Print "Error 0x" & CStr(Hex(Err.Number)) & " opening a filesystem object."
  677.                 If Err.Description <> "" Then
  678.                     Print "Error description: " & Err.Description & "."
  679.                 End If
  680.                 Exit Sub
  681.             End If
  682.         End If
  683.         'Open the file for input
  684.         Set objInputFile = objFileSystem.OpenTextFile(strInputFile)
  685.         If Err.Number then
  686.             Print "Error 0x" & CStr(Hex(Err.Number)) & " opening file " & strInputFile
  687.             If Err.Description <> "" Then
  688.                 Print "Error description: " & Err.Description & "."
  689.             End If
  690.             Exit Sub
  691.         End If
  692.         'Read input file.
  693.         While not objInputFile.AtEndOfStream
  694.             'Get rid of leading and trailing spaces
  695.             strADsPath = Trim(objInputFile.ReadLine)
  696.             If strADsPath <> "" Then                        'Get rid of empty lines
  697.                 If strUserName = ""    then        'The current user is assumed
  698.                     Set objDomain = GetObject(strADsPath)
  699.                 Else                        'Credentials are passed
  700.                     strProvider = Left(strADsPath, InStr(1, strADsPath, ":"))
  701.                     Set objProvider = GetObject(strProvider)
  702.                     'Use user authentication
  703.                     Set objDomain = objProvider.OpenDsObject(strADsPath,strUserName,_
  704.                         strPassword,1)
  705.                 End If
  706.                 If Err.Number then
  707.                     If CStr(Hex(Err.Number)) = "80070035" Then
  708.                         Print "Object " & strADsPath & " does not exist."
  709.                     Else
  710.                         Print "Error 0x" & CStr(Hex(Err.Number)) & _
  711.                             " occurred in getting object " & strADsPath & "."
  712.                         If Err.Description <> "" Then
  713.                             Print "Error description: " & Err.Description & "."
  714.                         End If
  715.                     End If
  716.                     Err.Clear
  717.                     Exit Sub
  718.                 End If
  719.  
  720.                 'Get the right file name
  721.                 If blnMultiFiles Then
  722.                     'Change the output file name to "Users" & intFiles
  723.                     strOutputFile = "Users" & intFiles
  724.                     'Open the file for output
  725.                     Set objOutputFile = objFileSystem.OpenTextFile(strOutputFile, 8, True)
  726.                 End If
  727.                 'intFound is the number of users found not logged in for intDays days.
  728.                 intFound = intFound + intChkOneDomain(objDomain, strCriteria, strProperties, _
  729.                     strPropertyValues, strOperators, strPropertiesOut, objOutputFile)
  730.                 If blnMultiFiles Then
  731.                     'Close the output file and initializes intFiles for next output file name.
  732.                     objOutputFile.Close
  733.                     intFiles = intFiles + 1
  734.                 End If
  735.             End If
  736.         Wend
  737.         objInputFile.Close
  738.     End If
  739.  
  740.     If blnMultiFiles Then
  741.         If intFound Then
  742.             strOutputFile = ""
  743.             For i = 1 To intFiles-1
  744.                 strOutputFile = strOutputFile & "Users" & i & ", "
  745.             Next
  746.             'Get rid of the last two characters.
  747.             strOutputFile = Left(strOutputFile, Len(strOutputFile)-2)
  748.             Wscript.Echo  "Results are saved in files " & strOutputFile & "."
  749.         End If
  750.     Else
  751.         If strOutputFile <> "" Then
  752.             If intFound Then
  753.                 Wscript.Echo  "Results are saved in file " & strOutputFile & "."
  754.             End If
  755.             objOutputFile.Close
  756.         End If
  757.     End If
  758.  
  759. End Sub
  760.  
  761. '********************************************************************
  762. '*
  763. '* Sub intChkOneDomain()
  764. '* Purpose:   Checks a domain for users against a given criteria.
  765. '* Input:   objDomain           the domain to be checked
  766. '*          strCriteria         the search criteria with each comparison replaced by
  767. '*                              a corresponding index
  768. '*          strProperties       an array containing names of user properties to be checked
  769. '*          strPropertyValues   an array containing a set of target values of user properties
  770. '*          strOperators        a string array containing comparison operators,
  771. '*                              including >, < and =
  772. '*          strPropertiesOut    an array containing names of user properties to be retrieved
  773. '*          strOutputFile       an output file name
  774. '* Output:  Specified properties of users satisfying the criteria are either printed
  775. '*          on screen or saved in file strOutputFile. intChkOneDomain is set to the
  776. '*          number of users found.
  777. '*
  778. '********************************************************************
  779.  
  780. Private Function intChkOneDomain(objDomain, strCriteria, strProperties, strPropertyValues,_
  781.     strOperators, strPropertiesOut, objOutputFile)
  782.  
  783.     ON ERROR RESUME NEXT
  784.  
  785.     Dim i, intFound, intUBound, strPropertyValue, intResults(), strTemp, objADs
  786.  
  787.     intFound = 0
  788.     intChkOneDomain = 0
  789.     intUBound = UBound(strProperties)
  790.     ReDim intResults(intUBound)
  791.     objDomain.Filter = Array("user")
  792.  
  793.     For Each objADs in objDomain
  794.         For i = 0 To intUBound
  795.             'Get a user property.
  796.             If blnGetOneProperty(objADs, strProperties(i), strPropertyValue) Then
  797.                 Print "Unable to get property " & strProperties(i)
  798.                 Exit Function
  799.             End If
  800.  
  801.             'Compare the value with the criteria.
  802.             intResults(i) = intCompare(strPropertyValue, strOperators(i), _
  803.                 strPropertyValues(i))
  804.             If intResults(i) = CONST_FAILED Then
  805.                 Print "Failed to compare property " & strProperties(i) & " with " _
  806.                     & strPropertyValues(i) & "."
  807.                 Exit Function
  808.             End if
  809.         Next
  810.  
  811.         'Copy criteria into strTemp so criteria is not modified by the subsequent operations.
  812.         strTemp = strCriteria
  813.         'Now replace the digits inserted in the criteria array with
  814.         'the corresponding value from the above comparison.
  815.         If blnCopyResults(intResults, strTemp) Then
  816.             Print "Error occurred in copying an array."
  817.             Err.Clear
  818.             Exit Function
  819.         End If
  820.  
  821.         'Evaluate the user properties to determine whether it satisfies the criteria.
  822.         i = intEvalCriteria(strTemp)
  823.         If i = CONST_FAILED Then
  824.             Print "Failed to evaluate the expression."
  825.             Exit Function
  826.         ElseIf i Then        'If it satisfies the criteria.
  827.             intFound = intFound + 1
  828.             If blnPrintProperties(objADs, strPropertiesOut, objOutputFile) Then
  829.                 Print "Failed to get properties for user " & objADs.Name & "."
  830.             End If
  831.         End If
  832.     Next
  833.  
  834.     Print intFound & " users satisfying the criteria have been found in " _
  835.         & objDomain.ADsPath & "."
  836.     intChkOneDomain = intFound
  837.  
  838. End Function
  839.  
  840. '********************************************************************
  841. '*
  842. '* Sub blnGetOneProperty()
  843. '* Purpose: Gets one property of a given ADS object.
  844. '* Input:   objADS              an ADS object
  845. '*          strProperty         name of a property
  846. '*          strPropertyValue    a string to save the value of the property
  847. '* Output:  blnGetOneProperty is set to True if an error occurred and False otherwise.
  848. '*
  849. '********************************************************************
  850.  
  851. Function blnGetOneProperty(objADS, ByVal strProperty, ByRef strPropertyValue)
  852.  
  853.     ON ERROR RESUME NEXT
  854.  
  855.     Dim lngFlag, strResult, i, intUBound
  856.  
  857.     blnGetOneProperty = False
  858.     strProperty = LCase(strProperty)
  859.  
  860.     Select Case strProperty
  861.         Case "usercannotchangepassword"
  862.             lngFlag = objADs.Get("UserFlags")
  863.             If lngFlag = lngFlag and CONST_UF_PASSWORD_CAN_CHANGE    Then
  864.                 strPropertyValue = 0        'User can change password
  865.             Else
  866.                 strPropertyValue = 1
  867.             End If
  868.         Case "passwordneverexpires"
  869.             lngFlag = objADs.Get("UserFlags")
  870.             If lngFlag = lngFlag or CONST_UF_DONT_EXPIRE_PASSWORD Then
  871.                 strPropertyValue = 1        'Password does not expire.
  872.             Else
  873.                 strPropertyValue = 0
  874.             End If
  875.         Case Else
  876.             strResult = objADS.Get(strProperty)
  877.             If Err.Number Then
  878.                 Err.Clear            'The property is not available.
  879.                 If strProperty = "lastlogin" or strProperty = "lastlogoff" Then
  880.                     strPropertyValue = CDate("1/1/1900")        'A date in the remote past.
  881.                 Else
  882.                     blnGetOneProperty = True
  883.                 End If
  884.             Else
  885.                 If IsArray(strResult) Then
  886.                     Print strProperty & " is a multivalued property."
  887.                     Print "The last value is used."
  888.                     strPropertyValue = strResult(UBound(strResult))
  889.                 Else
  890.                     strPropertyValue =  strResult
  891.                 End If
  892.             End If
  893.     End Select
  894.  
  895. End Function
  896.  
  897. '********************************************************************
  898. '*
  899. '* Function intCompare()
  900. '* Purpose: Compares the value of a user property with the input value.
  901. '* Input:   strValue1       the value of a user property
  902. '*          strOperator     the comparison operator
  903. '*          strValue2       the input value
  904. '* Output:  intCompare = CONST_FAILED if an error occurred, otherwise
  905. '*          it is 1 if the comparison evaluates to true and 0 if false.
  906. '*
  907. '********************************************************************
  908.  
  909. Private Function intCompare(strValue1, strOperator, strValue2)
  910.  
  911.     Dim i, strLeft1, strLeft2
  912.  
  913.     Select Case strOperator
  914.         Case ">"
  915.             If strValue1 > strValue2 Then
  916.                 intCompare = 1
  917.             Else
  918.                 intCompare = 0
  919.             End If
  920.         Case "<"
  921.             If strValue1 < strValue2 Then
  922.                 intCompare = 1
  923.             Else
  924.                 intCompare = 0
  925.             End If
  926.         Case "="
  927.             i = InStr(1, strValue2, "*")        'Check for wild card *
  928.             If i > 1 Then
  929.                 strLeft1 = Left(strValue1, i-1)
  930.                 strLeft2 = Left(strValue2, i-1)
  931.                 If LCase(strLeft1) = LCase(strLeft2) Then
  932.                     intCompare = 1
  933.                 Else
  934.                     intCompare = 0
  935.                 End If
  936.             ElseIf i = 1 Then        'As long as strValue1 is not empty, intCompare = 1.
  937.                 If strValue1 = "" Then
  938.                     intCompare = 0
  939.                 Else
  940.                     intCompare = 1
  941.                 End If
  942.             Else
  943.                 If LCase(strValue1) = LCase(strValue2) Then
  944.                     intCompare = 1
  945.                 Else
  946.                     intCompare = 0
  947.                 End If
  948.             End If
  949.         Case Else
  950.             Print "Operator " & strOperator & " is not supported."
  951.             intCompare = CONST_FAILED
  952.     End Select
  953.  
  954. End Function
  955.  
  956. '********************************************************************
  957. '*
  958. '* Function blnCopyResults()
  959. '* Purpose: Replaces integers in strString with corresponding elements of intResults.
  960. '* Input:   intResults      an array containing elements with a value of either 1 or 0
  961. '*          strString       the original criteria string with each comparison unit
  962. '*                          replaced by an integer
  963. '* Output:  blnCopyResults = True if an error occurred and False otherwise.
  964. '*
  965. '********************************************************************
  966.  
  967. Private Function blnCopyResults(intResults, strString)
  968.  
  969.     Dim i, k, strLeft, strRight
  970.  
  971.     k = 0
  972.     blnCopyResults = False        'No error.
  973.  
  974.     For i = 0 To UBound(intResults)
  975.         k = k + 1
  976.         'Start the search at position k and save the result in k.
  977.         k = InStr(k, strString, CStr(i))
  978.         strLeft = Left(strString, k-1)
  979.         strRight = Right(strString, Len(strString)-k)
  980.         If k Then
  981.             strString = strLeft & intResults(i) & strRight
  982.         Else
  983.             blnCopyResults = True        'This is an error.
  984.             Exit Function
  985.         End If
  986.     Next
  987.  
  988. End Function
  989.  
  990. '********************************************************************
  991. '*
  992. '* Function blnPrintProperties()
  993. '* Purpose: Gets specified user properties and writes them either to a file or on screen.
  994. '* Input:   objADS              an ADS object
  995. '*          strPropertyArray    an array of properties
  996. '*          objOutputFile       a file object for output
  997. '* Output:  blnGetOneProperty is True if an error occurred and False otherwise.
  998. '*
  999. '********************************************************************
  1000.  
  1001. Function blnPrintProperties(objADS, strPropertyArray, objOutputFile)
  1002.  
  1003.     ON ERROR RESUME NEXT
  1004.  
  1005.     Dim i, strResult, strTemp, strOutput
  1006.  
  1007.     blnPrintProperties = False        'No error.
  1008.     strOutput = ""
  1009.     For i = 0 To UBound(strPropertyArray)
  1010.         Select Case LCase(strPropertyArray(i))
  1011.             'First deal with some properties that can not be obtained with Get.
  1012.             Case "name"
  1013.                 strResult = objADS.Name
  1014.             Case "adspath"
  1015.                 strResult = objADS.ADsPath
  1016.             Case Else
  1017.                 strTemp = objADS.Get(strPropertyArray(i))
  1018.                 If Err.Number Then
  1019.                     Err.Clear            'The property is not available.
  1020.                     If strPropertyArray(i) = "lastlogin" or strPropertyArray(i) = _
  1021.                         "lastlogoff" Then
  1022.                         strResult = CDate("1/1/1900")        'A date in the remote past.
  1023.                     Else
  1024.                         blnPrintProperties = True
  1025.                     End If
  1026.                 Else
  1027.                     If IsArray(strResult) Then
  1028.                         Print strPropertyArray(i) & " is a multivalued property."
  1029.                         Print "The last value is used."
  1030.                         strResult = strResult(UBound(strTemp))
  1031.                     Else
  1032.                         strResult =  strTemp
  1033.                     End If
  1034.                 End If
  1035.         End Select
  1036.         strOutput = strOutput & "         " & strResult
  1037.     Next
  1038.     WriteLine strOutput, objOutputFile
  1039.  
  1040. End Function
  1041.  
  1042. '********************************************************************
  1043. '*
  1044. '* Sub WriteLine()
  1045. '* Purpose: Writes a text line either to a file or on screen.
  1046. '* Input:   strMessage  the string to print
  1047. '*          objFile     an output file object
  1048. '* Output:  strMessage is either displayed on screen or written to a file.
  1049. '*
  1050. '********************************************************************
  1051.  
  1052. Sub WriteLine(ByRef strMessage, ByRef objFile)
  1053.  
  1054.     If IsObject(objFile) then        'objFile should be a file object
  1055.         objFile.WriteLine strMessage
  1056.     Else
  1057.         Wscript.Echo  strMessage
  1058.     End If
  1059.  
  1060. End Sub
  1061.  
  1062. '********************************************************************
  1063. '*
  1064. '* Sub Print()
  1065. '* Purpose: Prints a message on screen
  1066. '* Input:   strMessage - the string to print
  1067. '* Output:  strMessage is printed on screen
  1068. '*
  1069. '********************************************************************
  1070.  
  1071. Sub Print(ByRef strMessage)
  1072.     Wscript.Echo strMessage
  1073. End Sub
  1074.  
  1075. '********************************************************************
  1076. '*
  1077. '* Function FormatProvider
  1078. '* Purpose: Formats Provider so it is not case sensitive
  1079. '* Input:   Provider    a string
  1080. '* Output:  FormatProvider is the Provider with the correct Case
  1081. '*
  1082. '********************************************************************
  1083. Private Function FormatProvider(Provider)
  1084.     FormatProvider = ""
  1085.     I = 1
  1086.     Do Until Mid(Provider, I, 1) = ":"
  1087.         If I = Len(Provider) Then
  1088.             'This Provider is Probabaly not valid, but we'll let it pass anyways.
  1089.             FormatProvider = Provider
  1090.             Exit Function
  1091.         End If
  1092.         I = I + 1
  1093.     Loop
  1094.  
  1095.     Select Case LCase(Left(Provider, I - 1))
  1096.         Case "winnt"
  1097.             FormatProvider = "WinNT" & Right(Provider,Len(Provider) - (I - 1))
  1098.         Case "ldap"
  1099.             FormatProvider = "LDAP" & Right(Provider,Len(Provider) - (I - 1))            
  1100.     End Select
  1101.  
  1102.  
  1103. End Function
  1104.  
  1105. '********************************************************************
  1106. '********************************************************************
  1107. '    THE CODE BELOW IS FOR EVULUATING THE CRITERIA STRING ONLY
  1108. '********************************************************************
  1109. '********************************************************************
  1110.  
  1111. '********************************************************************
  1112. '*
  1113. '* Function intEvalCriteria()
  1114. '* Purpose: Evaluates a string that can contain brackets.
  1115. '* Input:   strString    a string
  1116. '* Output:  intEvalCriteria = CONST_FAILED if the evaluation failed,
  1117. '*          otherwise it is the result of the evaluation.
  1118. '* Example: If strString="(1 and 0) or 1" then intEvalCriteria=1.
  1119. '*
  1120. '********************************************************************
  1121.  
  1122. Private Function intEvalCriteria(ByVal strString)
  1123.  
  1124.     ON ERROR RESUME NEXT
  1125.  
  1126.     Dim intLeft, intRight, i, strTemp
  1127.  
  1128.     'Check the number of ")" and "("
  1129.     If intCharCount(strString, ")") <> intCharCount(strString, "(") Then
  1130.         intEvalCriteria = CONST_FAILED        'Incorrect syntax
  1131.         Exit Function
  1132.     End If
  1133.  
  1134.     'Now get rid of all double spaces.
  1135.     Do
  1136.         i = InStr(1, strString, "  ")
  1137.         If i Then
  1138.             'Replace double spaces with single ones.
  1139.             strString = Replace(strString, "  ", " ")
  1140.         End If
  1141.     Loop Until i = 0
  1142.  
  1143.     Do
  1144.         'Look for first ")" in the array
  1145.         intRight = InStr(1, strString, ")")
  1146.         If intRight = 0 Then
  1147.             'There is no quote in the array
  1148.             intEvalCriteria = intEvalNoQuote(strString)
  1149.             Exit Function
  1150.         End If
  1151.  
  1152.  
  1153.         intLeft = InStrRev(strString, "(", intRight, 1)
  1154.         If intLeft = 0 Then
  1155.             intEvalCriteria = CONST_FAILED        'Syntax error
  1156.             Exit Function
  1157.         End If
  1158.  
  1159.         strTemp = Mid(strString, intLeft+1, intRight-intLeft-1)
  1160.  
  1161.         If strTemp <> "" Then
  1162.             i = intEvalNoQuote(strTemp)
  1163.             If i = CONST_FAILED Then
  1164.                 intEvalCriteria = i
  1165.                 Exit Function
  1166.             End If
  1167.         Else
  1168.             i = ""
  1169.         End If
  1170.  
  1171.         If blnReplaceString(strString, intLeft, intRight, i) Then
  1172.             intEvalCriteria = CONST_FAILED
  1173.             Exit Function
  1174.         End If
  1175.     Loop Until Len(strString) = 1
  1176.  
  1177.     intEvalCriteria = CInt(strString)
  1178.     If Err.Number Then
  1179.         Err.Clear
  1180.         intEvalCriteria = CONST_FAILED
  1181.     End If
  1182.  
  1183. End Function
  1184.  
  1185. '********************************************************************
  1186. '*
  1187. '* Function blnReplaceString()
  1188. '* Purpose: Replaces a sub string in a string with another sub string.
  1189. '* Input:   strString       a string
  1190. '*          intStart        the starting position of the sub string
  1191. '*          intEnd          the ending position of the sub string
  1192. '*          strReplace      the new sub string
  1193. '* Output:  blnReplaceString = True if an error occurred and False otherwise.
  1194. '*
  1195. '********************************************************************
  1196.  
  1197. Private Function blnReplaceString(strString, intStart, intEnd, strReplace)
  1198.  
  1199.     Dim strLeft, strRight, intLen
  1200.  
  1201.     blnReplaceString = False    'No error.
  1202.  
  1203.     intLen = Len(strString)
  1204.     If intStart < 1 or intEnd > intLen Then
  1205.         blnReplaceString = True        'This is an error
  1206.         Exit Function
  1207.     End If
  1208.  
  1209.     strLeft = Left(strString, intStart-1)
  1210.     strRight = Right(strString, intLen-intEnd)
  1211.     strString = strLeft & strReplace & strRight
  1212.  
  1213. End Function
  1214.  
  1215. '********************************************************************
  1216. '*
  1217. '* Function intCharCount()
  1218. '* Purpose: Finds the number of times a character appears in a string.
  1219. '* Input:   strString   a string
  1220. '*          chr         a character
  1221. '* Output:  intCharCount is the number of times a character appears in an array.
  1222. '*
  1223. '********************************************************************
  1224.  
  1225. Private Function intCharCount(ByVal strString, ByVal chr)
  1226.  
  1227.     Dim i, strTemp
  1228.  
  1229.     i = Len(strString)
  1230.     strTemp = Replace(strString, chr, "")
  1231.     intCharCount = i - Len(strTemp)
  1232.  
  1233. End Function
  1234.  
  1235.  
  1236. '********************************************************************
  1237. '*
  1238. '* Function intEvalNoQuote()
  1239. '* Purpose: Evaluates a string that does not contain any quote.
  1240. '* Input:   strString    a string
  1241. '* Output:  intEvalNoQuote = CONST_FAILED if the evaluation failed,
  1242. '*          otherwise it is the result of the evaluation.
  1243. '* Example: If strString="1 and 0 or 1", then intEvalNoQuote=1.
  1244. '*
  1245. '********************************************************************
  1246.  
  1247. Private Function intEvalNoQuote(ByVal strString)
  1248.  
  1249.     ON ERROR RESUME NEXT
  1250.  
  1251.     Dim i, intLeft, intRight, blnLeft, blnRight, chrSpace
  1252.     Dim intAnd, intNot, intOr
  1253.  
  1254.     chrSpace = chr(32)
  1255.     strString = LCase(Trim(strString))
  1256.  
  1257.     'Handling all "Not"
  1258.     Do
  1259.         intNot = InStr(1, strString, "not")
  1260.         If intNot Then
  1261.             If intNot > (Len(strString)-4) Then
  1262.                 intEvalNoQuote = CONST_FAILED    'It is an error.
  1263.                 Exit Function
  1264.             End If
  1265.             intRight = InStr(intNot+4, strString, chrSpace)
  1266.             If intRight = 0 Then
  1267.                 intRight = Len(strString) + 1
  1268.             End If
  1269.             blnRight = CBool(Mid(strString, intNot+4, intRight-intNot-4))
  1270.             'Commit the Not operation
  1271.             i = 1 + CInt(blnRight)        '1 for true and 0 for false
  1272.             If Err.Number Then
  1273.                 Err.Clear
  1274.                 intEvalNoQuote = CONST_FAILED    'An error occurred.
  1275.                 Exit Function
  1276.             End If
  1277.             'Get the result into the string.
  1278.             If blnReplaceString(strString, intNot, intRight-1, i) Then
  1279.                 intEvalNoQuote = CONST_FAILED    'An error occurred.
  1280.                 Exit Function
  1281.             End If
  1282.         End If
  1283.     Loop Until intNot = 0
  1284.  
  1285.     'Handling all "and"
  1286.     Do
  1287.         intAnd = InStr(1, strString, "and")
  1288.         If intAnd Then
  1289.             If intAnd < 3 or intAnd > (Len(strString)-4) Then
  1290.                 intEvalNoQuote = CONST_FAILED    'It is an error.
  1291.                 Exit Function
  1292.             End If
  1293.             intLeft = InStrRev(strString, chrSpace, intAnd-2)
  1294.             intRight = InStr(intAnd+4, strString, chrSpace)
  1295.             If intLeft = 0 Then
  1296.                 intLeft = 0
  1297.             End If
  1298.             If intRight = 0 Then
  1299.                 intRight = Len(strString) + 1
  1300.             End If
  1301.             'Get the value to the left
  1302.             blnLeft = CBool(Mid(strString, intLeft+1, intAnd-intLeft-2))
  1303.             'Get the value to the right
  1304.             blnRight = CBool(Mid(strString, intAnd+4, intRight-intAnd-4))
  1305.             If Err.Number Then
  1306.                 Err.Clear
  1307.                 intEvalNoQuote = CONST_FAILED    'An error occurred.
  1308.                 Exit Function
  1309.             End If
  1310.             'Commit the And operation
  1311.             i = -CInt(blnLeft and blnRight)            '1 for true and 0 for false
  1312.             If blnReplaceString(strString, intLeft+1, intRight-1, i) Then
  1313.                 'Get the result into the string.
  1314.                 intEvalNoQuote = CONST_FAILED    'An error occurred.
  1315.                 Exit Function
  1316.             End If
  1317.         End If
  1318.     Loop Until intAnd = 0
  1319.  
  1320.     'Handling all "or"
  1321.     Do
  1322.         intOr = InStr(1, strString, "or")
  1323.         If intOr Then
  1324.             If intOr < 3 or intOr > (Len(strString)-3) Then
  1325.                 intEvalNoQuote = CONST_FAILED    'It is an error.
  1326.                 Exit Function
  1327.             End If
  1328.             intLeft = InStrRev(strString, chrSpace, intOr-2)
  1329.             intRight = InStr(intOr+3, strString, chrSpace)
  1330.             If intLeft = 0 Then
  1331.                 intLeft = 0
  1332.             End If
  1333.             If intRight = 0 Then
  1334.                 intRight = Len(strString) + 1
  1335.             End If
  1336.             'Get the value to the left
  1337.             blnLeft = CBool(Mid(strString, intLeft+1, intOr-intLeft-1))
  1338.             'Get the value to the right
  1339.             blnRight = CBool(Mid(strString, intOr+3, intRight-intOr-3))
  1340.             If Err.Number Then
  1341.                 Err.Clear
  1342.                 intEvalNoQuote = CONST_FAILED    'An error occurred.
  1343.                 Exit Function
  1344.             End If
  1345.             'Commit the And operation
  1346.             i = -CInt(blnLeft or blnRight)            '1 for true and 0 for false
  1347.             If blnReplaceString(strString, intLeft+1, intRight-1, i) Then
  1348.                 'Get the result into the string.
  1349.                 intEvalNoQuote = CONST_FAILED    'An error occurred.
  1350.                 Exit Function
  1351.             End If
  1352.         End If
  1353.     Loop Until intOr = 0
  1354.  
  1355.     strString = Trim(strString)
  1356.     If Len(strString) > 1 Then
  1357.         intEvalNoQuote = CONST_FAILED    'It is an error.
  1358.     Else
  1359.         intEvalNoQuote = CInt(strString)
  1360.     End If
  1361.  
  1362. End Function
  1363.  
  1364. '********************************************************************
  1365. '*
  1366. '* Note:
  1367. '*
  1368. '* 1. The criteria should be combinations of expressions like (property1:=value1).
  1369. '*
  1370. '* 2. In parsing the input the property names (eg, property1) are read into array
  1371. '*    strProperties, the values(eg, value1) are read into array strPropertyValues,
  1372. '*    and the operators(eg, =) are read into strOperators(0).
  1373. '*
  1374. '* 3. String strCriteria stores the criteria with each comparison property1:=value1
  1375. '*    replaced by an integer representing the order it appears in the criteria.
  1376. '*    For example, criteria "name:='j*' and lastlogin:>4/3/98 and lastlogin:<8/8/98"
  1377. '*    becomes "0 and 1 and 2". This expression is then evaluated to determine whether
  1378. '*    the syntax is correct.
  1379. '*
  1380. '* 4. For users who has never logged in,  the lastlogin and lastlogoff dates assigned
  1381. '*    are "1/1/1900".
  1382. '*
  1383. '*  File Name:    ChkUsers.vbs
  1384. '*
  1385. '*    A detailed description:
  1386. '*
  1387. '*    This script is intended to be used to check a domain or container for users
  1388. '*    satisfying a given criteria. But it can be easily adapted to check a domain or
  1389. '*    container for other types of objects satisfying a given criteria. The only change
  1390. '*    required for the script is to change the filter for the domain or container in
  1391. '*    function intChkOneDomain().
  1392. '*
  1393. '*    The input to the script includes the ADsPath of a domain or container. It is also
  1394. '*    possible to check multiple domains or containers if the criteria is the same for all
  1395. '*    of them. To do this simply save ADsPaths of these domains or containers into a text
  1396. '*    file, one in a line, and use the [/I:inputfile] option instead of [/A:adspath].
  1397. '*
  1398. '*    /C:criteria is the only mandatory input for this script. The criteria should be
  1399. '*    enclosed in double quotes to be interpreted correctly. The criteria is composed of
  1400. '*    many comparisons linked using logic operators, such as And, Or, Not. Each comparison
  1401. '*    is in the format of property:>value, where property is a valid property name of the
  1402. '*    user object and > can also be replaced by < or = and value is a valid value of the
  1403. '*    property. For example FullName:='John Smith' specifies the user's fullname to be
  1404. '*    "John Smith". Note that there is a colon between the property name and the
  1405. '*    comparison operator and a string with space should be enclosed in single quotes. If
  1406. '*    the comparison operator is omitted the default is =. It is also possible to use wild
  1407. '*    card * with operator =.
  1408. '*
  1409. '*    Brackets can be used in the criteria so the criteria experession will be in a form
  1410. '*    like ((A or B) and Not C) where each of A, B, C is a valid comparison in the form of
  1411. '*    property:>value. In function intParseCmdLine(), each comparison is replaced with an
  1412. '*    integer in the order it appears. For example the expression above becomes ((0 or 1)
  1413. '*    and Not 2). This expression is first evaluated to determine whether the syntax is
  1414. '*    correct. For example, a left bracket without a right bracket, or an incorrect usage
  1415. '*    of a comparison operator would trigger an "incorrect syntax" warning and the program
  1416. '*    execution would be terminated. The comparisons are stored in three string arrays:
  1417. '*    strProperties, strPropertyValues, strOperators. strProperties stores the names of
  1418. '*    properties, while strPropertyValues stores the corresponding values and strOperators
  1419. '*    stores the operators.
  1420. '*
  1421. '*    If the syntax is correct the script proceeds to list every user in the
  1422. '*    domain/container and for each user the comparisons are evaluted to be either 1(true)
  1423. '*    or 0(false). For example, FullName:=J* will be 1 if the user's fullname starts with
  1424. '*    letter J and 0 otherwise. The values of these comparisons are then plugged back into
  1425. '*    the criteria experission and the criteria is evaluated to be either true or false
  1426. '*    based on these values. For example, if A is true, B is False and C is False then the
  1427. '*    strCriteria becomes ((1 or 0) and Not 0) which evaluates to 1, or True.
  1428. '*
  1429. '*    If the criteria is evaluated to be true, properties as listed in [/P:properties] of
  1430. '*    the user will be retrieved and either saved into an output file or printed on the
  1431. '*    screen. If no property is specified using [/P:properties], ADsPath is used as the
  1432. '*    default.
  1433. '*
  1434. '********************************************************************
  1435.  
  1436. '********************************************************************
  1437. '*                                                                  *
  1438. '*                           End of File                            *
  1439. '*                                                                  *
  1440. '********************************************************************
  1441.  
  1442. '********************************************************************
  1443. '*
  1444. '* Procedures calling sequence: CHKUSERS.VBS
  1445. '*
  1446. '*  intChkProgram
  1447. '*  intParseCmdLine
  1448. '*      blnParseCriteria
  1449. '*      intEvalCriteria
  1450. '*  ShowUsage
  1451. '*  ConvertPropertyValues
  1452. '*  ChkUsers
  1453. '*      intChkOneDomain
  1454. '*          blnGetOneProperty
  1455. '*          intCompare
  1456. '*          blnCopyResults
  1457. '*          intEvalCriteria
  1458. '*          blnPrintProperties
  1459. '*              WriteLine
  1460. '*
  1461. '********************************************************************
  1462.