Archives for the Month of March, 2007

How do you get rid of the Judge?

Wow. I was looking through some old backups today, and found a writing assignment where we actually had to turn in the drek that came out when working to clear "The Judge."

The assignment was to do 8 of these 20-minute sessions in one week. By the end, I was closing my eyes and making an insane amount of typos.

Time 1:40pm, September 17.  Word count 1405
Time 1:26am, September 19.  Word count 1409
Time 11:46pm, September 19.  Word count 1694
Time 4:41pm, September 20.  Word count 1730
Time 6:59pm, September 21.  Word count 1793
Time 11:15pm, September 22.  Word count 1856
Time 11:08pm, September 23.  Word count 1942
Time 9:30apm, September 24.  Word count 1879
Time 11:35a, September 30.  Word count 1715

And to be clear that we're not talking about poetry here:

Time 11:35a, September 30.  Word count 1715

 

Well it=s time again..  I=ll stop typing at 11:55 If the cassiopeia ahasn=t gone off..  This is interesting talking for awhile... It=s not that exciting, but htat=s OK.  What i will be interested in ins is ws seeing how fo t it makes my craetivity better hen it comes to going for a boat fir de ride and seeing how aI am doing?  I think I will be doing OK, but I haven=t typed in awhile , so I don=t know what I am feeling like.  I know I am feeling pretty well, so that=s OK and I don=t have to worry about it.  I=m kinda sad that I ddin=t eever h it the 2000 word b mark, but it will come in time.  I just ahve eto remember to ekekeep on tyalking fast and then that will be oK OK.  That=s mint..  That=s really mint, and t I=m mind all about that.  Hello how are you doing?  I am doing fine, but I don=t really know what to talk about..  The funmny think thing is that bp people were talking about how they would say A I don= tknow what to talk about so I=ll just write about it@... I can=t even imagine having so much time alone with my mind that I would think that way?  I think that this is loosening up my braein fluid dthough, so OH my GOD that was the Judge coming in again.  I thi though t I had given him the bood bot... I thhink I might as well hae removed my BS cas key that is backspace not bullshit..  Hahahah... I might as well have removed it, buec becaseu becasu because I ahd eliminated the problem of wanting to backspace at all... Hello that= s really strange.  I wish bubba would stop playing witht the TB TV because it=s really annoying..  And then we just let e her and she walkas walks around think in th thinking about it... That=s poretty mn nuts but if wh she want=s to she can do it.  Hello I think that=s carzy, but I don=t know crazy all I know is f nutty funiness..  Fuziness i s is not funiness, that is not funny.. .H ello how are yo udougn??  I think I we will describe the funniness of the thinkint and the hello and the hello and how are you i you?  I don= thtink th you are all that well, ebcause I have 15 minutes left and I am giong as as slow as one could imagine... I think that was pretty nutes ant that I was 20 go asdf I don=t want to think about that.  What I really want to think about is the deep inner space fof the human psyche and all of that jazz..  I think that I have to return the SparQ a lot sooner than I really want to... I really want to, so hello I think I will ..  Hello that=s really nuts.  I have to blow my nose too, but ydo you hear me complaining about it?  I think that she=s cout caught her... I think she=s been tricked, but I don=t think r she really minsd.. 
I think the neat thing about ebaing a man of letters and a man of email is that I keep up my typing shk skills, and I keep up my literary skills.  I don
= twrite t letters s themselves very often , but that=s OK..  I don=t think I reallly want to... Hello that=s pretty nuts.  Hello I don=t know what I to do..  I think that alltheweb.com indexes 20 million to Lycos=s on ly only has 7..  I don=t kknow what to think about..  I think I if I was the man.  i of all men, I would index onlly the title... I t don=t know what kind of crap I would get in searches, though, because when you have title s titles that purposely do that, and mess up tht tite title hthatn I would bre breally annoyed that I would come up with thi... Oanother option would be to j I dont= know I... I think you would have to have a nutty nutinees,,,..  I think that the web should be ... D See thent the think thing is, that msost people don=t have an idea what their page is about, so the title isn=t ev ery descriptive... I think that is nuts... I On the ohter hadn, you could just so go searhc searching for informations and keep a adatabase of nuts and peanuts especially..  I don=t htink I would mind that at all..  I think I would like a peanut butter and jam sandwich today..  I think I would also like some of the nutella..  Hello how are you doing?  I tink think I am doing fine!  I think that fr Frankie are lying.  Hello howa rey oug?  I think that I am doing fine, but I don=t htink I am anhywhere even near 1500 words... I think If i am w to do this, it will be more like 14000 1400 1300 words or so..  I odjust don=t have tht the speed that I did nbe before, and I j certainly don= thta have the speed of my fingers..  I don=t even know how fast I could bring it up if c I continued to type at all the speed int the o workl world for the next 10 minmutes..  I f I continued to type at 100 wpm for then enxt while, as compare t compared to the 0 l words I was typing before, then w that would take me to 85 words per mintes..  However that i swhat I am ocom always thyping at, so I think that I am only able to makx max out I think that I fi type really cast and don=t care about anything else then I will end up at about some hshee... I think that I will end up ab at about 15600 ow words, seeing as I am not raelly going all that fast and that I am not raelliy going as fast ags sas gI can..  I think that >s mint..  That candle smells really goodd... I think that I love vanilla..  I am actually in lvoe with the flavour aof fa vanilla..  I don=t knowo what in the heck is going on..  I have to work with cr cChris and Somebboyd eles to day..  That=s rpetty OK..  A Aand I don= tmind it at all..  I am fine about coming early , alnthough I don= th tink I will have to..  I think I shall just go to sleep now and let myu fingers to d do t he walking..  Walk walk walk hahaha..  That=s rn not really that funny..  I think it shall realkly go to sleep..  I don=t know how I will be able to write and that I don= tknow what to say!  I tihn Think that I was want to go for about a page at a font 1 of about 13 14 15 so that I will probably havbe to write around 5 600 words... I am not paln planning an anthology a or and epoch or anything like that..  I just hope to write and write..  as fasrt as I can ..  That makes me happy..  I think that I am happy that I am happy that I am watching TV and typing at the same it time..  I th ink that w I will be doing about 1500 words, in retrospect..  That makes me tired... actually I would really not br be very typed typed..  O What is a typed language?  Hello how are you doing?  Hello I don=t know wwhat to do?  I think that I am tired and would reatud gradually be following int obeing nice?  Hello how are you dongt?  I think that I wam am adoing fine?/ I think that they are fighting.  Hello how are you ding?  I don=t know what to do?  I am very angry angry with th her that is really carea crazy ?  Hello how are you dong?  I am nuts..  I think that she is fighting and that is no fun?  I think that I saw another another argument..  Everybody fights won once ain awhile..  I think that the interesting thing is that I can just type and type.;..  Without even considering what is giog around on the computer??  I( think that I thingk it is interesting that I can watch tv and talk at the same times..  I think that my fingers have managed to go on cone consider how my fingers..  I thin that that the funny thing aobut the funinees..  Hello I don= tknow what I=m typing abut aat least a I am wathcving TV..  IT=s important that I am watching TV..  I think I will be fininished at about 11:55, but I don= tknow why the Cassiopeia hasn=t gone of..  I think the problem i sthat I se it about aon only a mintue or so beco before I startes d started looking at the TV tco cmoptuer..  So I se t ts et the computer, and then thing king thtat the Casiopeia was talkinga longerthan it wo was, I didn=t really care..  That=s OK ..  I can=t believe that i >m aon on 15 pages..  That=s re pretyt crazy..  My finger types fast aenought..  I love her too,.  I think that it must be a alittl ech.. . OK I have just a seocnd sleft..  That=s wahat I though..  It didn=t take me long enought to go from the cassiopeia to the comptuer.  I was expecting a lot less time.. .OH OK anyways I w think that I will be wrapping it up, buecause I am almost done..  Almost sI tell you..  I o wonder if that should be a hint..  I have it save on me, but it is only every 20 minutes... That actually is it sa..  BVTE
 

 

Advanced HTTP / ASP.Net Scripting with PowerShell

Back in the good ol’ days, my domain used to support a catch-all email address. I could give out any email address (as long as it ended in @leeholmes.com,) and still be guaranteed to receive the mail. When asked to create an account at some random website, I just make one up on the spot tailored specifically to them.

I got an odd look from my dentist when I did this, though. They must have thought — “who in their right mind likes dentists so much that they pick it for their email address?” As with all computer jokes, explaining it didn’t make it any better – I just got a smile and nod.

In any case, my domain soon fell under the wrath of a dictionary attack spammer – which I noticed when I was getting spam as fast as Outlook could receive it. I quickly disabled my catch-all alias, but I immediately missed how easy it was to create a new temporary alias. Creating the aliases manually got old quickly, so I wrote a script to do it instead. It works against “SmarterMail Professional Edition” – the software used at WebHost4Life (the hosting company I use.)

If you use WebHost4Life, feel free to use this script to let you create email aliases with ease. If you don’t, you might still find the code useful for building your own script to automate HTTP / ASP.Net scenarios.

PS >New-MailAlias foobar2

cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential
User: domain-admin-address
@leeholmes.com
Password for user domain-admin-address@leeholmes.com: ***********

Logging in.
Logged in.
Creating alias.
Alias created.

(Note: This script uses Send-TcpRequest, which I posted earlier as “Connect-Computer”: http://www.leeholmes.com/blog/ScriptingNetworkTCPConnectionsInPowerShell.aspx)

##############################################################################
##
## New-MailAlias.ps1
##
## Create a new mail alias on a mail host managed by a "SmarterMail 
## Professional Edition" web interface
##
## The alias provided will redirect mail to the user specified by $recipient
## The credential should be the full email address and password of the domain
## email administrator -- usually [email protected]$mailHost.
##
##############################################################################

param(
    [string] $alias = $("Specify an alias"), 
    [System.Management.Automation.PsCredential] $cred = $(Get-Credential))

[void] [Reflection.Assembly]::LoadWithPartialName("System.Web")

$mailHost = "mail.example.com"
$recipient = "[email protected]"

function Main
{
    Write-Host "Logging in."
    $session = LogIn
    Write-Host "Logged in."

    Write-Host "Creating alias."
    $viewstate = GetAliasPage $session

    SubmitNewAlias $viewstate $session
    Write-Host "Alias created."
}

function LogIn
{
    ## The template for the login request
    $loginRequest = 
@"
GET /Login.aspx HTTP/1.1
Host: $mailHost

"@

    ## Request the login page from the server
    $loginPage = Send-TcpRequest $mailHost 80 -Input $loginRequest

    ## Extract the ASP.Net session ID and viewstate for the page
    $session = GetSessionId $loginPage
    Write-Verbose "Got session: $session"
    $viewstate = GetViewState $loginPage

    ## The template for the login page submission
    $login = 
@"
POST /Login.aspx HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: __LENGTH
Host: $mailHost
Cookie: SelectedLanguage=; permcookie=1680x1050; ASP.NET_SessionId=__SESSION; screensize=1680x1050; settings=

__POSTDATA

"@

    ## Add the username, password, and viewstate into the information we want to
    ## post
    $postData = "__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=__REPLACEMENTVIEWSTATE&txtUserName=__USERNAME&txtPassword=__PASSWORD&LanguageList=&btnEnterClick.x=0&btnEnterClick.y=0"
    $postData = $postData.Replace("__REPLACEMENTVIEWSTATE", [System.Web.HttpUtility]::UrlEncode($viewstate))
    $postData = $postData.Replace("__USERNAME", [System.Web.HttpUtility]::UrlEncode($cred.GetNetworkCredential().UserName + '@' + $cred.GetNetworkCredential().Domain))
    $postData = $postData.Replace("__PASSWORD", [System.Web.HttpUtility]::UrlEncode($cred.GetNetworkCredential().Password))

    ## Replace the placeholders for the post data in the login template, as well as
    ## the ASP.Net session cookie
    $login = $login.Replace("__LENGTH"$postData.Length)
    $login = $login.Replace("__POSTDATA"$postData)
    $login = $login.Replace("__SESSION"$session)

    ## Submit the login page, and check for errors
    $output = Send-TcpRequest&nbs
p;$mailHost 80 -Input $login
    if($output -match "frmerror.aspx")
    {
        Write-Error "Failed to log in."
        Write-Error "Sent:"
        Write-Error $login
        Write-Error "Output:"
        Write-Error $output

        exit
    }
    ##############################################################################

    $session
}

function GetAliasPage($session)
{
    ## The template for the alias page request
    $aliasRequest = 
@"
GET /DomainAdmin/frmUserAlias.aspx HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Host: $mailHost
Cookie: SelectedLanguage=; permcookie=1680x1050; ASP.NET_SessionId=__SESSION; screensize=1680x1050; settings=empty

"@

    ## Replace the placeholder for the ASP.Net session cookie
    $aliasRequest = $aliasRequest.Replace("__SESSION"$session)

    ## Request the alias page from the server
    $aliasPage = Send-TcpRequest $mailHost 80 -Input $aliasRequest

    ## Check for errors
    if($aliasPage -match "frmerror.aspx")
    {
        Write-Error "Failed to get alias page."
        Write-Error "Sent:"
        Write-Error $aliasRequest
        Write-Error "Got:"
        Write-Error $aliasPage

        exit
    }

    ## Extract the viewstate from the page
    GetViewState $aliasPage
}

function SubmitNewAlias($viewstate$session)
{
    ## The template for the alias page submission
    $addAlias = 
@"
POST /DomainAdmin/frmUserAlias.aspx HTTP/1.1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Content-Length: __LENGTH
Host: $mailHost
Cookie: SelectedLanguage=; permcookie=1680x1050; ASP.NET_SessionId=__SESSION; screensize=1680x1050; settings=empty

__POSTDATA

"@

    ## Add the alias name and viewstate into the information we want to post
    $postData = "__EVENTTARGET=SaveTextImageButton&__EVENTARGUMENT=&__VIEWSTATE=__REPLACEMENTVIEWSTATE&headerinnercontrol_folderlist1_TreeView1_CheckedList=&headerinnercontrol_folderlist1_TreeView1_MultipleSelectedList=&sbsearchtext232=&sbsearchfolder232=&sbsearchfield232=0&headerinnercontrol_pulldownmenu1_M1_ContextData=&headerinnercontrol_pulldownmenu1_M2_ContextData=&txtName=__ALIASNAME&txtEmails=$recipient"
    $postData = $postData.Replace("__REPLACEMENTVIEWSTATE", [System.Web.HttpUtility]::UrlEncode($viewState))
    $postData = $postData.Replace("__ALIASNAME", [System.Web.HttpUtility]::UrlEncode($alias))

    ## Replace the placeholders for the post data in the login template, as well as
    ## the ASP.Net session cookie
    $addAlias = $addAlias.Replace("__LENGTH"$postData.Length)
    $addAlias = $addAlias.Replace("__POSTDATA"$postData)
    $addAlias = $addAlias.Replace("__SESSION"$session)

    ## Submit the alias page, and check for errors
    $output = Send-TcpRequest $mailHost 80 -Input $addAlias

    if($output -match "tiptextfailure")
    {
        Write-Error "Failed to add alias."
        $output = $output -replace "(?s).*<div class='tiptextfailure'>([^<]+)</div>.*",'$1'
        Write-Error $output

        exit
    }
    elseif($output -match "frmerror.aspx")
    {
        Write-Error "Failed to add alias -- protocol error"
        Write-Error "Sent:"
        Write-Error $addAlias
        Write-Error "Got:"
        Write-Error $output

        exit
    }
    elseif($output -notmatch "</html>")
    {
        Write-Error "Failed to add alias -- confirmation not present"
        Write-Error "Sent:"
        Write-Error $addAlias
        Write-Error "Got:"
        Write-Error $output

        exit
    }
}

## Extract the ASP.Net session ID frcom a page
function GetSessionId($page)
{
    $page -replace '(?s).*Set-Cookie: ASP.NET_SessionId=([^;]+);.*','$1'
}

## Extract the viewstate from a page
function GetViewState($page)
{
    $page -replace '(?s).*name="__VIEWSTATE" value="([^"]+)".*','$1'
}

. Main

 

Converting C# to PowerShell

Tom recently ran into a problem with the Connect-WebService script given here -- when run against a server that returns malformed headers, he received the error message, “The server committed a protocol violation.

The .NET Framework takes this approach as a security precaution, since parsing non-standard headers is one of the most common sources of vulnerabilities. It’s not that the header-parsing code has vulnerabilities (it may or may not, I have no inside knowledge,) but it is an absolute fact that you increase your risk as you expose more code to malicious input.

If you connect to a malicious server without this safeguard, then, you run an increased risk that the server might use malformed headers to exploit problems in the code intended to deal with it.

If you trust the server, then you might want work with it in all of its malformed-header glory, anyways. According to Tom’s research on the Internet, though, the only way to programmatically do this is through an internal property on the System.Net.Configuration.SettingsSection class.

The accepted solution from the MSDN Forum looks like this:

public static bool SetAllowUnsafeHeaderParsing20()
{
  //Get the assembly that contains the internal class
  Assembly aNetAssembly = Assembly.GetAssembly(typeof(System.Net.Configuration.SettingsSection));
  if (aNetAssembly != null)
  {
    //Use the assembly in order to get the internal type for the internal class
    Type aSettingsType = aNetAssembly.GetType("System.Net.Configuration.SettingsSectionInternal");
    if (aSettingsType != null)
    {
      //Use the internal static property to get an instance of the internal settings class.
      //If the static instance isn't created allready the property will create it for us.
      object anInstance = aSettingsType.InvokeMember("Section",
        BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.NonPublic, null, null, new object[] { });
      if (anInstance != null)
      {
        //Locate the private bool field that tells the framework is unsafe header parsing should be allowed or not
        FieldInfo aUseUnsafeHeaderParsing = aSettingsType.GetField("useUnsafeHeaderParsing", BindingFlags.NonPublic | BindingFlags.Instance);
        if (aUseUnsafeHeaderParsing != null)
        {
          aUseUnsafeHeaderParsing.SetValue(anInstance, true);
          return true;
        }
      }
    }
  }
  return false;
}

Which translates cleanly to PowerShell as this:

$netAssembly = [Reflection.Assembly]::GetAssembly([System.Net.Configuration.SettingsSection])

if($netAssembly)
{
    $bindingFlags = [Reflection.BindingFlags] "Static,GetProperty,NonPublic"
    $settingsType = $netAssembly.GetType("System.Net.Configuration.SettingsSectionInternal")

    $instance = $settingsType.InvokeMember("Section", $bindingFlags, $null, $null, @())

    if($instance)
    {
        $bindingFlags = "NonPublic","Instance"
        $useUnsafeHeaderParsingField = $settingsType.GetField("useUnsafeHeaderParsing", $bindingFlags)

        if($useUnsafeHeaderParsingField)
        {
          $useUnsafeHeaderParsingField.SetValue($instance, $true)
        }
    }
}

Getting Things Done – Outlook Task Automation with PowerShell

Scott just finished writing about his boss (and now him) using Blat to help him Get Things Done.

I've been "Getting Things Done" for some time, and one thing that's always annoyed me was how difficult it is to convert a desire into a categorized task. Outlook 2007 had the opportunity to make this better with the quick-entry field in the TODO bar, but tasks entered that way unfortunately have a due date for "Tomorrow." While not terrible, it means additional fiddling around – which I wanted to avoid in the first place.

The blat solution is helpful, but still requires an intermediary. Once the reminder is in your inbox, it requires an additional step of triaging to convert it into a task.

Some time back, I wrote the following script to make it easy to directly enter tasks into Outlook from PowerShell. It even validates the category (if you specify one,) so you don't get items in the wrong category:

## Add-OutlookTask.ps1
## Add a task to the Outlook Tasks list

param( $description = $(throw "Please specify a description"), $category, [switch$force )

## Create our Outlook and housekeeping variables. 
## Note: If you don't have the Outlook wrappers, you'll need
## the commented-out constants instead

$olTaskItem = "olTaskItem"
$olFolderTasks = "olFolderTasks"

#$olTaskItem = 3
#$olFolderTasks = 13

$outlook = New-Object -Com Outlook.Application
$task = $outlook.Application.CreateItem($olTaskItem)
$hasError = $false

## Assign the subject
$task.Subject = $description

## If they specify a category, then assign it as well.
if($category)
{
    if(-not $force)
    {
        ## Check that it matches one of their already-existing categories, but only
        ## if they haven't specified the -Force parameter.
        $mapi = $outlook.GetNamespace("MAPI")
        $items = $mapi.GetDefaultFolder($olFolderTasks).Items
        $uniqueCategories = $items | Sort -Unique Categories | % { $_.Categories }
        if($uniqueCategories -notcontains $category)
        {
            $OFS = ", "
            $errorMessage = "Category $category does not exist. Valid categories are: $uniqueCategories. " +
                            "Specify the -Force parameter to override this message in the future."
            Write-Error $errorMessage
            $hasError = $true
        }
    }

    $task.Categories = $category 
}

## Save the item if this didn't cause an error, and clean up.
if(-not $hasError) { $task.Save() }
$outlook = $null

 

One valid point that Scott brought up for using a batch file instead of a PowerShell script is that it was easier to run batch files from places like Start | Run, SlickRun, and apps like that. One thing I sometimes do is write a wrapper batch file:

REM todo.bat
powershell -command "Add-OutlookTask '%1'
'@TODO' -Force"

Another option is to associate PowerShell as the interpreter for .PS1 files. Which reminds me...

[C:\Temp]
PS:44 > Add-OutlookTask
"Write about associating PowerShell with PS1" "@Write"