<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>sidefumbling &#187; VBScript</title>
	<atom:link href="http://www.flobee.net/category/scripts/vbscript/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.flobee.net</link>
	<description>The consequence of not having six hydrocoptic marzelvanes.</description>
	<lastBuildDate>Fri, 20 Jan 2012 17:04:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Get automatic notification when running low on BES licenses</title>
		<link>http://www.flobee.net/get-automatic-notification-when-running-low-on-bes-licenses/</link>
		<comments>http://www.flobee.net/get-automatic-notification-when-running-low-on-bes-licenses/#comments</comments>
		<pubDate>Thu, 30 Jul 2009 23:57:48 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=81</guid>
		<description><![CDATA[This VBScript uses native SQL connectivity via ADO to query your BlackBerry Enterprise Server configuration database for the current number of users. Since the license total is not stored in the database you have to set it as a variable in the script. It then compare the two numbers and if your defined threshold is [...]]]></description>
			<content:encoded><![CDATA[<p>This VBScript uses native SQL connectivity via ADO to query your BlackBerry Enterprise Server configuration database for the current number of users.  Since the license total is not stored in the database you have to set it as a variable in the script.  It then compare the two numbers and if your defined threshold is exceeded, it will email you a notification.  Just schedule the script to run daily.  Download a zipped version <a href="/download/BESLicenseMonitor.zip">here</a>, or copy below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
'BES license usage monitor.  Set current license total, alert threshold, And
'other variables to be notified when the license usage exceeds the threshold.
Dim strSQLServer, strBESDB, iCurrentLicenseTotal, iLicenseThreshold
Dim strMailRecipients, strMailSender, strMailSubject, strMailServer
'*************************************
'------Configurable Variables---------
iCurrentLicenseTotal = 1000 'Total number of licenses in License Management
iLicenseThreshold = 20 'Free licenses threshold for notification
strSQLServer = "ServerName" 'NetBIOS name of SQL Server
strBESDB = "DatabaseName" 'Database name in SQL
'------Notification configuration-----
strMailRecipients = "user1@domain.com,user2@domain.com" 'Command-separated list of addresses
strMailSender = "BESLicenseCounter@domain.com" 'From address
strMailSubject = "BES license usage at critical level" 'Subject of notification message
strMailServer = "smtpserver.domain.com" 'FQDN of smarthost
'*************************************

Dim iCurrentUserTotal, iAvailableLicenses, strSQLQuery, strSQLConn
Dim objSQLConn, objSQLRecordSet, objSQLExec
strSQLConn = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Data Source=" &#038; _
	strSQLServer &#038; "; Initial Catalog=" &#038; strBESDB
strSQLQuery = "Select Count(*) As Total_Users From UserStats"
Set objSQLConn = CreateObject("ADODB.Connection")
Set objSQLRecordSet = CreateObject("ADODB.Recordset")
objSQLConn.Open = strSQLConn
objSQLRecordSet.Open strSQLQuery, objSQLConn
Set objSQLExec = objSQLRecordSet
While Not objSQLExec.EOF
	iCurrentUserTotal = objSQLExec.Fields("Total_Users").Value
	objSQLExec.MoveNext
Wend
iAvailableLicenses = iCurrentLicenseTotal - iCurrentUserTotal
If iAvailableLicenses &lt; iLicenseThreshold Then
	Dim strMailBody
	strMailBody = iCurrentUserTotal &#038; " of " &#038; iCurrentLicenseTotal &#038; " licenses are in use."
	strMailBody = strMailBody &#038; "  It is time to order more licenses."
	SendMail
End If

Sub SendMail
	Set objEmail = CreateObject("CDO.Message")
	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = strMailServer
	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
	objEmail.Configuration.Fields.Update
	objEmail.From = strMailSender
	objEmail.To = strMailRecipients
	objEmail.Subject = strMailSubject
	objEmail.HTMLBody = strMailBody
	objEmail.Send
End Sub
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/get-automatic-notification-when-running-low-on-bes-licenses/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Convert a mailbox GUID to the user and display name</title>
		<link>http://www.flobee.net/convert-a-mailbox-guid-to-the-user-and-display-name/</link>
		<comments>http://www.flobee.net/convert-a-mailbox-guid-to-the-user-and-display-name/#comments</comments>
		<pubDate>Tue, 26 Feb 2008 20:39:21 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=277</guid>
		<description><![CDATA[If certain MAPI limits are reached when working with sessions, items, attachments, etc., Exchange will deny further access to that user to that object type.&#160; When this happens event ID 9646 is logged in the Application log.&#160; The description of the event contains a mailbox GUID that is causing the issue, but the GUID alone [...]]]></description>
			<content:encoded><![CDATA[<p>If certain MAPI limits are reached when working with sessions, items, attachments, etc., Exchange will deny further access to that user to that object type.&nbsp; When this happens event ID 9646 is logged in the Application log.&nbsp; The description of the event contains a mailbox GUID that is causing the issue, but the GUID alone does nothing to indicate what user/mailbox is affected.</p>
<p>Microsoft KB <a target="_blank" href="http://support.microsoft.com/kb/899663">899663</a>&nbsp;instructs how to manually transpose GUID into a format that can be used in an LDAP filter so that you can search for a match.&nbsp; Why do all this by hand when a script can do it for you?&nbsp; I took an existing script I had that already does the transposition and added an AD search to return the matching dn.&nbsp; The dn is passed to a name translation function that converts the dn to the NT4 format (domain\username) and displays the match with username and display name.</p>
<p>In addition to the VBScript file, I have also included a compiled version that uses SAPIEN Script Host as the engine.&nbsp; This is a self-contained, runs-in-memory-only, no-DOS-box-comes-up engine from PrimalScript.&nbsp; Running the compiled version is nice since you don&#8217;t have to ensure that CScript is the default host and no DOS box appears while the script is running.</p>
<p>The zip file with both versions is available <a href="http://www.flobee.net/download/MbxGUIDtoMbxName.zip">here </a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/convert-a-mailbox-guid-to-the-user-and-display-name/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Script to format an LDAP filter for readability</title>
		<link>http://www.flobee.net/script-to-format-an-ldap-filter-for-readability/</link>
		<comments>http://www.flobee.net/script-to-format-an-ldap-filter-for-readability/#comments</comments>
		<pubDate>Tue, 07 Aug 2007 14:32:07 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=226</guid>
		<description><![CDATA[I have automated DLs that use LDAP filters for&#160;membership criteria.&#160; These filters are stored as binary data in the extensionData attribute of the group object, so they are not easily accessible by users who want to know what filter is being applied to a DL they own. It was easy enough to extract the LDAP [...]]]></description>
			<content:encoded><![CDATA[<p>I have automated DLs that use LDAP filters for&nbsp;membership criteria.&nbsp; These filters are stored as binary data in the extensionData attribute of the group object, so they are not easily accessible by users who want to know what filter is being applied to a DL they own.</p>
<p>It was easy enough to extract the LDAP filter from the attribute, convert it to a string value, and display it.&nbsp; But users don&#8217;t generally know how to read LDAP filters, let alone represented as a long line of text.&nbsp; So I started looking for utilities or scripts that would take an LDAP filter, parse it, and display it with nesting.&nbsp; Uh, yeah, there aren&#8217;t any.&nbsp; I was determined, and so countless hours later I have a function that do such a thing.</p>
<p>The difficult part was keeping track of the level of nesting/indentation at any point. Since a filter can be written in almost any order as long as the resulting equation equals what you want, I couldn&#8217;t use any kind of static detection. Just because an open parentheses is following by another one doesn&#8217;t mean that you are, say, three levels nested. So the important part of the script keeps track of the indentation level as the cursor position moves through the filter.</p>
<p>I replace ampersands in the filter with crosshatches while working with it because I was having weird results otherwise, most likely because the ampersand is an operator in both LDAP and VBScript.  And because the output is to an IE window and the indentation uses non-breaking spaces instead of the traditional tab (which doesn&#8217;t exist in HTML), I substitute the HTML string for a non-breaking space with a unique string of letters so the ampersand in that doesn&#8217;t get in the way.</p>
<p>Because this is part of a bigger script, I pulled out just the portion that formats the LDAP filter and displays it in an IE window.  You just need to provide the &quot;raw&quot; filter, whether directly in the script or some other method. Copy and paste the code below or download it <a href="http://www.flobee.net/download/ParseLDAPFilter.zip" title="Parse LDAP filter for readbility">here</a>.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit
Dim strLDAPFilter, strFormattedFilter
Dim objIE, objDoc
strLDAPFilter = &quot;YourLDAPFilter&quot;
Set objIE =  CreateObject(&quot;InternetExplorer.Application&quot;) &nbsp;&nbsp;objIE.AddressBar = False
objIE.Menubar = False
objIE.Toolbar = False
objIE.Resizable = True
objIE.Height = 450
objIE.Width = 700
objIE.Visible = True
objIE.Navigate(&quot;about:blank&quot;)
While objIE.Busy
	WScript.Sleep 100
Wend

Set objDoc = objIE.Document
objDoc.Open
objDoc.Write(&quot;&lt;TITLE&gt;LDAP Filter Display&lt;/TITLE&gt;&quot;)
objDoc.Write(&quot;&lt;BODY BGCOLOR=#C0C0C0&gt;&quot;)

'Parse the LDAP filter and format results for display
'Apply nesting for readability
strFormattedFilter = FormatLDAPDisplay(strLDAPFilter)
objDoc.Write(strFormattedFilter)
'Format single-line LDAP filter to include nesting
Function FormatLDAPDisplay(strLDAPFilter)
	'Replace ampersands with crosshatches to keep them from interfering
	strLDAPFilter = Replace(strLDAPFilter, Chr(38), Chr(35))

	'Iterate through each character in filter and insert CRLF and nesting
	Dim intPos, intIndentCount, intWhileIndent
	Dim strIndentation, strInsert, strCharacter, strNewLDAPFilter
	Dim bolDblClose
	intPos = 1
	intIndentCount = 0
	Do While intPos &lt; Len(strLDAPFilter)
		strCharacter = Mid(strLDAPFilter, intPos, 1)
		intWhileIndent = 1
		strIndentation = &quot;&quot;
		Select Case strCharacter
			'LDAP operators to watch for to modify nesting level.
			'NOT operator ignored because only used in one-off attribute value
			Case Chr(35), Chr(124)
				'Operator followed by open paren means nesting increase
				If Mid(strLDAPFilter, intPos + 1, 1) = Chr(40) Then
					intIndentCount = intIndentCount + 1
					'Build nest based on number of indentations
					Do While intWhileIndent &lt;= intIndentCount
						'Use unique string as placeholder for nesting with HTML spaces
						strIndentation = strIndentation &amp; &quot;QZNBSPQZNBSPQZNBSPQZNBSP&quot;
						intWhileIndent = intWhileIndent + 1
					Loop
					'Insert new string for formatting
					strNewLDAPFilter = Replace(strLDAPFilter, strCharacter, strCharacter &amp; &quot;&lt;br&gt;&quot; &amp; strIndentation, intPos, 1)
					'Restore full filter including new string
					strLDAPFilter = Left(strLDAPFilter, intPos - 1) &amp; strNewLDAPFilter
					'Move current position to next character after inserted string
					intPos = intPos + Len(strIndentation) + 6
				Else
					intPos = intPos + 1
				End If
			Case Chr(40)
				Do While intWhileIndent &lt;= intIndentCount
					strIndentation = &quot;QZNBSPQZNBSPQZNBSPQZNBSP&quot; &amp; strIndentation
					intWhileIndent = intWhileIndent + 1
				Loop
				If Not strIndentation = &quot;&quot; Then
					'If open paren follows close paren, insert CRLF
					If Mid(strLDAPFilter, intPos - 1, 1) = Chr(41) Then
						strIndentation = strIndentation &amp; &quot;&lt;br&gt;&quot;
					End If
					strNewLDAPFilter = Replace(strLDAPFilter, strCharacter, strIndentation &amp; strCharacter, intPos, 1)
					strLDAPFilter = Left(strLDAPFilter, intPos - 1) &amp; strNewLDAPFilter
					intPos = intPos + Len(strIndentation) +1
				Else
					intPos = intPos + 1
				End If
			Case Chr(41)
				'Two consecutive close paren means nesting reduces one level
				If Mid(strLDAPFilter, intPos + 1, 1) = Chr(41) Then
					intIndentCount = intIndentCount - 1
					bolDblClose = True
				End If
				Do While intWhileIndent &lt;= intIndentCount
					strIndentation = strIndentation &amp; &quot;QZNBSPQZNBSPQZNBSPQZNBSP&quot;
					intWhileIndent = intWhileIndent + 1
				Loop
				strNewLDAPFilter = Replace(strLDAPFilter, strCharacter, strCharacter &amp; &quot;&lt;br&gt;&quot; &amp; strIndentation, intPos, 1)
				strLDAPFilter = Left(strLDAPFilter, intPos - 1) &amp; strNewLDAPFilter
				'Adjust position to account for two close paren
				If bolDblClose = True Then
					intPos = intPos + Len(strIndentation) + 5
					bolDblClose = Empty
				Else
					intPos = intPos + Len(strIndentation) + 6
				End If
			Case Else
				'No paren or operator means move to next character
				intPos = intPos + 1
		End Select
	Loop

	'Replace LDAP operators with words
	strLDAPFilter = Replace(strLDAPFilter, Chr(35), &quot;&lt;i&gt;AND&lt;/i&gt;&quot;)
	strLDAPFilter = Replace(strLDAPFilter, Chr(124), &quot;&lt;i&gt;OR&lt;/i&gt;&quot;)
	strLDAPFilter = Replace(strLDAPFilter, Chr(33), &quot;&lt;i&gt;NOT &lt;/i&gt;&quot;)
	'Replace spaceholders with HTML spaces
	strLDAPFilter = Replace(strLDAPFilter, &quot;QZNBSP&quot;, Chr(38) &amp; &quot;nbsp;&quot;)
	FormatLDAPDisplay = strLDAPFilter
End Function

Set objIE = Nothing
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/script-to-format-an-ldap-filter-for-readability/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Add comment notification to simplebog 3.0</title>
		<link>http://www.flobee.net/add-comment-notification-to-simplebog-3-0/</link>
		<comments>http://www.flobee.net/add-comment-notification-to-simplebog-3-0/#comments</comments>
		<pubDate>Mon, 30 Jul 2007 18:52:43 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=220</guid>
		<description><![CDATA[Since I am using approval for comments on my site, I had no way of knowing when someone posted a comment pending approval.&#160; And since you can&#8217;t simply look for comments to posts without going into the database directly, I needed a way to know when someone has posted a comment and to which post [...]]]></description>
			<content:encoded><![CDATA[<p>Since I am using approval for comments on my site, I had no way of knowing when someone posted a comment pending approval.&nbsp; And since you can&#8217;t simply look for comments to posts without going into the database directly, I needed a way to know when someone has posted a comment and to which post it belongs.</p>
<p>To do this, add this subroutine to the end of functions.asp, which is the CDO code to send a message.&nbsp; I didn&#8217;t use variables that are assigned in config.asp, so you will have to set them in the subroutine directly for SMTP server, from address, to address, and domain name in the body.&nbsp;</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:false">
&lt;%
'Send comment notification
Sub SendEmail (strBDate, strBID)
	Dim objMail
	Set objMail = CreateObject(&quot;CDO.Message&quot;)
	objMail.Configuration.Fields.Item (&quot;http://schemas.microsoft.com/cdo/configuration/sendusing&quot;)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 2
	objMail.Configuration.Fields.Item (&quot;http://schemas.microsoft.com/cdo/configuration/smtpserver&quot;)&nbsp;&nbsp;&nbsp;&nbsp; = &quot;SMTP hostname&quot;
	objMail.Configuration.Fields.Item (&quot;http://schemas.microsoft.com/cdo/configuration/smtpserverport&quot;) = 25
	objMail.Configuration.Fields.Update
	objMail.From&nbsp;&nbsp;&nbsp;&nbsp; = &quot;fromaddress@yourdomain.com&quot;
	objMail.To&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;toaddress@yourdomain.com&quot;
	objMail.Subject&nbsp; = &quot;Comment has been submitted for approval&quot;&nbsp;&nbsp;&nbsp;
	objMail.HTMLBody = &quot;A comment has been submitted for approval.&nbsp; Go to http://www.yourdomain.com/admin?cmd=bloglist&amp;view=calendar&amp;blogDate=&quot; &amp; strBDate &amp; &quot;&amp;comments=&quot; &amp; strBID &amp; &quot; to approve.&quot;
	objMail.Send
	Set objMail = Nothing
End Sub
%&gt;
</pre>
<p>Then you need to add code to functions.asp to call this subroutine from the subroutine that inserts the comment.&nbsp; Find the subroutine labeled InsertComment(), which should be around line 475 depending on other mods of mine or your own that you may have inserted.&nbsp; Right before the existing line:&nbsp;</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:475">
Response.Redirect(&quot;default.asp?view=plink&amp;id=&quot; &amp; bID &amp; &quot;&amp;comments=1&quot;)
</pre>
<p>insert this code:</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:474">
' convert blog ID to blog Date
strSQL = &quot;SELECT * FROM&nbsp; T_WEBLOG WHERE id = &quot; &amp; bID &amp; &quot; ORDER BY id DESC&quot;
Set Rs = Server.CreateObject(&quot;ADODB.Recordset&quot;)
Rs.ActiveConnection = strConn
Rs.Source = strSQL
Rs.CursorType = 0
Rs.CursorLocation = 2
Rs.LockType = 1
Rs.Open()

If Not rs.EOF Then
	rs.MoveFirst
	While Not rs.EOF
		strBDate = rs(&quot;b_date&quot;)
		rs.MoveNext
	Wend
End If

SendEmail strBDate, bID
</pre>
<p>This is necessary because the comments are not accessed by using the blog entry ID, but the blog entry&#8217;s date.&nbsp; So it is necessary to cross-reference the entry ID to the entry date, and then link to the comments for a given entry ID on that date.&nbsp; The last line calls the email function which will include a hyperlink to the comments for the blog entry that has a new comment.</p>
<p>Now, that is all fine and dandy.&nbsp; You will receive the email with the link, but when you follow it to your site, you probably won&#8217;t have an active session so you will have to log in.&nbsp; The way the admin default and login pages work, you will lose the link to the entry you need to approve so you have to click the link in the email again.&nbsp; I work around this by implementing the use of query strings so the site remembers where you were trying to go before you had to log in.</p>
<p>To add this feature, edit admindefault.asp.&nbsp; At line 8, replace the Response.Redirect line with the following code:&nbsp;</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:8">
Dim sProtocol, sDomain, sPath, sQuerystring, sResult
sProtocol = &quot;http://&quot;
If UCase(Request.ServerVariables(&quot;HTTPS&quot;)) = &quot;ON&quot; Then
	sProtocol = &quot;https://&quot;
	sDomain = LCase(Request.ServerVariables(&quot;SERVER_NAME&quot;))
	sPath = LCase(Request.ServerVariables(&quot;SCRIPT_NAME&quot;))
	sQuerystring = LCase(Request.Querystring)
	sResult = sProtocol &amp; sDomain &amp; sPath
	If Len(sQuerystring) &gt; 0 Then
		sResult = sResult &amp; &quot;?&quot; &amp; sQuerystring
		sResult = Server.URLEncode(sResult)
		Response.Redirect(&quot;login.asp&quot;) &amp; &quot;?sURL=&quot; &amp; sResult
</pre>
<p>This builds a query string of the URL you were going to go to before you are redirected to the login page.&nbsp; Now edit adminlogin.asp.&nbsp; At line 34, before the If statement to check for a postback, insert the following line:&nbsp;</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:34">
strSourceURL = Request.QueryString(&quot;sURL&quot;)
</pre>
<p>This retrieves the query string and puts it into a variable.&nbsp; At line 39, before the SQL statement to select the users from the database, insert the following code:&nbsp;</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:39">
If Not Trim(Request.Form(&quot;sourceURL&quot;)) = &quot;&quot; Then
	strSourceURL = Request.Form(&quot;sourceURL&quot;)
Else
	strSourceURL = &quot;./?&quot;
End If
</pre>
<p>This extra code is added to accommodate you entering a bad password or if you are logging in without following a comment link.&nbsp; At line 74, comment out Johann&#8217;s Response.Redirect line after a successful login and then insert and replace the Response.Redirect for no user found with this:&nbsp;</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:74">
	Response.Redirect(strSourceURL)
Else
	Response.Redirect(&quot;login.asp?error=nouser&quot;) &amp; &quot;&amp;sURL=&quot; &amp; Server.URLEncode(strSourceURL)
</pre>
<p>This sends you to the page you were intending to go to in the first place.&nbsp; If you entered a bad password, this also preserves the original URL after the login page reloads.</p>
<p>Last, but not least, you need to a hidden form field that will store the original URL when you submit the form to log in.&nbsp; Near the end of the file, insert a line before the close form tag and paste this:&nbsp;</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:false">
&lt;input name=&quot;sourceURL&quot; type=&quot;hidden&quot; value=&quot;&lt;%= strSourceURL %&gt;&quot; /&gt;
</pre>
<p>I hope that&#8217;s not too complicated to follow.&nbsp; It&#8217;s a little more convoluted for this modification because it is necessary to add code in multiple places in multiple files, rather than just a chunk of code in one file.&nbsp; But, now you will receive an email when a comment is posted and be easily able to approve or deny the comment just by following the link in the email.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/add-comment-notification-to-simplebog-3-0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Updated: Copy DLs from one user to another</title>
		<link>http://www.flobee.net/updated-copy-dls-from-one-user-to-another/</link>
		<comments>http://www.flobee.net/updated-copy-dls-from-one-user-to-another/#comments</comments>
		<pubDate>Sun, 22 Jul 2007 23:01:09 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=202</guid>
		<description><![CDATA[The first version of the script really was quick and dirty, requiring you to manually put the source and target users&#8217; DNs in the script.&#160; Since a coworker has been using the script, I thought it appropriate to update it to prompt for the usernames.&#160; In addition, I added a new feature I recently read [...]]]></description>
			<content:encoded><![CDATA[<p>The first version of the script really was quick and dirty, requiring you to manually put the source and target users&#8217; DNs in the script.&nbsp; Since a coworker has been using the script, I thought it appropriate to update it to prompt for the usernames.&nbsp; In addition, I added a new feature I recently read about, which is to output the results in real-time to a GUI.&nbsp; This is done by creating an object for IE and writing the output similar to wscript.echo, but with the Write method of the object.</p>
<p>Like the original script, since we use automated DLs, too, I look for an indication that a given DL is a SmartDL and skip it.&nbsp; And I now use PrimalScript to work with my scripts, so I use its packager to make an exectuable.&nbsp; This makes it easier and nicer for non-IT end-users who will be running scripts like these.</p>
<p>Download it <a href="http://www.flobee.net/download/CopyDLMembership.zip">here</a>, or copy/paste below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
'Version 2.0 - July 23, 2007
'Copy distribution group membership from one user to another,
'excluding automated DLs (SmartDL).
'Get source user
While Not bolExit = True
	strOldSamUser = InputBox(&quot;Enter the sAMAccountName of the person to copy DLs FROM.&quot; _
		, &quot;Enter username&quot;)
	If strOldSamUser = &quot;&quot; Then
		WScript.Quit
	End If

	'Find the Global Catalog server
	Set objCont = GetObject(&quot;GC:&quot;)
	For Each objGC In objCont
		strADsPath = objGC.ADsPath
	Next

	Set objConnection = CreateObject(&quot;ADODB.Connection&quot;)
	Set objRecordset = CreateObject(&quot;ADODB.Recordset&quot;)
	objConnection.Provider = &quot;ADsDSOObject&quot;

	objConnection.Open &quot;ADs Provider&quot;
	strQuery = &quot;&lt;&quot; &amp; strADsPath &amp; &quot;&gt;;(&amp;(objectcategory=user)(sAMAccountName=&quot; &amp; strOldSamUser &amp; _
		&quot;));displayName,distinguishedName;subtree&quot;
	Set objRecordset = objConnection.Execute(strQuery)

	If Trim(objRecordset.Fields(&quot;distinguishedName&quot;)) = &quot;&quot; Then
		strNoUser = MsgBox(&quot;Warning: User cannot be found.&nbsp; Verify sAMAccountName.&quot;, vbCritical, &quot;User not found!&quot;)
		bolExit = False
	Else
		intCorrectUser = MsgBox(&quot;Is this the correct user?&quot; &amp; VbCrLf &amp; VbCrLf &amp; &quot;Display Name: &quot; &amp; _
		objRecordset.Fields(&quot;displayName&quot;) &amp; VbCrLf &amp; &quot;DN: &quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;), _
			vbYesNo, &quot;Old user?&quot;)
		If intCorrectUser &lt;&gt; 6 Then
			bolExit = False
		Else
			strSrcDN = objRecordset.Fields(&quot;distinguishedName&quot;)
			bolExit = True
		End If
	End If
 Wend
'Open IE to display progress and results
Set objIE = CreateObject(&quot;InternetExplorer.Application&quot;)
objIE.AddressBar = False
objIE.Menubar = False
objIE.Toolbar = False
objIE.Resizable = True
objIE.Left = 10
objIE.Height = 450
objIE.Width = 800
objIE.Visible = True
objIE.Navigate(&quot;about:blank&quot;)
While objIE.Busy
	WScript.Sleep 100
Wend
Set objDoc = objIE.Document
objDoc.Open
objDoc.Write(&quot;&lt;TITLE&gt;Copy DL Membership&lt;/TITLE&gt;&quot;)
objDoc.Write(&quot;&lt;BODY BGCOLOR=#C0C0C0&gt;&quot;)
objDoc.Write(&quot;&lt;P&gt;&lt;b&gt;Source:&lt;/b&gt; &quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;) &amp; &quot;&lt;br&gt;&quot;)
'Get target user
bolExit = False
While Not bolExit = True
	strNewSamUser = InputBox(&quot;Enter the sAMAccountName of the person to copy DLs TO.&quot; _
		, &quot;Enter username&quot;)
	If strNewSamUser = &quot;&quot; Then
		objIE.Quit
		WScript.Quit
	End If
	strQuery = &quot;&lt;&quot; &amp; strADsPath &amp; &quot;&gt;;(&amp;(objectcategory=user)(sAMAccountName=&quot; &amp; strNewSamUser &amp; _
		&quot;));displayName,distinguishedName;subtree&quot;
	Set objRecordset = objConnection.Execute(strQuery)
	If Trim(objRecordset.Fields(&quot;distinguishedName&quot;)) = &quot;&quot; Then
		strNoUser = MsgBox(&quot;Warning: User cannot be found.&nbsp; Verify sAMAccountName.&quot;, vbCritical, &quot;User not found!&quot;)
		bolExit = False
	Else
		intCorrectUser = MsgBox(&quot;Is this the correct user?&quot; &amp; VbCrLf &amp; VbCrLf &amp; &quot;Display Name: &quot; &amp; _
			objRecordset.Fields(&quot;displayName&quot;) &amp; VbCrLf &amp; &quot;DN: &quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;), _
			vbYesNo, &quot;Old user?&quot;)
		If intCorrectUser &lt;&gt; 6 Then
			bolExit = False
		Else
			Set objTargetUser = GetObject(&quot;LDAP://&quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;))
			bolExit = True
		End If
	End If
 Wend
 'Write target user to IE window
 objDoc.Write(&quot;&lt;b&gt;Target:&lt;/b&gt; &quot; &amp; objRecordset.Fields(&quot;distinguishedName&quot;) &amp; &quot;&lt;/P&gt;&quot;)
 'Copy DLs
 strDomFQDN = Mid(strSrcDN, InStr(LCase(strSrcDN), &quot;,dc=&quot;) + 4)
 strGCFQDN = Replace(LCase(strDomFQDN), &quot;,dc=&quot;, &quot;.&quot;)
 Set objOldUser = GetObject(&quot;GC://&quot; &amp; strGCFQDN &amp; &quot;/&quot; &amp; strSrcDN)
 For Each strGroup in objOldUser.MemberOf
	On Error Resume Next
	Set objGroup = GetObject(&quot;LDAP://&quot; &amp; strGroup)
	If Not Trim(objGroup.mailNickname) = &quot;&quot; Then
		If Not Instr(objGroup.info, &quot;SmartDL&quot;) &gt; 0 Then
			objGroup.Add(objTargetUser.ADsPath)
			If Err.Number = 0 Then
				objDoc.Write(objGroup.DisplayName &amp; &quot;: Update successful.&lt;br&gt;&quot;)
			Else
				objDoc.Write(objGroup.DisplayName &amp; &quot;: Update UNSUCCESSFUL.&lt;br&gt;&quot;)
			End If
		Else
			objDoc.Write(objGroup.DisplayName &amp; &quot;: Skipped (SmartDL).&lt;br&gt;&quot;)
		End If
	End If
	On Error Goto 0
 Next

 Set objOldUser = Nothing
 Set objTargetUser = Nothing
 Set objIE = Nothing
 Set objRecordset = Nothing
 Set objConnection = Nothing
 </pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/updated-copy-dls-from-one-user-to-another/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to add CAPTCHA to simpleblog 3.0</title>
		<link>http://www.flobee.net/how-to-add-captcha-to-simpleblog-3-0/</link>
		<comments>http://www.flobee.net/how-to-add-captcha-to-simpleblog-3-0/#comments</comments>
		<pubDate>Sun, 22 Jul 2007 04:34:30 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=198</guid>
		<description><![CDATA[Johann almost added CAPTCHA verification to simpleblog 3, but he notes on his blog that he decided not to after looking at the pros and cons.&#160; He says that simpleblog isn&#8217;t a target of comment spam because of the inability to post html or javascript code into a comment.&#160; I disagree, however, because you still [...]]]></description>
			<content:encoded><![CDATA[<p>Johann almost added CAPTCHA verification to simpleblog 3, but he notes on his <a href="http://blog.8pixel.net" target="_blank">blog</a> that he decided not to after looking at the pros and cons.&nbsp; He says that simpleblog isn&#8217;t a target of comment spam because of the inability to post html or javascript code into a comment.&nbsp; I disagree, however, because you still get targeted by comment spam by the very nature that bots will still post comments.&nbsp; Even with approval enabled, I still have to delete the pending comments, and there can be A LOT of them.</p>
<p>Johann included a copy of Emir Tuzul&#8217;s free <a href="http://www.tipstricks.org" target="_blank">ASP CAPTCHA implementation</a>, but never incorporated it into simpleblog.&nbsp; I looked at how the code works and how Johann implemented comments, and I have successfully added CAPTCHA verification to the comments system.&nbsp; Since doing so a few days ago, not a single spam comment has been left.&nbsp; If you are interested, this is how to do it.</p>
<p>Since Johann included version 2 of the CAPTCHA code page, you do not need to download anything, but you can opt to use<a href="http://www.tipstricks.org/aspsig/examplev3.asp" target="_blank"> version 3 beta 1</a>, which uses more character obfuscation to make it harder for bots to determine the characters in the image.</p>
<p>Edit functions.asp to add the following code to the end of the file, which is the verification function:</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:false">
&lt;%
Function CheckCAPTCHA(valCAPTCHA)
	SessionCAPTCHA = Trim(Session(&quot;CAPTCHA&quot;))
	Session(&quot;CAPTCHA&quot;) = vbNullString
	If Len(SessionCAPTCHA) &lt; 1 Then
		CheckCAPTCHA = False
		Exit Function
	End if
	If CStr(SessionCAPTCHA) = CStr(valCAPTCHA) Then
		CheckCAPTCHA = True
	Else
		CheckCAPTCHA = False
	End if
End Function
%&gt;
</pre>
<p>Add the following code to functions.asp in the CommentsGet subroutine, which for me starts at line 351.&nbsp; It may be different for you since I think I have added other code higher in the file.&nbsp; This adds the actual CAPTCHA image to the comments form.&nbsp; You will add this code after the call for GetEmoticons and the line break, which for me means inserting this at line 454:</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:454">
Type the characters shown in the image for verification.
&lt;img src=&quot;captcha.asp&quot; alt=&quot;&quot; width=&quot;86&quot; height=&quot;21&quot; /&gt;
&lt;input name=&quot;strCAPTCHA&quot; type=&quot;text&quot; id=&quot;strCAPTCHA&quot; maxlength=&quot;8&quot; /&gt;&lt;/td&gt;
</pre>
<p>At line 481 (after the declaration of the str_userIP variable), insert this, which puts the characters entered into the form in a variable:</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:481">
strCAPTCHA = Trim(Request.Form(&quot;strCAPTCHA&quot;))
</pre>
<p>Lastly, replace the code that inserts the comment into the database with the code below, starting at line 492 (after the comment&nbsp; &quot;insert Comment.&quot;&nbsp; Instead of simply inserting the comment into the database, this will compare the entered characters to the actual ones in the image.&nbsp; If they match, the comment is inserted.&nbsp; If not, I use a JavaScript alert to present a popup box and then redirect the user back to the post:</p>
<pre class="brush:vb;gutter:true;wrap-lines:false;auto-links:false;collapse:false;first-line:492">
If CheckCAPTCHA(strCAPTCHA) = True Then
	SQL = &quot;INSERT INTO T_COMMENTS(c_content, c_name, c_email, c_url, c_bID_fk,ip) VALUES ('&quot; &amp; strComment &amp; &quot;','&quot; &amp; sanitize( strName ) &amp; &quot;','&quot; &amp; sanitize( strEmail ) &amp; &quot;','&quot; &amp;&nbsp; sanitize( strUrl )&amp; &quot;',&quot; &amp;&nbsp; sanitize( bID )&amp; &quot;,'&quot;&amp;str_userIP&amp;&quot;')&quot;
	Set MyConn = Server.CreateObject(&quot;ADODB.Connection&quot;)
	MyConn.Open strConn
	MyConn.Execute(strSQL)
	MyConn.Close
	Set MyConn = Nothing
	Response.Redirect(&quot;default.asp?view=plink&amp;id=&quot; &amp; bID &amp; &quot;&amp;comments=1&quot;)
Else
	%&gt;
	&lt;script language=&quot;Javascript&quot;&gt;
		alert('You did not type the verification code correctly.');
		location.replace('default.asp?view=plink&amp;id=&lt;%= bID %&gt;&amp;comments=1');
	&lt;/script&gt;
	&lt;%
End If
</pre>
<p>You&#8217;re done!&nbsp; Save functions.asp and then go add a comment to one of your posts.&nbsp; Intentionally enter incorrect characters to confirm the popup works and that the comment did not get added.&nbsp; The only thing missing from this is that it doesn&#8217;t preserve the comment in the session.&nbsp; This means that if a real person incorrectly enters the code, when returned to the post to try and enter another code, the actual comment data will have to be entered again.&nbsp; Name, email, and URL don&#8217;t have to because they are stored in a cookie on the client.&nbsp; Perhaps I will add that at a later time.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/how-to-add-captcha-to-simpleblog-3-0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick and dirty script to copy DLs from one user to another</title>
		<link>http://www.flobee.net/quick-and-dirty-script-to-copy-dls-from-one-user-to-another/</link>
		<comments>http://www.flobee.net/quick-and-dirty-script-to-copy-dls-from-one-user-to-another/#comments</comments>
		<pubDate>Sun, 04 Feb 2007 23:07:35 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=187</guid>
		<description><![CDATA[Edit: The inline code in this post is not the latest version of the script. Get the latest version from the downloads page. It is not uncommon at my company to have to move a user from one domain to another for technical, logistical, or political reasons.  For another set of reasons, moving the user [...]]]></description>
			<content:encoded><![CDATA[<p><font color="red">Edit:  The inline code in this post is not the latest version of the script.  Get the latest version from the <a href="/downloads">downloads</a> page.</font></p>
<p>It is not uncommon at my company to have to move a user from one domain to another for technical, logistical, or political reasons.  For another set of reasons, moving the user account to the other domain is not done, instead manually creating a new one and associating the mailbox with the new account.</p>
<p>DL membership does not automatically get updated when this is done, so I have been doing it manually.  It has been on my to-do list for awhile to write a script to copy the DL membership from the old account to the new one.  So I threw this together this morning.  It lacks some of the nice extras my other scripts have (finding the user by logon name, email results, true logging) but it does work.</p>
<p>You have to edit the script to give it the variables for the old and new users&#8217; dn.  It will skip security groups (any group without an alias) and also groups whose Notes (info) attribute contains the word SmartDL.  We use Imanami&#8217;s SmartDL for automated DL membership when applicable.  Those will be automatically updated the next time each of their jobs run.</p>
<p>Download it <a title="Copy DLs from one user to another" href="http://www.flobee.net/download/CopyDLMembership.zip">here</a>, or copy/paste below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit
Dim strOldUser, strNewUser, objOldUser, objNewUser, strGroup, objGroup
strOldUser = &quot;&quot; 'dn of user to copy from'
strNewUser = &quot;&quot; 'dn of user to copy to'
Set objOldUser = GetObject(&quot;LDAP://&quot; &amp; strOldUser)
Set objNewUser = GetObject(&quot;LDAP://&quot; &amp; strNewUser)
wscript.echo &quot;Source user: &quot; &amp; objOldUser.DisplayName
wscript.echo &quot;Target user: &quot; &amp; objNewUser.DisplayName
For Each strGroup in objOldUser.MemberOf
	On Error Resume Next
	Set objGroup = GetObject(&quot;LDAP://&quot; &amp; strGroup)
    If Not Trim(objGroup.mailNickname) = &quot;&quot; Then
        If Not Instr(objGroup.info, &quot;SmartDL&quot;) &gt; 0 Then
			objGroup.Add(objNewUser.ADsPath)
			If Err.Num = 0 Then
				wscript.echo objGroup.DisplayName &amp; &quot;: Update successful.&quot;
			Else
				wscript.echo objGroup.DisplayName &amp; &quot;: Update UNSUCCESSFUL.&quot;
			End If
        Else
			wscript.echo objGroup.DisplayName &amp; &quot;: Skipped (SmartDL).&quot;
        End If
	End If
	On Error Goto 0
Next
Set objOldUser = Nothing
Set objNewUser = Nothing
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/quick-and-dirty-script-to-copy-dls-from-one-user-to-another/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Updated Last Backup Report script</title>
		<link>http://www.flobee.net/updated-last-backup-report-script/</link>
		<comments>http://www.flobee.net/updated-last-backup-report-script/#comments</comments>
		<pubDate>Mon, 04 Dec 2006 18:17:33 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[VBScript]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=175</guid>
		<description><![CDATA[I had previously posted (here) a script to create an email that reports the last full backup time of every database in the organization.  There wasn&#8217;t too much in the way of error correcting, so the script in my environment was failing to send anything when an Exchange server went offline recently (but is still [...]]]></description>
			<content:encoded><![CDATA[<p>I had previously posted (<a title="Last Exchange Backup report" href="http://www.flobee.net/report-the-last-time-your-exchange-servers-were-backed-up/">here</a>) a script to create an email that reports the last full backup time of every database in the organization.  There wasn&#8217;t too much in the way of error correcting, so the script in my environment was failing to send anything when an Exchange server went offline recently (but is still in AD).</p>
<p>I have updated the script, which can be downloaded <a title="Last Exchange Backup script" href="http://www.flobee.net/download/lastbackup.zip">here</a>, to not error when this happens and include if it is unable to connect to a specific server and read the last full backup time.  The HTML rendering didn&#8217;t look right for the server reported with an error, and I couldn&#8217;t figure out why, so also updated how the table is built.  Now the report is one big table instead of every server in its own table.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/updated-last-backup-report-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Updated Exchange ActiveSync disable script</title>
		<link>http://www.flobee.net/updated-exchange-activesync-disable-script/</link>
		<comments>http://www.flobee.net/updated-exchange-activesync-disable-script/#comments</comments>
		<pubDate>Sun, 03 Dec 2006 19:48:33 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=172</guid>
		<description><![CDATA[I previously posted (here) a script to disable Exchange ActiveSync for unauthorized users.  The script is based on using the dn of one or more groups to determine authorized users (i.e., if you are in the group, you are allowed).  This method recently failed me (twice) because some of the groups were renamed by another admin [...]]]></description>
			<content:encoded><![CDATA[<p>I previously posted (<a title="Disable Exchange ActiveSync post" href="http://www.flobee.net/script-to-disable-exchange-activesync-for-unauthorized-users/">here</a>) a script to disable Exchange ActiveSync for unauthorized users.  The script is based on using the dn of one or more groups to determine authorized users (i.e., if you are in the group, you are allowed).  This method recently failed me (twice) because some of the groups were renamed by another admin to make them more readable.  Not only were the display names changed, but the objects were renamed as well, so the cn and dn were changed.  This means my script couldn&#8217;t find the groups I had hard-coded, which isn&#8217;t good scripting practice anyway.</p>
<p>I have updated the script, which can be downloaded <a title="Exchange ActiveSync disable script" href="http://www.flobee.net/download/EASDisable.zip">here</a>, to search for the groups based on their objectGUID, which never changes no matter what you do to the object (rename, move, etc.).  Instead of hard-coding the dn of the object, I hard-code the objectGUID.  Then I bind directly to the object based on the GUID to retrieve its dn, which is used in the search filter to find the users I want to modify.</p>
<p>Note that ADO allows you to bind to an object given just its GUID, without having to specify other connection parameters.  You can also use the hexadecimal or binary format of the GUID in the connection string; AD will figure out which format you are using. I used the binary format in my script so that I could just copy and paste the value from <a title="SystemTools' Hyena" href="http://www.systemtools.com/hyena/index.html" target="_blank">Hyena</a> into my script without having to convert to a hex value.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/updated-exchange-activesync-disable-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Change users&#8217; default calendar permission (and email the users and admin)</title>
		<link>http://www.flobee.net/change-users-default-calendar-permission-and-email-the-users-and-admin/</link>
		<comments>http://www.flobee.net/change-users-default-calendar-permission-and-email-the-users-and-admin/#comments</comments>
		<pubDate>Wed, 21 Jun 2006 23:49:31 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=152</guid>
		<description><![CDATA[Our IT Leadership team decided that it will promote more effective collaboration if the Default calendar permission for all IT employees is set to Reviewer (instead of the Default of None). As with many of my scripts, I started with the work Glen Scales has already done. Go there for information on the prerequisite for [...]]]></description>
			<content:encoded><![CDATA[<p>Our IT Leadership team decided that it will promote more effective collaboration if the Default calendar permission for all IT employees is set to Reviewer (instead of the Default of None). As with many of my scripts, I started with the work <a href="http://gsexdev.blogspot.com/2005/05/changing-default-permissions-on.html" target="_blank" alt="Glen Scales' original script to set the default permission to Reviewer.">Glen Scales</a> has already done. Go there for information on the prerequisite for using this script to make the change (the need for ACL.dll).</p>
<p>Glen&#8217;s script uses an input argument of a server name. My distributed environment doesn&#8217;t allow for such a broad application, so I use the membership of a DL that contains the IT personnel. This DL actually contains other DLs, so I separately use another <a href="http://www.rlmueller.net/List%20Members%20of%20a%20Group.htm" target="_blank" alt="VBSCript to enumerate all members of a group, event nested members.">script</a> by Richard Mueller that enumerates nested groups and puts the results into a domain local group that is used for other purposes.</p>
<p>After getting the group members, I loop through each member for only those with a mailbox that isn&#8217;t hidden. For those who haven&#8217;t been touched by the script before, a function is called that uses CDO to log into the mailbox, enumerate the calendar permissions looking for the Default entry, and change it to Reviewer. The user&#8217;s local FreeBusy Data folder is also updated to set the Default to Editor since that permission goes hand in hand when setting permissions on the calendar.</p>
<p>To keep tabs on who the script has touched, a notation is added to extensionAttribute4 (customizable). Those with that notation are skipped in the future. Then an email is sent to the user informing them of the change. I chose to do this because Exchange users may be used to the fact the the Default permission is None; this way they are aware that their calendar is open for viewing. It also helps with new employees who are not aware of this &quot;policy&quot; and can mark items as Private as necessary.</p>
<p>Finally, an email is sent to the admin with the results of the job run. It includes the display name, mailbox server, and whether the permission was set or the object skipped (to catch entries that slip through my filter). I have scheduled this to run weekly, and I will get an email each time so I know that the script is successfully running and which users are being modified.</p>
<p>All of the variables that require customization are near the top. These are for mail configuration, search filter, and the notation parameters for touched mailboxes.</p>
<p>Download it <a href="http://www.flobee.net/download/DefaultCalPerm.zip">here</a> or copy below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit

Public Const CdoDefaultFolderCalendar = 0
Dim strSMTPServer, strMailFrom, strUserMailSubject, strUserMailBody
Dim strAdminMailRecip, strAdminMailSubject, strAdminMailBody
Dim strDefaultNamingContext, strQueryFilter, strAttName, strAttNote
Dim conn, com, iAdRootDSE, strNamingContext
Dim Rs, objGroup, strMember, objUser, strMsExchHomeServerName
Dim objSession, CdoInfoStore, CdoFolderRoot, ACLObj, CdoCalendar, FolderACEs, fldACE
Dim objRoot, objFreeBusyFolder

'Configuration parameters:
''''''''''''''''''''''''''''''''''''''''''''''
'Email notification configuration
strSMTPServer = &quot;server.company.com&quot; 'FQDN of SMTP server
strMailFrom = &quot;&quot;&quot;Display Name&quot;&quot; &lt;a href="mailto:SMTPAddress@company.com"&gt;SMTPAddress@company.com&lt;/a&gt;&quot; ' Display name and address of sender
strUserMailSubject = &quot;The default permission on your calendar has been updated&quot; 'Subject of message sent to users
strUserMailBody = &quot;In order to promote effective collaboration among the Company &quot; &amp; _
    &quot;employees, the Default permission on your calendar has been updated to Reviewer.  This allows anyone within the organization &quot; &amp; _
    &quot;to see the details of items within your calendar.  For items that are of a sensitive, confidential, or personal nature, &quot; &amp; _
    &quot;you can mark them as Private.  This will restrict the details of the item so that others cannot see the subject or body.  &quot; &amp; _
    &quot;If you have any questions about this change, please reply to this message or contact the Help Desk at XXXX.&lt;br&gt;&lt;br&gt;&quot; &amp; _
    &quot;Company Messaging and Collaboration Team (GAL Display Name)&lt;br&gt;Department&lt;br&gt;Company&quot; 'Body of message sent to users
strAdminMailRecip = &quot;&lt;a href="mailto:SMTPAddress@company.com"&gt;SMTPAddress@company.com&lt;/a&gt;&quot; 'Address of admin to receive status report
strAdminMailSubject = &quot;Default calendar permission change report&quot; 'Subject of message sent to admin
'Domain to search for object
strDefaultNamingContext = &quot;dc=company,dc=com&quot; 'AD FQDN of base scope to search
'Search filter for group with users
strQueryFilter = &quot;(&amp;(objectcategory=group)(displayName=something))&quot; 'LDAP filter to locate group with members to modify

'Attribute and notation for updated objects
strAttName = &quot;extensionAttribute4&quot; 'defined for a single-valued string attribute
strAttNote = &quot;DefaultCalSet&quot; 'Notation used to skip processed users on future script runs
''''''''''''''''''''''''''''''''''''''''''''''
strAdminMailBody = &quot;&quot;
Set conn = createobject(&quot;ADODB.Connection&quot;)
Set com = createobject(&quot;ADODB.Command&quot;)
Set iAdRootDSE = GetObject(&quot;&lt;a href="ldap://RootDSE"&gt;LDAP://RootDSE&lt;/a&gt;&quot;)
strNamingContext = iAdRootDSE.Get(&quot;configurationNamingContext&quot;)
Conn.Provider = &quot;ADsDSOObject&quot;
Conn.Open &quot;ADs Provider&quot;
Com.ActiveConnection = Conn
com.Properties(&quot;Page Size&quot;) = 1000
Com.CommandText = &quot;&lt;GC://&quot; &amp; strDefaultNamingContext &amp; &quot;&gt;;&quot; &amp; StrQueryFilter &amp; &quot;;distinguishedname;subtree&quot;
Set Rs = Com.Execute
While Not Rs.EOF
	Set objGroup = GetObject(&quot;LDAP://&quot; &amp; Rs.fields(&quot;distinguishedname&quot;))
	For each strMember in objGroup.Member
		Set objUser = GetObject(&quot;LDAP://&quot; &amp; strMember)
		If InStr(objUser.Get(strAttName), strAttNote) &lt; 1 Then
			strAdminMailBody = strAdminMailBody &amp; objUser.displayName &amp; &quot;&lt;br&gt;&quot;
			If objUser.Class = &quot;user&quot; And Not IsNull(objUser.msExchHomeServerName) And Not UCase(objUser.msExchHidefromAddressLists) = &quot;TRUE&quot; Then
				strMsExchHomeServerName = objUser.msExchHomeServerName
				strMsExchHomeServerName = right(strMsExchHomeServerName,len(strMsExchHomeServerName)-instrrev(strMsExchHomeServerName,&quot;/cn=&quot;)-3)
				strAdminMailBody = strAdminMailBody &amp; &quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot; &amp; strMsExchHomeServerName &amp; &quot;&lt;br&gt;&quot;
				Call dofreebusy(strMsExchHomeServerName, objUser.mailNickname)
				strAdminMailbody = strAdminMailBody &amp; &quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Permission set on: &quot; &amp; objUser.mailNickname &amp; &quot;&lt;br&gt;&quot;
				WriteTag
				SendEmail objUser.mail, strUserMailSubject, strUserMailBody
			Else
				strAdminMailBody = strAdminMailBody &amp; &quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Skipping object&lt;br&gt;&quot;
			End If
		End If
	Next
	Rs.MoveNext
Wend
Rs.Close

'Send admin email report
If Not strAdminMailBody = &quot;&quot; Then
	strAdminMailBody = strAdminMailBody &amp; &quot;&lt;br&gt;Done.&lt;br&gt;&quot;
Else
	strAdminMailBody = &quot;No mailboxes needed updating.&quot;
End If
SendEmail strAdminMailRecip, strAdminMailSubject, strAdminMailBody

Set conn = Nothing
Set com = Nothing

Function dofreebusy(serverName, mailboxName)
	'Set Default permission to Reviewer
	Set objSession = CreateObject(&quot;MAPI.Session&quot;)
	objSession.Logon &quot;&quot;,&quot;&quot;,false,true,0,true,servername &amp; vbLF &amp; mailboxname
	Set CdoInfoStore = objSession.GetInfoStore
	Set CdoFolderRoot = CdoInfoStore.RootFolder
	Set ACLObj = CreateObject(&quot;MSExchange.aclobject&quot;)
	Set CdoCalendar = objSession.GetDefaultFolder(CdoDefaultFolderCalendar)
	ACLObj.CdoItem = CdoCalendar
	Set FolderACEs = ACLObj.ACEs
	For each fldACE in FolderACEs
		If fldACE.ID = &quot;ID_ACL_DEFAULT&quot; Then
			fldACE.Rights = 1025
			ACLObj.Update
		End If
	Next

	'Set local FreeBusy folder permission to Editor
	Set objRoot = objSession.GetFolder(&quot;&quot;)
	Set objFreeBusyFolder = objRoot.Folders.Item(&quot;FreeBusy Data&quot;)
	ACLObj.CdoItem = objFreeBusyFolder
	Set FolderACEs = ACLObj.ACEs
	For each fldACE in FolderACEs
		If fldACE.ID = &quot;ID_ACL_DEFAULT&quot; Then
		fldACE.Rights = 1123
		ACLObj.Update
		End If
	Next
End function

'Write notation tag into attribute
Sub WriteTag()
	If IsNull(objUser.Get(strAttName)) or IsEmpty(objUser.Get(strAttName)) Then
		objUser.Put strAttName, strAttNote &amp; &quot;;&quot;
	Else
		Dim strExistAttName
		strExistAttName = objUser.Get(strAttName)
		objUser.Put strAttName, strExistAttName &amp; strAttNote &amp; &quot;;&quot;
	End If
	objUser.SetInfo
End Sub

'Send change notification and report
Sub SendEmail (strRecipAddress, strMailSubject, strMailBody)
	Dim objMail
	Set objMail = CreateObject(&quot;CDO.Message&quot;)
	objMail.Configuration.Fields.Item (&quot;<a href="http://schemas.microsoft.com/cdo/configuration/sendusing">http://schemas.microsoft.com/cdo/configuration/sendusing</a>&quot;) = 2
	objMail.Configuration.Fields.Item (&quot;<a href="http://schemas.microsoft.com/cdo/configuration/smtpserver">http://schemas.microsoft.com/cdo/configuration/smtpserver</a>&quot;) = strSMTPServer
	objMail.Configuration.Fields.Item (&quot;<a href="http://schemas.microsoft.com/cdo/configuration/smtpserverport">http://schemas.microsoft.com/cdo/configuration/smtpserverport</a>&quot;) = 25
	objMail.Configuration.Fields.Update

 objMail.From = strMailFrom
	objMail.To = strRecipAddress
	objMail.Subject = strMailSubject
	objMail.HTMLBody = strMailBody
	objMail.Send
	Set objMail = Nothing
End Sub
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/change-users-default-calendar-permission-and-email-the-users-and-admin/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Script to disable Exchange ActiveSync for unauthorized users</title>
		<link>http://www.flobee.net/script-to-disable-exchange-activesync-for-unauthorized-users/</link>
		<comments>http://www.flobee.net/script-to-disable-exchange-activesync-for-unauthorized-users/#comments</comments>
		<pubDate>Thu, 01 Jun 2006 16:09:11 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=147</guid>
		<description><![CDATA[By default, users have all mobile services enabled (OMA, EAS including AUTD/DP).  This is a pain in my environment because only authorized users are allowed to use EAS (to ensure only approved devices procured through proper channels are used).  OMA, being similar to OWA, is allowed for everyone. I had written a batch file long ago [...]]]></description>
			<content:encoded><![CDATA[<p>By default, users have all mobile services enabled (OMA, EAS including AUTD/DP).  This is a pain in my environment because only authorized users are allowed to use EAS (to ensure only approved devices procured through proper channels are used).  OMA, being similar to OWA, is allowed for everyone.</p>
<p>I had written a batch file long ago to change the bitmask attribute for users whose mobile services are enabled for everything (0) and are not in the appropriate DL of authorized users to disable only EAS (5).  It was an inefficient script that required explicit permissions for each domain, called a command-line regex tool to format the ldifde export, and was prone to errors.</p>
<p>This updated script accomplishes the same thing, but more efficiently.  It processes all users at one time (inside a for loop) and uses implicit permissions.  It even emails the results of the number of users modified.  The script is customized for my environment, but you can tweak it as necessary.  </p>
<p>I have five user domains, but the DLs for authorization are in one domain.  I wanted it to be as dynamic as possible, but balancing that with all the extra code necessary to make every piece not rely on hard-coded information.  So you need to provide mail config information, the NetBIOS domain names you want to loop through, and the dn of the groups for each user domain.  The GC to search and the DC to make the change to will be determined automatically.</p>
<p>Download it <a href="http://www.flobee.net/download/EASDisable.zip">here</a> or copy below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit

Dim mailFrom, mailTo, mailSubject, mailBody, mailServer
Dim objConnection, objCommand, objRecordSet, objUser, objGC, objDomain
Dim strGCPath, strNBDomain, strdomain, arrDomains, strCount
dim strSearchFilter, strEASDL, strDomainPath
strCount = ""

'Mail config
mailFrom = Wscript.ScriptName &#038; "@company.com"
mailTo = "user@company.com"
mailSubject = "EAS Disable Results"
mailBody = ""
mailServer = "server.company.com"

'AD connection parameters
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection

'Get FQDN of a GC
Set objGC = GetObject("GC://RootDSE")
strGCPath = objGC.Get("dnsHostName")

'Query each domain and call disable subroutine
arrDomains = array("domainA", "domainB", "domainC", "domainD", "domainE")
For each strDomain in arrDomains
	subOMADisable strdomain
Next

'Email results
Call subSendMail(mailFrom, mailTo, mailSubject, strCount, mailServer)

'Sub to change attribute value
Sub subOMADisable(strNBDomain)
	'Get base DN of domain
	Set objDomain = GetObject("LDAP://" &#038; strNBDomain &#038; "/RootDSE")
	strDomainPath = objDomain.Get("DefaultNamingContext")
	Set objDomain = Nothing

	Select Case strNBDomain
		Case "domainA"
			strEASDL = "dn of groupA"
		Case "domainB"
			strEASDL = "dn of groupB"
		Case "domainC"
			strEASDL = "dn of groupC"
		Case "domainD"
			strEASDL = "dn of groupD"
		Case "domainE"
			strEASDL = "dn of groupE"
	End Select
	strSearchFilter="(&#038;(objectcategory=user)(mailnickname=*)(homeMDB=*)(!memberof=" &#038; strEASDL &#038; ")(!msExchOMAAdminWirelessEnable=5))"
	objCommand.CommandText = "&lt;GC://" &#038; strGCPath &#038; "/" &#038; strDomainPath &#038; "&gt;;" &#038; strSearchFilter &#038; ";distinguishedname;subtree"
	Set objRecordSet = objCommand.Execute
	If objRecordSet.RecordCount &gt; 0 Then
		While Not objRecordSet.EOF
			Set objUser = GetObject("LDAP://" &#038; objRecordSet.Fields("distinguishedname"))
			'wscript.echo objUser.DisplayName
			objUser.Put "msExchOMAAdminWirelessEnable", "5"
			objUser.SetInfo
			objRecordSet.MoveNext
		Wend
	End If
	strCount = strCount &#038; vbTAB &#038; strNBDomain &#038; ": " &#038; objRecordSet.RecordCount &#038; vbCRLF
End Sub

'Sub to send email with results
Sub subSendMail (mFrom, mTo, mSubject, mBody, mServer)
	Dim objEmail
	Set objEmail = CreateObject("CDO.Message")
	objEmail.From = mFrom
	objEmail.To = mTo
	objEmail.Subject = mSubject
	objEmail.Textbody = "Objects modified in each domain:" &#038; vbCRLF &#038;vbCRLF &#038; mBody
	objEmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
	objEmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = mServer
	objEmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
	objEmail.Configuration.Fields.Update
	objEmail.Send
End Sub

'Cleanup
Set objUser = Nothing
Set objCommand = Nothing
Set objGC = Nothing
Set objConnection = Nothing
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/script-to-disable-exchange-activesync-for-unauthorized-users/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Company name change and email addresses</title>
		<link>http://www.flobee.net/company-name-change-and-email-addresses/</link>
		<comments>http://www.flobee.net/company-name-change-and-email-addresses/#comments</comments>
		<pubDate>Mon, 01 May 2006 20:14:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=108</guid>
		<description><![CDATA[My company changed its name a couple weeks ago and so I needed to add a new primary address for those who had the old domain as a primary, and move the old primary to a secondary. I was surprised that I couldn&#8217;t hardly find any existing scripts to accommodate such an endeavor so I [...]]]></description>
			<content:encoded><![CDATA[<p>My company changed its name a couple weeks ago and so I needed to add a new primary address for those who had the old domain as a primary, and move the old primary to a secondary. I was surprised that I couldn&#8217;t hardly find any existing scripts to accommodate such an endeavor so I had to resort to doing it myself.</p>
<p>This script goes through all accounts in a given AD domain and whose primary email address is the &quot;old&quot; SMTP domain name, makes the primary a secondary, takes the username portion of the address and appends the new domain and makes it the new primary. I log all of the old address and new addresses to the screen, so redirect the output to a file to capture that. It doesn&#8217;t check for preexisting addresses so conflicts can occur. I had previously done my own extract to look for those, so dealing with them manually was easier and faster than coding for that.</p>
<p>I modified the script each time I ran it to change the AD domain I wanted to search (though I could have just defined an array of domain names and looped through each of them), and ran it against DLs and public folders, too, adjusting the filter to return the different object types. You will need to do the same.</p>
<p>Download the code <a href="http://www.flobee.net/download/UpdateProxy.zip" alt="Download script">here</a>, or copy below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit
'Set value for domain to run against and LDAP filter to use
'Also set the old and new SMTP domain names on line 33
'If you need to update the display name to remove a company designation, that is line 80
dim strDomain, strSearchFilter
strdomain = "nbdomain" 'Put the NetBIOS domain name here
'Update filter below to return the object types you want, e.g., users, groups, publicfolders
strSearchFilter = "(&#038;(objectcategory=publicfolder)(mailnickname=*)(mail=*@olddomain.com))" 

'Connect to AD
Dim objConnection, objCommand, objRecordSet, objUser
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 10000

'Retrieve user objects
Const ADS_PROPERTY_UPDATE = 2
objCommand.CommandText = "&lt;LDAP://" &#038; strDomain &#038; "&gt;; " &#038; strSearchFilter &#038; "; distinguishedname"
Set objRecordSet = objCommand.Execute
wscript.echo "Count:" &#038; objRecordSet.RecordCount
While not objRecordSet.EOF
	Set objUser = GetObject("LDAP://" &#038; Replace(objRecordSet.Fields("distinguishedname"),"/", "\/"))
	wscript.echo objUser.displayName &#038; "; " &#038; strDomain &#038; "\" &#038; objUser.sAMAccountName

	'Set values for new email addresses
	Dim blnExists, strNewMail, i, strOldProxy, strAddress
	Dim arrProxyAddresses, intProxyAddresses, strNewProxy, intProxyForLoop
	arrProxyAddresses = objUser.proxyAddresses
	intProxyAddresses = UBound(arrProxyAddresses)
	intProxyForLoop = intProxyAddresses	

	'Set value for mail
	strNewMail = Replace(objUser.mail, "@olddomain.com", "@newdomain.com")
	wscript.echo vbTab &#038; "Old mail: " &#038; objUser.mail
	wscript.echo vbTab &#038; "New mail: " &#038; strNewMail
	wscript.echo ""

	'Log old SMTP proxies
	For each strOldProxy in arrProxyAddresses
		If UCase(Left(strOldProxy,5)) = "SMTP:" Then
			wscript.echo vbTab &#038; "Old proxies: " &#038; strOldProxy
		End If
	Next

	'Change primary to secondary and add new primary
	blnExists = False
	For i = 0 to intProxyForLoop
		dim strAddressType, strAddressBody
		strAddressType = Left(arrProxyAddresses(i),5)
		strAddressBody = Mid(arrProxyAddresses(i),6)
		If UCase(strAddressType) = "SMTP:" Then
			If strAddressType = "SMTP:" Then
	    			arrProxyAddresses(i) = Replace(arrProxyAddresses(i), "SMTP:", "smtp:")
	    		Else
	    			If LCase(strAddressBody) = LCase(strNewMail) Then
					blnExists = True
					arrProxyAddresses(i) = Replace(arrProxyAddresses(i), "smtp:", "SMTP:")
				End If
			End If
		End If
		If i = intProxyForLoop Then
			If Not blnExists Then
				ReDim Preserve arrProxyAddresses(intProxyAddresses + 1)
				arrProxyAddresses(intProxyAddresses + 1) = "SMTP:" &#038; strNewMail
			End If
		End If
	Next

	'Log new SMTP proxies
	For each strNewProxy in arrProxyAddresses
		If UCase(Left(strNewProxy,5)) = "SMTP:" Then
			wscript.echo vbTab &#038; "New proxies: " &#038; strNewProxy
		End If
	Next
	wscript.echo ""

	'Remove component designation from display name
	Dim strDisplayName
	strDisplayName = Replace(objUser.displayName, " - COMPANY", "")
	wscript.echo vbTab &#038; "New display: " &#038; strDisplayName

	'Commit changes
	objUser.Put "mail", strNewMail
	objUser.PutEx ADS_PROPERTY_UPDATE, "proxyAddresses", arrProxyAddresses
	objUser.Put "displayName", strDisplayName
	On Error Resume Next
	objUser.SetInfo
	If Err.Number <> 0 Then
		wscript.echo "Update unsuccessful!"
	Else
		wscript.echo "Update successful"
	End If
	wscript.echo vbCRLF
	On Error Goto 0
	objRecordSet.MoveNext
Wend

Set objRecordSet = Nothing
Set objUser = Nothing
Set objCommand = Nothing
Set objConnection = Nothing
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/company-name-change-and-email-addresses/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Query for a mailbox&#8217;s size and quota</title>
		<link>http://www.flobee.net/query-for-a-mailboxs-size-and-quota/</link>
		<comments>http://www.flobee.net/query-for-a-mailboxs-size-and-quota/#comments</comments>
		<pubDate>Wed, 05 Oct 2005 15:43:27 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=24</guid>
		<description><![CDATA[There are a lot of scripts out there to report a mailbox&#8217;s current size and others to report the quota for a mailbox.  And some might even do both, but for an entire domain, server, etc.  I wanted one that I could use to list a single mailbox&#8217;s current size and where it falls within [...]]]></description>
			<content:encoded><![CDATA[<p>There are a lot of scripts out there to report a mailbox&#8217;s current size and others to report the quota for a mailbox.  And some might even do both, but for an entire domain, server, etc.  I wanted one that I could use to list a single mailbox&#8217;s current size and where it falls within its quota.</p>
<p>This script allows you to find a user based on login name (samAccountName) or email address.  If multiple matches are found it will report on all of them.  It uses WMI to query Exchange for the mailbox&#8217;s current size and then uses LDAP to determine the quota.  Since there are multiple places a quota can be set (system policy, server, mailbox), the script factors those in and backtracks to the resulting quota in effect.</p>
<p>The results are output to the screen and to a popup window.  And since it is nice to know, it also will display if default or custom limits are in use.  This script is nice because you don&#8217;t have to customize anything.  Just <a href="http://www.flobee.net/download/MailboxSizePlusQuota.zip">download</a>\copy it and run it.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit
Dim objGC, objOU, strADPath
Dim strUserLoginName
Dim objADOCnxn, objADOCmd, strSearchFilter, strReturnAttrib, strSearchDepth, objResults, intMatchingRecords
Dim strUserDisplayName, strRawExchServer, strExchServer
Dim wmiConn, strWQL, wmiColl, wmiObj
Dim mbstore, strquota, stroverquota, strHardLimit, strmbsize, strquotasum

'
' Messages to be displayed if the scripting host is not cscript
'
Const kMessage1 = "Please run this script using CScript."
Const kMessage2 = "This can be achieved by"
Const kMessage3 = "1. Using ""CScript script.vbs arguments"" or"
Const kMessage4 = "2. Changing the default Windows Scripting Host to CScript"
Const kMessage5 = "   using ""CScript //H:CScript //S"" and running the script "
Const kMessage6 = "   ""script.vbs arguments""."

' Make sure running with CScript
If Not IsHostCscript() Then
	Call WScript.echo(kMessage1 &amp; vbCRLF &amp; kMessage2 &amp; vbCRLF &amp; _
         kMessage3 &amp; vbCRLF &amp; kMessage4 &amp; vbCRLF &amp; _
         kMessage5 &amp; vbCRLF &amp; kMessage6 &amp; vbCRLF)
	WScript.quit
End If

' Connect to a global catalog for the forest
Set objGC = GetObject("GC:")
For Each objOU In objGC
	strADPath = "&lt;" &amp; objOU.AdsPath &amp; "&gt;"
Next
Set objOU = Nothing
Set objGC = Nothing

WScript.Echo "* Searching within: " &amp; strADPath

' Get input - strUserLoginName
strUserLoginName = InputBox("This will search for the user's mailbox and display its size." &amp; _
 vbCrLf &amp; vbCrLf &amp; "Enter a user's login name, or" &amp; vbCrLf &amp; "their primary SMTP address:" &amp; _
 vbCrLf &amp; vbCrLf &amp; "(LDAP wildcard characters are supported.)")
If strUserLoginName = "" Then
	WScript.Echo "User Canceled"
	WScript.Quit
End If
' Use ADO to query on the given user login name
Set objADOCnxn = CreateObject("ADODB.Connection")
objADOCnxn.Provider = "ADsDSOObject"
objADOCnxn.Open "Active Directory Provider"
Set objADOCmd = CreateObject("ADODB.Command")
objADOCmd.ActiveConnection = objADOCnxn
strSearchFilter ="(&amp;(objectCategory=person)(|(mail=" &amp; strUserLoginName &amp; ")(samAccountName=" &amp; strUserLoginName &amp; ")))"
strReturnAttrib = "displayName,msExchHomeServerName,samAccountName,mdbUseDefaults,homemdb,mDBStorageQuota,mDBOverQuotaLimit,mDBOverHardQuotaLimit"
strSearchDepth = "SubTree"
objADOCmd.CommandText = strADPath &amp; ";" &amp; strSearchFilter &amp; ";" &amp; strReturnAttrib &amp; ";" &amp; strSearchDepth
Set objResults = objADOCmd.Execute
intMatchingRecords = objResults.RecordCount
WScript.Echo "    AD Search Returned " &amp; intMatchingRecords &amp; " Records" &amp; vbCrLf
If intMatchingRecords &lt; 1 Then
	' User name was not found
	MsgBox "The specified string was not found!" &amp; vbCrLf &amp; vbCrLf &amp; "No matching user name or SMTP address(es)", 0, "Search Results"
Else
	' We found a match, for each record in result set...
	Do
		strUserDisplayName = objResults.Fields("displayName").value
		strRawExchServer = objResults.Fields("msExchHomeServerName").value

		' only proceed if the msExchHomeServerName attribute contains an '=' character
		If InStr(1, strRawExchServer, "=", vbTextCompare) Then
		' Parse out the actual Exchange server name (everything to right of last '=')
		strExchServer = Mid(strRawExchServer,InStrRev(strRawExchServer, "=", -1, vbTextCompare) + 1)

		' Create a WMI connection to that server
		Set wmiConn = GetObject("WinMgmts:{impersonationLevel=impersonate}!\\" &amp; strExchServer &amp; "\root\microsoftexchangev2")

		' Search for the display name of the user
		WScript.Echo "* Looking for '" &amp; strUserDisplayName &amp; "' on server " &amp; strExchServer
		strWQL = "SELECT * FROM Exchange_Mailbox WHERE MailboxDisplayName = '" &amp; strUserDisplayName &amp; "'"
		'WScript.Echo "    DEBUG " &amp; strWQL
		WScript.Echo "  Searching... Please wait"

		Set wmiColl = wmiConn.ExecQuery(strWQL)
		If wmiColl.Count &gt;= 1 Then

			' Get quota limits
			If objResults.Fields("mDBUseDefaults").value = true Then
				Set mbstore = GetObject("GC://" &amp; objResults.Fields("homemdb"))
				If mbstore.mDBStorageQuota = "" Then
					strquota =  "No Quota"
				Else
					strquota = formatnumber(mbstore.mDBStorageQuota/1024,0)
				End if
				If mbstore.mDBOverQuotaLimit = "" Then
					stroverquota =  "No Quota"
				Else
					stroverquota = formatnumber(mbstore.mDBOverQuotaLimit/1024,0)
				End if
				If mbstore.mDBOverHardQuotaLimit = "" Then
					strHardLimit =  "No Quota"
				Else
					strHardLimit = formatnumber(mbstore.mDBOverHardQuotaLimit/1024,0)
				End if
				If strquota &lt;&gt; "No Quota" Then
					strquotasum = "    Storage Quotas (Using store limits):" &amp; vbcrlf
					strquotasum = strquotasum &amp; "    Warning Limit: " &amp; strquota &amp; " MB" &amp; vbcrlf
					strquotasum = strquotasum &amp; "    Prohibit Send: " &amp; stroverquota &amp; " MB" &amp; vbcrlf
					strquotasum = strquotasum &amp; "    Prohibit Receive: " &amp; strHardLimit &amp; " MB" &amp; vbcrlf
				Else
					strquotasum = "Storage Limits: No Quotas Configured" &amp; vbcrlf
				End if
			Else
				If IsNull(objResults.fields("mDBStorageQuota").value) Then
					strquota =  "No Quota"
				Else
					strquota = formatnumber(objResults.fields("mDBStorageQuota").value/1024,0) &amp; " MB"
				End if
				If IsNull(objResults.fields("mDBOverQuotaLimit").value) Then
					stroverquota =  "No Quota"
				Else
					stroverquota = formatnumber(objResults.fields("mDBOverQuotaLimit").value/1024,0) &amp; " MB"
				End if
				If IsNull(objResults.fields("mDBOverHardQuotaLimit").value) Then
					strHardLimit =  "No Quota"
				Else
					strHardLimit = formatnumber(objResults.fields("mDBOverHardQuotaLimit").value/1024,0) &amp; " MB"
				End if
				strquotasum = "    Storage Quotas (Using custom limits):" &amp; vbcrlf
				strquotasum = strquotasum &amp; "    Warning Limit: " &amp; strquota &amp; vbcrlf
				strquotasum = strquotasum &amp; "    Prohibit Send: " &amp; stroverquota &amp; vbcrlf
				strquotasum = strquotasum &amp; "    Prohibit Receive: " &amp; strHardLimit &amp; vbcrlf

			End if

			' for each mailbox found (should only be one), display the size
			For Each wmiObj In wmiColl
				WScript.Echo "    Found: " &amp; wmiObj.MailboxDisplayName
				WScript.Echo "    Mailbox Size: " &amp; formatnumber(wmiObj.Size/1024,1) &amp; " MB" &amp; vbCrLf
				Wscript.Echo strquotasum
				MsgBox "    Mailbox: " &amp; strUserDisplayName &amp; vbcrlf &amp; "    Size: " &amp; formatnumber(wmiObj.Size/1024,1) &amp; _
				 " MB" &amp; vbcrlf &amp; vbcrlf &amp; strquotasum, 0, "Search Results"
			Next
		Else
			' No mailbox found
			MsgBox "'" &amp; strUserDisplayName &amp; "' mailbox was not found on server " &amp; strExchServer, 0, "Search Results"
		End If

		Set wmiColl = Nothing
		Set wmiConn = Nothing
	Else
		WScript.Echo "* No Exchange Home Server defined for " &amp; objResults.Fields("samAccountName").value &amp; vbCrLf
	End If

	'move to the next record in the record set; quit when EOF is true
	objResults.MoveNext
	Loop until objResults.EOF

End If
Set objResults = Nothing
Set objADOCmd = Nothing
Set objADOCnxn = Nothing

WScript.Echo "Done!"
WScript.Quit

' Determines which program is used to run this script.
' Returns true if the script host is cscript.exe
Function IsHostCscript()
	On Error Resume Next
	Dim strFullName
	Dim strCommand
	Dim i, j
	Dim bReturn
	bReturn = False
	strFullName = WScript.FullName
	i = InStr(1, strFullName, ".exe", 1)
	If i &lt;&gt; 0 Then
		j = InStrRev(strFullName, "\", i, 1)
		If j &lt;&gt; 0 Then
			strCommand = Mid(strFullName, j+1, i-j-1)
			If LCase(strCommand) = "cscript" Then
				bReturn = True
			End If
		End If
	End If
	If Err &lt;&gt; 0 Then
		Call WScript.echo("Error 0x" &amp; Hex(Err.Number) &amp; " occurred. " &amp; Err.Description _
			&amp; ". " &amp; vbCRLF &amp; "The scripting host could not be determined.")
	End If
	IsHostCscript = bReturn
End Function</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/query-for-a-mailboxs-size-and-quota/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Report the last time your Exchange servers were backed up</title>
		<link>http://www.flobee.net/report-the-last-time-your-exchange-servers-were-backed-up/</link>
		<comments>http://www.flobee.net/report-the-last-time-your-exchange-servers-were-backed-up/#comments</comments>
		<pubDate>Thu, 22 Sep 2005 19:14:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=16</guid>
		<description><![CDATA[Like a lot of my scripts, they start from the hard work that someone else has done.  This one began as a similar script that Glen Scales wrote and posted over on his blog.  His version enumerates the servers/stores in a domain and outputs the results to the screen. My needs required some tweaking since [...]]]></description>
			<content:encoded><![CDATA[<p>Like a lot of my scripts, they start from the hard work that someone else has done.  This one began as a similar script that Glen Scales wrote and posted over on his <a href="http://gsexdev.blogspot.com/2004/12/finding-when-exchange-store-last.html" target="_blank">blog</a>.  His version enumerates the servers/stores in a domain and outputs the results to the screen.</p>
<p>My needs required some tweaking since I have servers in multiple domains, and I wanted it to email the results to multiple people who are responsible for backups.  I also needed to account for servers that don&#8221;t have public and/or private stores (e.g., front-end servers, conferencing servers).  The script will email the report in HTML format, grouping the stores alphabetically by server name.  Change the constants at the top to suit your needs and then schedule it to run daily.  My next step is to note stores that haven&#8221;t been backed up in X days (perhaps two or three) and highlight them in red so it is easy to spot those stores (since my report currently has 60+ stores in it).  Until then, download this version <a href="http://www.flobee.net/download/lastbackup.zip">here</a>, or copy it below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
'Change the constants below
CONST strSMTPServer = "Change to server name"
CONST strSMTPRecipient = "Change to recipient address(es)"
CONST strSMTPSender = "Change to sender address"
set conn = createobject("ADODB.Connection")
set mdbobj = createobject("CDOEXM.MailboxStoreDB")
set pdbobj = createobject("CDOEXM.PublicStoreDB")
set com = createobject("ADODB.Command")
Set iAdRootDSE = GetObject("&lt;a href="ldap://RootDSE/"&gt;LDAP://RootDSE&lt;/a&gt;")
strNameingContext = iAdRootDSE.Get("configurationNamingContext")
Conn.Provider = "ADsDSOObject"
Conn.Open "ADs Provider"
serverQuery = "&lt;GC://" &amp; strNameingContext &amp; "&gt;;(&amp;(objectCategory=msExchExchangeServer));name,distinguishedName;subtree"
Com.ActiveConnection = Conn
Com.Properties("Sort on") = "name"
Com.CommandText = serverQuery
Set Rs = Com.Execute
While Not Rs.EOF
	output = output &amp; "&lt;font size=2&gt;&lt;u&gt;&lt;b&gt;" &amp; Rs.Fields("name") &amp; "&lt;/b&gt;&lt;/u&gt;&lt;/font&gt;" &amp; vbcrlf
mbQuery = "&lt;LDAP://" &amp; strNameingContext &amp; "&gt;;(&amp;(objectCategory=msExchPrivateMDB)(legacyExchangeDN=*" &amp; _
	Rs.Fields("name") &amp; "/cn=Microsoft Private MDB));name,distinguishedName;subtree"
	pfQuery = "&lt;LDAP://" &amp; strNameingContext &amp; "&gt;;(&amp;(objectCategory=msExchPublicMDB)(legacyExchangeDN=*" &amp; _
	Rs.Fields("name") &amp; "/cn=Microsoft Public MDB));name,distinguishedName;subtree"
	Com.CommandText = mbQuery
	Set Rs1 = Com.Execute
	If Rs1.RecordCount = 0 Then
		output = output &amp; "&lt;table&gt;&lt;tr&gt;&lt;td width=50%&gt;&lt;font size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;No mailbox stores." &amp; _
		"&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;" &amp; vbcrlf
	Else
		output = output &amp; "&lt;table&gt;"
		While Not Rs1.EOF
			mdbobj.datasource.open "LDAP://" &amp; Rs1.Fields("distinguishedName")
			output = output &amp; "&lt;tr&gt;&lt;td width=50%&gt;&lt;font size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;" &amp; Rs1.Fields("name") &amp; _
			" &lt;/font&gt;&lt;/td&gt;&lt;td&gt;&lt;font size=2&gt;Last Backed Up :" &amp; mdbobj.LastFullBackupTime &amp; "&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;" &amp; vbcrlf
			Rs1.MoveNext
		Wend
	End If
	Rs1.Close
	Com.CommandText = pfQuery
	Set Rs2 = Com.Execute
	If Rs2.RecordCount = 0 Then
		output = output &amp; "&lt;tr&gt;&lt;td width=50%&gt;&lt;font size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;No public folder store.&lt;/td&gt;&lt;/tr&gt;" &amp; vbcrlf
	Else
		pdbobj.datasource.open "LDAP://" &amp; Rs2.Fields("distinguishedName")
		output = output &amp; "&lt;tr&gt;&lt;td width=50%&gt;&lt;font size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;" &amp; _
		Rs2.Fields("name") &amp; " &lt;/font&gt;&lt;/td&gt;&lt;td&gt;&lt;font size=2&gt;Last Backed Up :" &amp; _
		pdbobj.LastFullBackupTime &amp; "&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;" &amp; vbcrlf
	End If
	output = output &amp; "&lt;/table&gt;"
	Rs2.Close
	On Error Goto 0
	output = output &amp; vbcrlf
	Rs.MoveNext
Wend
Set iMsg = CreateObject("CDO.Message")
With iMsg
	.To = strSMTPRecipient
	.From = strSMTPSender
	.Subject = "Last Exchange Backup Report"
	.HTMLBody =  output
	.Configuration.Fields.Item("&lt;a href="http://schemas.microsoft.com/cdo/configuration/sendusing"&gt;http://schemas.microsoft.com/cdo/configuration/sendusing&lt;/a&gt;") = 2
	.Configuration.Fields.Item("&lt;a href="http://schemas.microsoft.com/cdo/configuration/smtpserver"&gt;http://schemas.microsoft.com/cdo/configuration/smtpserver&lt;/a&gt;") = strSMTPServer
	.Configuration.Fields.Item("&lt;a href="http://schemas.microsoft.com/cdo/configuration/smtpserverport"&gt;http://schemas.microsoft.com/cdo/configuration/smtpserverport&lt;/a&gt;") = 25
	.Configuration.Fields.Update
	.Send
End With
Rs.Close
Conn.Close
set mdbobj = Nothing
set pdbobj = Nothing
Set Rs = Nothing
Set Rs1 = Nothing
Set Rs2 = Nothing
Set Com = Nothing
Set Conn = Nothing
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/report-the-last-time-your-exchange-servers-were-backed-up/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Send email notification for password expiration to &#8220;remote&#8221; users</title>
		<link>http://www.flobee.net/send-email-notification-for-password-expiration-to-remote-users/</link>
		<comments>http://www.flobee.net/send-email-notification-for-password-expiration-to-remote-users/#comments</comments>
		<pubDate>Mon, 19 Sep 2005 16:18:47 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=9</guid>
		<description><![CDATA[Over on Michael Smith&#8217;s blog, he has a script to notify users when their password is about to expire.  This is handy for Exchange users who never log in to the network because they are offsite or on an extranet (i.e., POP3/IMAP4/RPC-HTTP users).  It will crawl an OU and check the days until expiration and, [...]]]></description>
			<content:encoded><![CDATA[<p>Over on Michael Smith&#8217;s <a href="http://blogs.brnets.com/michael" target="_blank">blog</a>, he has a <a href="http://blogs.brnets.com/michael/archive/2005/09/13/1474.aspx" target="_blank">script</a> to notify users when their password is about to expire.  This is handy for Exchange users who never log in to the network because they are offsite or on an extranet (i.e., POP3/IMAP4/RPC-HTTP users).  It will crawl an OU and check the days until expiration and, if less than a variable you set, send an email via CDO.</p>
<p>However, at my office we don&#8217;t have these kinds of users in one OU.  They are spread across multiple domains, but are all in a local group that has permission to a web page that allows them to change their password.  So I updated the script to enumerate the members of this group.  To accommodate different password aging settings in each domain I moved the domain query for this setting into the For loop.  I also added logging so you will have a record of what was sent, not sent, and why.  This way you can schedule it or run it interactively without having to adjust the code.  Download it <a href="http://www.flobee.net/download/PwdExpEmail.zip">here</a>, or copy below.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
Option Explicit

' Per environment constants - you should change these
Const SMTP_SERVER  = "ServerName"
Const STRFROM   = "From SMTP Address"
Const DAYS_FOR_EMAIL  = 14 'Send notification when pwd will expire in this number of days
Const GROUPDN = "DN of group to enumerate"
Const LOGFILE = "Patch and filename of log file"

' System Constants - do not change
Const ONE_HUNDRED_NANOSECOND    = .000000100   ' .000000100 is equal to 10^-7
Const SECONDS_IN_DAY            = 86400
Const ADS_UF_DONT_EXPIRE_PASSWD = &amp;h10000
Const E_ADS_PROPERTY_NOT_FOUND  = &amp;h8000500D

' Change to "True" for extensive debugging output
Const bDebug   = False

Dim numDays, iResult
Dim strDomain
Dim objGroup, objMember, member
Dim objFSO, objFile, strOutput

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(LOGFILE, 8, True)

Set objGroup = GetObject("LDAP://" &amp; GROUPDN)
objFile.WriteLine "Executed at " &amp; Now() &amp; vbCRLF
objFile.WriteLine "Enumerating members of " &amp; objGroup.distinguishedName &amp; ":" &amp; vbCRLF
For each member in objGroup.member
	Set objMember = GetObject("LDAP://" &amp; member)
	objFile.WriteLine objMember.distinguishedname
	strDomain = Mid(objMember.distinguishedname, InStr(objMember.distinguishedname, "DC="))
	numdays = GetMaximumPasswordAge (strDomain)
	Call ProcessUser (numDays)
Next

objFile.WriteLine "Done. Finished at " &amp; Now() &amp; "."
objFile.Close

Function GetMaximumPasswordAge (ByVal strDomainDN)
	Dim objDomain, objMaxPwdAge
	Dim dblMaxPwdNano, dblMaxPwdSecs, dblMaxPwdDays

	Set objDomain = GetObject("LDAP://" &amp; strDomainDN)
	Set objMaxPWdAge = objDomain.maxPwdAge

	If objMaxPwdAge.LowPart = 0 And objMaxPwdAge.Highpart = 0 Then
		' Maximum password age is set to 0 in the domain
		' Therefore, passwords do not expire
		GetMaximumPasswordAge = 0
	Else
		dblMaxPwdNano = Abs (objMaxPwdAge.HighPart * 2^32 + objMaxPwdAge.LowPart)
		dblMaxPwdSecs = dblMaxPwdNano * ONE_HUNDRED_NANOSECOND
		dblMaxPwdDays = Int (dblMaxPwdSecs / SECONDS_IN_DAY)
		GetMaximumPasswordAge = dblMaxPwdDays
	End If
End Function

Function UserIsExpired (objMember, iMaxAge, iDaysForEmail, iRes)
	Dim intUserAccountControl, dtmValue, intTimeInterval
	Dim strName
	On Error Resume Next
	Err.Clear
	strName = Mid (objMember.Name, 4)
	intUserAccountControl = objMember.Get ("userAccountControl")

	If intUserAccountControl And ADS_UF_DONT_EXPIRE_PASSWD Then
		dp "The password for " &amp; strName &amp; " does not expire."
		UserIsExpired = False
	Else
		iRes = 0
		dtmValue = objMember.PasswordLastChanged
		If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then
			UserIsExpired = True
			dp "The password for " &amp; strName &amp; " has never been set."
		Else
			intTimeInterval = Int (Now - dtmValue)
			dp "The password for " &amp; strName &amp; " was last set on " &amp; _
			DateValue(dtmValue) &amp; " at " &amp; TimeValue(dtmValue) &amp; _
			" (" &amp; intTimeInterval &amp; " days ago)"
			If intTimeInterval &gt;= iMaxAge Then
				dp "The password for " &amp; strName &amp; " has expired."
				UserIsExpired = True
			Else
				iRes = Int ((dtmValue + iMaxAge) - Now)
				dp "The password for " &amp; strName &amp; " will expire on " &amp; _
				DateValue(dtmValue + iMaxAge) &amp; " (" &amp; _
				iRes &amp; " days from today)."
				If iRes &lt;= iDaysForEmail Then
					dp strName &amp; " needs an email for password change"
					UserIsExpired = True
				Else
					dp strName &amp; " does not need an email for password change"
					'Swap commented variable below to force email to be sent (for testing).
					UserIsExpired = False
					'UserIsExpired = True
				End If
			End If
		End If
	End If
End Function

Sub ProcessUser (iMaxPwdAge)
	Dim iResult, strExpire
	If Right (objMember.Name, 1) &lt;&gt; "$" Then
		If IsEmpty (objMember.Mail) or IsNull  (objMember.Mail) Then
			dp Mid (objMember.Name, 4) &amp; " has no mailbox"
		Else
			If UserIsExpired (objMember, iMaxPwdAge, DAYS_FOR_EMAIL, iResult) Then
				objFile.WriteLine "Sending an email to " &amp; objMember.givenName &amp; " " &amp; objMember.sn &amp; _
				" (" &amp; objMember.Mail &amp; ").  Password expires in " &amp; iResult &amp; " days." &amp; vbCRLF
				Call SendEmail (iResult)
			Else
				If iResult = "" Then
					strExpire = "."
				Else
					strExpire = " for " &amp; iResult &amp; " days."
				End If
				objFile.WriteLine "Skipping " &amp; objMember.givenName &amp; " " &amp; objMember.sn &amp; _
				". Password does not expire" &amp; strExpire &amp; vbCRLF
			End If
		End If
	End If
End Sub

Sub SendEmail (iResult)
	Dim objMail
	Set objMail = CreateObject ("CDO.Message")
	objMail.Configuration.Fields.Item ("<a href="http://schemas.microsoft.com/cdo/configuration/sendusing">http://schemas.microsoft.com/cdo/configuration/sendusing</a>")      = 2
	objMail.Configuration.Fields.Item ("<a href="http://schemas.microsoft.com/cdo/configuration/smtpserver">http://schemas.microsoft.com/cdo/configuration/smtpserver</a>")     = SMTP_SERVER
	objMail.Configuration.Fields.Item ("<a href="http://schemas.microsoft.com/cdo/configuration/smtpserverport">http://schemas.microsoft.com/cdo/configuration/smtpserverport</a>") = 25
	objMail.Configuration.Fields.Update

	objMail.From = STRFROM
	objMail.To = objMember.Mail

	objMail.Subject = "The Windows password is going to expire for " &amp; Mid (objMember.Name, 4)
	objMail.Textbody = "The Windows Active Directory password for user " &amp; objMember.givenName &amp; " " &amp; objMember.sn &amp; _
	" (" &amp; objMember.sAMAccountName &amp; ")" &amp; " will expire in " &amp; iResult &amp; " days. " &amp; vbCRLF &amp; vbCRLF &amp; _
	"Please use Outlook Web Access (<a href="https://www.company.com">https://www.company.com</a>) to change it before it expires." &amp; vbCRLF &amp; vbCRLF &amp; _
	"Thank you," &amp; vbCRLF &amp; _
	"Company Exchange Team" &amp; vbCRLF &amp; vbCRLF &amp; _
	"Note: You have received this email because you are a member of a group that is authorized to change your " &amp; _
	"password via Outlook Web Access since you do not log in to the corporate network.  If this no longer applies to you, " &amp; _
	"please notify the Company Email Team (SMTPAddress) so you can be removed from the group."

	objMail.Send
	Set objMail = Nothing
End Sub

Sub dp (str)
	If bDebug Then
		objFile.WriteLine str
	End If
End Sub
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/send-email-notification-for-password-expiration-to-remote-users/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Find (and delete) orphaned delegate rules</title>
		<link>http://www.flobee.net/find-and-delete-orphaned-delegate-rules/</link>
		<comments>http://www.flobee.net/find-and-delete-orphaned-delegate-rules/#comments</comments>
		<pubDate>Thu, 15 Sep 2005 15:20:20 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Exchange 2003]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[inline code]]></category>

		<guid isPermaLink="false">http://www.flobee.net/?p=6</guid>
		<description><![CDATA[When a user adds someone as a delegate of his/her mailbox and selects the option to have meeting requests go to the delegate, a hidden rule is created in the Inbox of the delegator. When you remove the delegate from the list the rule is deleted. But if the delegate isn&#8217;t removed before the delegee&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>When a user adds someone as a delegate of his/her mailbox and selects the option to have meeting requests go to the delegate, a hidden rule is created in the Inbox of the delegator. When you remove the delegate from the list the rule is deleted. But if the delegate isn&#8217;t removed before the delegee&#8217;s mailbox is deleted (presumably due to termination), the rule isn&#8217;t deleted. The delegate is removed from the Delegates tab so you can&#8217;t remove the rule, effectively orphaning the rule. Because the rule still exists, meeting requests will still be sent to the delegate. Since the mailbox doesn&#8217;t exist anymore, the meeting requestor will receive an NDR from a person they didn&#8217;t even invite to the meeting.</p>
<p>Microsoft&#8217;s <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;253557" target="_blank">solution</a> is to use MDBView and go through the hidden items in the inbox until you find the corresponding rule and manually delete it. You can also use the better tool, <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;291794">MFCMapi</a>, to do it. I wanted a better way to do it, and one that admins who don&#8217;t delve into raw MAPI viewers could use. So I wrote this WebDAV script that queries the target mailbox for the hidden items in the inbox, displays them in a table, and allows you to delete the message from the results window.</p>
<p>The delegate rule&#8217;s Rule Provider Content value is &#8220;Schedule+ EMS Interface.&#8221; If the delegator has multiple delegates who receive meeting requests (not that common) you won&#8217;t see multiple rules. There is only one rule, regardless of the number of recipients. If you don&#8217;t see an item with the Rule Provider Content Value above, then the user doesn&#8217;t have any delegates who receive meeting requests.</p>
<p>The script uses Exchange 2003 SP1&#8242;s ability to use the SMTP address in the URL, negating the need for a virtual directory for each SMTP namespace in your org. So you need SP1 for this to function. Just change the values near the top for the protocol and server name of your front-end or back-end server. I wrapped the script in a web page so it can be hosted on an IIS server and then you can just browse to it from anywhere. Note that it is not a server-side script. The code will be executed by the client and will prompt for credentials. Putting it in a web page is just a convenience. Just copy and paste it into a file, or download it <a href="http://www.flobee.net/download/delegaterule.zip">here</a>.</p>
<pre class="brush:vb;gutter:false;wrap-lines:false;auto-links:false;collapse:true">
&lt;html&gt;
&lt;head&gt;
&lt;script language='VBScript'&gt;
Dim objXMLHTTP, objXMLDoc
' Define your protocol; http or https
strProtocol = "http"
' Define your server name
strServername = "[server]"
' Define your local name for 'Inbox'
strInbox = "Inbox"
Sub getMessages_OnClick()
	strUsername = document.all.mailbox.value
	If strUsername &lt;&gt; "" Then
		strInboxURL = strProtocol &amp; "://" &amp; strServername &amp; "/Exchange/"
		strInboxURL = strInboxURL &amp; strUsername &amp; "/" &amp; strInbox
		Set objXMLHTTP = CreateObject("Microsoft.XMLHTTP")
		objXMLHTTP.Open "SEARCH", strInboxURL, True
		objXMLHTTP.setRequestHeader "Content-type:", "text/xml"
		objXMLHTTP.setRequestHeader "Depth", "1"
		objXMLHTTP.onReadyStateChange = getRef("checkXMLHTTPState")
		strXML = "&lt;?xml version='1.0' ?&gt;" &amp; _
			"&lt;a:searchrequest xmlns:a='DAV:'&gt;&lt;a:sql&gt;" &amp; _
			"SELECT" &amp; _
			" ""DAV:displayname""" &amp; _
			",""&lt;a href="http://schemas.microsoft.com/mapi/proptag/x65eb001f"&gt;http://schemas.microsoft.com/mapi/proptag/x65eb001f&lt;/a&gt;""" &amp; _
			" FROM scope('shallow traversal of """ &amp; strInboxURL &amp; """')" &amp; _
			" WHERE ""DAV:ishidden""=True" &amp; _
			" AND ""DAV:isfolder""=False" &amp; _
			"&lt;/a:sql&gt;&lt;/a:searchrequest&gt;"
		objXMLHTTP.Send(strXML)
	End If
End Sub

Sub checkXMLHTTPState
	If objXMLHTTP.readyState = 4 Then
		ResponseStatus.innerHTML = "Server Response: " &amp; objXMLHTTP.Status &amp; " - " &amp; objXMLHTTP.StatusText
		Set objXMLDoc = objXMLHTTP.ResponseXML
		XSLDiv.innerHTML = objXMLDoc.TransformNode(responseXSL.documentElement)
		Set objXMLHTTP = Nothing
		Set objXMLDoc = Nothing
	End If
End Sub

Function deleteMessage(strURL)
	strUsername = document.all.mailbox.value
	strInboxURL = strProtocol &amp; "://" &amp; strServername &amp; "/Exchange/"
	strInboxURL = strInboxURL &amp; strUsername &amp; "/" &amp; strInbox
	strItemURL = strInboxURL &amp; "/" &amp; Replace(strURL, chr(34), "")
	Set objDelete = CreateObject("Microsoft.XMLHTTP")
	objDelete.Open "DELETE", strItemURL, False
	objDelete.Send
	If (objDelete.Status &gt;=200 And objDelete.Status &lt;300) Then
		MsgBox "Successful deletion."
	Else
		MsgBox "Delete request failed."
	End If
	Set objDelete = Nothing
	Call getMessages_OnClick()
End Function
&lt;/script&gt;

&lt;xml id='responseXSL'&gt;
&lt;xsl:template
xmlns:xsl='uri:xsl' xmlns:a='DAV:' xmlns:d='urn:schemas:httpmail:'
xmlns:mapitag='http://schemas.microsoft.com/mapi/proptag/'&gt;
&lt;table width='75%' border='1'&gt;
&lt;tr&gt;
&lt;td align='center'&gt;&lt;font size='2'&gt;&lt;b&gt;Action&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td width='40%' align='center'&gt;&lt;font size='2'&gt;&lt;b&gt;Message Subject&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td align='center'&gt;&lt;font size='2'&gt;&lt;b&gt;Rule Provider Content&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;xsl:for-each select='a:multistatus/a:response'&gt;
&lt;tr&gt;
&lt;td align='center'&gt;
&lt;input&gt;
&lt;xsl:attribute name='type'&gt;button&lt;/xsl:attribute&gt;
&lt;xsl:attribute name='name'&gt;deleteMessage1&lt;/xsl:attribute&gt;
&lt;xsl:attribute name='value'&gt;Delete&lt;/xsl:attribute&gt;
&lt;xsl:attribute
name='onclick'&gt;vbscript:deleteMessage("&lt;xsl:value-of
select='string(a:propstat/a:prop/a:displayname)' /&gt;")
&lt;/xsl:attribute&gt;
&lt;/input&gt;
&lt;/td&gt;
&lt;td&gt;&lt;font size='2'&gt;&lt;xsl:value-of select='a:propstat/a:prop/a:displayname' /&gt;&lt;/font&gt;&lt;/td&gt;
&lt;td&gt;&lt;font size='2'&gt;&lt;xsl:value-of select='a:propstat/a:prop/mapitag:x65eb001f' /&gt;&lt;/font&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/xsl:for-each&gt;
&lt;/table&gt;
&lt;/xsl:template&gt;
&lt;/xml&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;font face='Verdana' size='2'&gt;SMTP Address:&lt;br&gt;
&lt;input type='text' name='mailbox'&gt;&lt;br&gt;
&lt;input type='button' name='getMessages' value='Retrieve'&gt;&lt;br&gt;&lt;br&gt;
&lt;div id='responseStatus'&gt;&lt;/div&gt;
&lt;div id='XSLDiv'&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.flobee.net/find-and-delete-orphaned-delegate-rules/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

