[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2704
  • Last Modified:

Script an IMAP session to get data

Situation:

I have a voicemail server that runs an IMAP server on it.  My voicemail server is not unified messaging or anything like that, it's an "old school" voicemail server.

I want to try and leverage the IMAP server to tell me when there are new messages in a specific users mailbox, and then upon success of that check, launch a script to email a user that a new VM is sitting in their box.

I have worked out the IMAP portion of it, if I telnet in on port 143 I can execute the following (my input commands are the lines that start with "a";

a login username password
OK User logged in
a select inbox
1 EXISTS
0 RECENT
FLAGS (.......)
OK
a search unseen
* SEARCH
OK Completed
a logout

The above is the output if there are 0 unread messages in the mailbox.

The following would be an example if there was 1 unread message in the mailbox;

a login username password
OK User logged in
a select inbox
1 EXISTS
1 RECENT
FLAGS (.......)
OK [UNSEEN 1]
a search unseen
* SEARCH 1
OK Completed
a logout


I'm looking for a way to build a script (powershell??) that can key in on some of the above data, and execute a command based on the criteria.

I see 2 sections that provide me with the information that I'm looking for.

First, when I execute the "a search unseen" command, it returns back either "SEARCH" or "SEARCH 1" as a response.  If the value is a positive integer, then I know there is a new voicemail in the mailbox.

The second area i see that could be used is right after login, you get the "1 Exist 0 Recent" or "1 Exists 1 Recent" outputs.  Again, if the integer before "recent" is positive, then I know there is a voicemail in the mailbox.

What my ultimate goal here is, is to craft up a script (powershell???) that will telnet into my server using the IMAP port, login as the user, and check to see if there is any new voicemail's in the box.  If there is, then I need to script to email the user, it can call a batch file if necessary, I think I can handle that portion of it.

Never user powershell before, maybe that is the right solution here, maybe something else.

Anyone have any ideas on how to do this?
0
Vjz1
Asked:
Vjz1
  • 44
  • 33
1 Solution
 
Chris DentPowerShell DeveloperCommented:
No problem with that, or at least not much of one :) I have a module here:

http://www.indented.co.uk/index.php/2010/11/25/netshell/

If you open the psm1 file in Notepad you'll find the very last function in the file is Test-Smtp. That function uses some of the others in the module to Send and Receive strings from a TCP Port (SMTP commands on 25 in this case).

You / we could do much the same thing for IMAP, you would need the module on your system, but that's not too much to ask (normally).

If you don't like mine, there are other options in PS, including:

http://www.powershellinside.com/powershell/netcmdlets/

Obviously I'm bias, I prefer mine :)

Chris
0
 
Vjz1Author Commented:
HI Chris,

Thanks for your post.  I looked at your site, thanks for posting that.

Let me first say, I've never scripted much of anything before, so some of my questions may seem very basic, there's a reason for that.  :)

Ok so I've looked at your site, I guess the next step is to figure out how to add 2 and 2 together, and 4.

How do I take your NetShell, and get it to do what I want?

presumably, I'd have the NetShell script telnet to my IMAP server on port 143, login, change to the inbox, execute the "a search unseen" and based on the output of that command, either logout, or execute a script to send off an email.

How do I get from A to B, so to speak?

many thanks in advance!
0
 
Chris DentPowerShell DeveloperCommented:
First the module needs "installing". That's not too arduous, open up "(My) Documents", create a folder called WindowsPowerShell (if it doesn't exist), then inside that another called Modules. NetShell, foldername and all, should go in there. So you end up with:

Documents
       |- WindowsPowerShell
                        |- Modules
                                 |- NetShell
                                          |- NetShell.psd1
                                          |- NetShell.psm1

Once you've got that, you have to turn down PowerShell's code checking because my module is unsigned. Run:

Set-ExecutionPolicy RemoteSigned

If you want to know what that does, run:

Get-Help about_execution_policies | more

Next step is to start playing with commands. Open PowerShell then load the module, and off we go:
Import-Module NetShell

# Your IMAP server
$IPAddress = 1.2.3.4
# IMAP port
$Port = 143

$Socket = New-Socket -Protocol Tcp
# Now we can start sending things, we're following it with a Windows line-break, might work, but test, and test again
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "THIS IS OUR COMMAND TEXT`r`n")
# Get the response to our command
Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

Open in new window

The Send / Receive commands should go in much the same way as you do with Telnet. When we get to the result we want to test the response.
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String
If ($Response -NotMatch '0 Recent') {
  # Recent must be positive, unless it can be a negative number, so do stuff
  # Do more stuff here
}

Open in new window

You might do the test on the result of your search instead.

After you're finished with it, remember to close the connection.
Remove-Socket $Socket

Open in new window

It's going to take a bit of playing around to get that working properly. Unfortunately I can't test it from here to make it more explicit. If you give me a full command line I can show you how I'd start out with it though.

Chris
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
Vjz1Author Commented:
Chris,

Thanks, I'm going to give this a shot, but will probably have quite a few questions.

Trying to load your module, i did the "set-executionpolicy remotesigned" and chose Y, but when I try to load the module, i get "netshell.psm1 cannot be loaded.  the file netshell.psm1 is not digitally signed."

what am i missing here?
0
 
Chris DentPowerShell DeveloperCommented:
Hmm kind of expected that to work. Try:

Set-ExecutionPolicy Unrestricted

I don't suppose you redirect Documents to a network drive do you? That's what RemoteSigned makes it insist on.

I really need to get myself a certificate and start signing my code.

Chris
0
 
Vjz1Author Commented:
Ok i got the module to load, i think.  i entered netshell and it brought me back to the PS prompt.

I tried entering some of the commands out outlined above, but I don't think i'm doing this right.

I'm afraid I am so much of a n00b at this, I can't even do the basics, or begin to understand why it isn't working.  And I'm not totally sure what the heck I'm doing.  should i be typing in the commands one line at a time?  should i be building a script in notepad then calling the script?

I'm totally lost here.

For example, i started with your above commands;

# Your IMAP server
$IPAddress = 1.2.3.4
# IMAP port
$Port = 143

The first line went in ok.
The second line gave me an error;

Unexpected token '.0' in expression or statement.
At line:1 char:21
+ $IPAddress = 10.10.0 <<<< .26
    + CategoryInfo       :  ParserError: (.0:String) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId  : UnexpectedToken


0
 
Chris DentPowerShell DeveloperCommented:
My fault, we need to quote the IP address:

$IPAddress = "1.2.3.4"

Otherwise it sort of things it's a number and gets all confused.

Chris
0
 
Vjz1Author Commented:
ok,  moving right along, i'm not at this point;

Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "THIS IS OUR COMMAND TEXT`r`n")

What do you mean by "THIS IS OUR COMMAND TEXT"  ?  
0
 
Chris DentPowerShell DeveloperCommented:
That's where you need to start entering stuff, I don't have an IMAP server here to test against, but when you run telnet the first step is to log in, right? Exactly what do you type to do that? We need to replace the text above with that.

Hitting return in Telnet is simulated by the `r`n part, that's a windows line-break.

Chris
0
 
Vjz1Author Commented:
ok so i got that to go in, it brings me back to the PS prompt.

should i be seeing some kind of output?

i moved onto the 2nd command;
Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

and i got the following error;

Receive-Bytes  :  Cannot process argument transformation on parameter 'ExpectPackets'.  Cannot convert value "l" to type "System.UInt32"  Error:  "Input string was not in a correct format."
0
 
Chris DentPowerShell DeveloperCommented:
"l"

As in lower-case L? Should be numeric one (1) there.

You won't see anything back until it's successfully run Receive-Bytes, it's a conversation, so far you've said things to the server, but you need to be listening to hear what it replies with :)

Chris
0
 
Vjz1Author Commented:
ok i got it to work.

i figured out i needed to add that whole line of code for every action i needed to take.  so i ended up here;

PS C:\Windows\system32> Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a login username password`r`n")

PS C:\Windows\system32> Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")

PS C:\Windows\system32> Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a search unseen`r`n")

i then entered your Receive-Bytes command and it brought me to a >> prompt.

I hit enter, and I saw the entire output that I would see when I telnet in from a CMD window.

So i think we're off to the races here.  I guess the next question is, now that I have my command set working, what the heck to do I do with it??

0
 
Chris DentPowerShell DeveloperCommented:
You can assign the output to a variable like this:

$Response = Receive-Bytes ...

We want to catch the response to the search request, then we can test it.

If ($Response -Match 'Thing you want to check for') {
  # Now do other things
}

Or:

If ($Response -NotMatch 'Thing that indicates there were no results') {
  # Now do other things
}

In my example above I opted to check and see if "0 Recent" was there, if it was we did nothing. If it was not there, implying there were a number of recent messages (?) then it would do stuff. That was this snippet:

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String
If ($Response -NotMatch '0 Recent') {
  # Recent must be positive, unless it can be a negative number, so do stuff
  # Do more stuff here
}

See what I mean?

What would you like to do if you find results?

Chris
0
 
Vjz1Author Commented:
does this whole thing need to be entered in 1 line?

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String
If ($Response -NotMatch '0 Recent') {
  # Recent must be positive, unless it can be a negative number, so do stuff
  # Do more stuff here
}

when i copy and paste it, it seems to be breaking it up, see attached screenshot. Powershell
0
 
Vjz1Author Commented:
is it possible for now, just to get the basics down, to have it write to a file?

if it matches "0 Recent" then write a 0 in a txt file.  if it doesn't match "0 Recent" then write a 1 to the file.  overwriting the file each time.

possible?  hard?  easy?
0
 
Chris DentPowerShell DeveloperCommented:
Nah, PowerShell is happy with splitting over multiple lines which eases the pain on our eyes :) We have to ways to help it out when working with long commands (like the one above). The ` character lets us extend a single command onto another line. So these two are, as far as PS is concerned, the same (prompt is displayed for illustrative purposes):

PS C:\> Get-Process `
>> Explorer
>>

And:

PS C:\> Get-Process Explorer

If it's not a single command, if it's broken by a pipe, or a few other ways then it'll happily carry on. So these two are also the same:

PS C:\> Get-Process |
>>  Where-Object { $_.ProcessName -eq "explorer" }
>>

And:

PS C:\> Get-Process | Where-Object { $_.ProcessName -eq "explorer" }

Notice the extra >> at the end of my examples? That's because PS thinks we're entering a script, it repeats the last one until we press return again.

Next bit, # denotes a comment, it doesn't actually do anything. So the code you have above executes, then does exactly what we told it: nothing. That's why you get no output. Lets add a bit to help it along:
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String
If ($Response -NotMatch '0 Recent') {
  Write-Host "Tested response and found Recent was not 0" -ForegroundColor Green

  # Recent must be positive, unless it can be a negative number, so do stuff
  # Do more stuff here
} Else {
  Write-Host "Recent was 0" -ForegroundColor Cyan
}

Open in new window

Chris
0
 
Chris DentPowerShell DeveloperCommented:
> possible?  hard?  easy?

Easy :)

We'll keep the little colourful comments as well for now, but the file output can be this:
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String
If ($Response -NotMatch '0 Recent') {
  Write-Host "Tested response and found Recent was not 0" -ForegroundColor Green

  1 | Out-File TextFile.txt -Encoding ASCII
} Else {
  Write-Host "Recent was 0" -ForegroundColor Cyan

  0 | Out-File TextFile.txt -Encoding ASCII
}

Open in new window

We can use Redirection as well, but it makes Unicode files which is sometimes a problem:

1 > TextFile.txt
0 > TextFile.txt

In each case we're overwriting the file, not appending.

Chris
0
 
Vjz1Author Commented:
ok got it.

we're getting close.

only issue i see here is that it's returning the incorrect result.

when i telnet in, login, run the command, i see "0 Recent"

I see the same result when i run the script through "0 Recent"

However, the script is returning the "Tested response and found Recent was not 0"

See screenshot below:

is it something I'm doing??       PS2
0
 
Chris DentPowerShell DeveloperCommented:
We need to capture an earlier response. Our script has the memory of a gold-fish. You ask it one thing, it answers, then forgets.

So instead of just Receive-Bytes after it's come back with all that stuff, we need that stuff to be in our variable. That gives us:
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String

Write-Host "This was our response:" -ForegroundColor Yellow
Write-Host $Response

If ($Response -NotMatch '0 Recent') {
  Write-Host "Tested response and found Recent was not 0" -ForegroundColor Green

  1 | Out-File TextFile.txt -Encoding ASCII
} Else {
  Write-Host "Recent was 0" -ForegroundColor Cyan

  0 | Out-File TextFile.txt -Encoding ASCII
}

Open in new window

Chris
0
 
Vjz1Author Commented:
ok i think part of my issue is that i'm hitting RETURN when I shouldn't be.

is there a way i can package this up into a file, then just call the file from the PS command prompt and see what the result is?

i'd like to take away my n00bness as a factor here.

many thanks.
0
 
Vjz1Author Commented:
ok i think i figured that part out.  issue is still not the correct result.

It seems as if it's keying in on the wrong thing here.

hmmm PS
0
 
Chris DentPowerShell DeveloperCommented:
Hmm I wonder... Can we try this please?

Chris
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String

Write-Host "This was our response:" -ForegroundColor Yellow
$Response

If ($Response -NotMatch '0 Recent') {
  Write-Host "Tested response and found Recent was not 0" -ForegroundColor Green

  1 | Out-File TextFile.txt -Encoding ASCII
} Else {
  Write-Host "Recent was 0" -ForegroundColor Cyan

  0 | Out-File TextFile.txt -Encoding ASCII
}

Open in new window

0
 
Vjz1Author Commented:
ok i got it.

i need to add an extra one of these;

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String'

does that stand to reason?
0
 
Chris DentPowerShell DeveloperCommented:
Potentially, it depends how IMAP likes to pass data down to us. Give it a try and see what happens? :)

Chris
0
 
Vjz1Author Commented:
yeah ok, so i think the issue now, is in how the IMAP server is responding.

i'm not getting a consistent result.

see the screenshot.  what do you think in this case?

note that in the screenshot, i was just calling the script, and changing nothing inbetween.  you can see how the results are different. PS
0
 
Vjz1Author Commented:
ok i added this;

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String

and that seems to have fixed it.

testing now, but 10 times in a row it's outputting correctly.
0
 
Chris DentPowerShell DeveloperCommented:
Cool, fingers crossed :)

Chris
0
 
Vjz1Author Commented:
ok i think it's doing what i want.

so the last piece is, based on the condition, i'd like it to call an external script (to send an email).

i'm fairly confident i can get an email script going, but what's the best way to integrate something like this?  have the PS script just do it inline, or have it call another script to do the function?

thanks
0
 
Chris DentPowerShell DeveloperCommented:
PowerShell 2 has a function that can send e-mail (using SMTP):

Send-MailMessage

It's pretty straightforward:

Send-MailMessage -From "someone@domain.com" -To "someoneelse@domain.com" `
  -SmtpServer "mail.domain.com" -Subject "Some Subject" -Body "Something"

It can send HTML mail as well, depends what you'd like to do though. See help for full syntax:

Get-Help Send-MailMessage

That fits into the If statement in our script.

Chris
0
 
Vjz1Author Commented:
wow that works perfectly.

ok so what if i want the first condition, to do nothing?

so the IF condition.  I'd like the IF to do nothing, then the ELSE to perform the email function.
0
 
Chris DentPowerShell DeveloperCommented:
That's no problem, we can just leave the first part blank.

Naturally you should feel free to remove anything you don't want in there, whether that's the Write-Host bit, or the Out-File bit.

Chris
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String

Write-Host "This was our response:" -ForegroundColor Yellow
$Response

If ($Response -NotMatch '0 Recent') {
  Write-Host "Tested response and found Recent was not 0" -ForegroundColor Green

  1 | Out-File TextFile.txt -Encoding ASCII
} Else {
  Write-Host "Recent was 0" -ForegroundColor Cyan

  0 | Out-File TextFile.txt -Encoding ASCII

  Send-MailMessage -From "someone@domain.com" -To "someoneelse@domain.com" `
    -SmtpServer "mail.domain.com" -Subject "Some Subject" -Body "Something"
}

Open in new window

0
 
Vjz1Author Commented:
pure genius.

awarding points now.

can i reply to this later on if I have other questions?
0
 
Vjz1Author Commented:
Amazing help, A+++++++

Feel free to answer my questions any time.
0
 
Chris DentPowerShell DeveloperCommented:
Sure you can, any time.

Chris
0
 
Vjz1Author Commented:
ok thought of one.

now that the basic functionality is there, i'd like to add some logic to it.

i'd like it to count the number of unseen messages, and log it, then use that number the next time the script is run.  something with this kind of logic;

Get the numeric value next to UNSEEN classification (which is formatted as "UNSEEN 2")
If that value is greater than the last time the script ran (using the log file to reference) - send an email
Log that value as the new number in the log file

Playing this through:
-I start with an empty mailbox
Script runs
-there is 0 unseen messages, nothing will happen
Someone leaves me a message
Script runs
-there is now 1 unseen message, an email will get sent and a 1 will get logged to a logfile
Script runs
-there is still 1 unseen message, nothing happens
User checks the voicemail
Script runs
-there is 0 UNSEEN messages, which is less than 1, no email sent, 0 logged to logfile (overwriting)

what would have to be done to include this kind of logic?

thanks a million
0
 
Vjz1Author Commented:
ok thought about this a little more.  i think there are 3 conditions;

UNSEEN = 0
UNSEEN = greater than last time it was checked
UNSEEN = less than last time it was checked

0 is easy, do nothing
greater than, i'd like it to send the email, and note that numeric value in the log file
less than, do nothing, and note the value (even if it's 0) in the log file

doable?
0
 
Chris DentPowerShell DeveloperCommented:
That's no problem, I'll catch up with you in the morning, heading off to bed :)

Chris
0
 
Chris DentPowerShell DeveloperCommented:
Something like this I think.

Chris
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String

Write-Host "This was our response:" -ForegroundColor Yellow
$Response

#
# Pull the value for UNSEEN from the Response
#

$Response -Match '(?<=UNSEEN\s)\d*' | Out-Null
$Unseen = [UInt32]($Matches[0])

#
# Take action based on the value
#

If ($Unseen -eq 0) {
  Write-Host "Unseen is 0, doing nothing" -ForegroundColor Green
} ElseIf ($Unseen -ge 1) {
  Write-Host "Unseen is 1 or more" -ForegroundColor Cyan

  # Get the value from the file, if the file exists
  # Start by initialising the value as 0 (will be overwritten if a file exists)

  $LastUnseen = 0
  If (Test-Path LastUnseen.txt) {
    $LastUnseen = [UInt32](Get-Content LastUnseen.txt)
  }

  # Show us the value

  Write-Host "Value for Last Unseen is $LastUnseen" -ForegroundColor Magenta

  # Now we have both the old and new value, see if we've got more messages

  If ($Unseen -gt $LastUnseen) {

    Write-Host "Sending e-mail" -ForegroundColor Green

    Send-MailMessage -From "someone@domain.com" -To "someoneelse@domain.com" `
      -SmtpServer "mail.domain.com" -Subject "Some Subject" -Body "Something"

  }
}

# Finally, we need to update the value for LastUnseen
# This is outside the loop because we need to do it even if Unseen is 0

$Unseen | Out-File LastUnseen.txt -Encoding ASCII

Open in new window

0
 
Vjz1Author Commented:
ok thanks, i'm going to give this a whirl and see what i can come out with.

also, how do i incorporate the "Import-Module NetShell" into the script so it will run every time?  I noticed that when i close powershelll, i have to reload this module every time before i run the script.  I'd like to schedule this script to run every X minutes, so having it work front to back without intervention is where i'd like to be.

thanks
0
 
Chris DentPowerShell DeveloperCommented:
Pop it (the Import-Module command) in as the first item in your script, that would do.

If you end up running it as a different user account you might want to put the module folder here instead:

C:\Windows\System32\WindowsPowerShell\v1.0\Modules

Then it'll be available to all users.

You may also have to do that Set-ExecutionPolicy thing again.

Chris
0
 
Vjz1Author Commented:
ok great i'll try that.

as for the above code, i got it imported into my script, but it needs just a little tweaking.

i need it to log the actual numeric value to that txt file each time, and base the decision off that value.  let me explain by walking through a scenario;

-0 messages in mailbox
script runs - exits, logs a 0 to the txt file,

-someone leaves a message
-1 message in the mailbox
script runs - checks txt file, 1 is greater than 0, send email,  exits, logs a 1 to the txt file

-another person leaves a message
-2 messages in the mailbox
script runs - checks the txt file, 2 is greater than 1, send email, exits

-user checks and deletes 1 of the messages
-that leaves 1 message in the mailbox
script runs - checks the txt file, 1 is less than 2 (no email needed), exits, logs the new current value of 1 to the txt file.

Basically this method of logging the current value, and checking that value, would allow me to send an email when the value has increased, while also accounting for situations where the value may decrease, but not be 0.

Do you get what i'm trying to say here?  I'm sure it's just a little tweak to your code, which is working as advertised, thanks again.

0
 
Vjz1Author Commented:
basically what i'm trying to say is this;

-check the txt file, if the value retrieved during the script is greater than that value, send email.  then log that value to txt file.

i think that's all i need.
0
 
Chris DentPowerShell DeveloperCommented:
> script runs - exits, logs a 0 to the txt file,

No problem, we got that one.

> script runs - checks txt file, 1 is greater than 0, send email,  exits, logs a 1 to the txt file

And that one :)

> script runs - checks the txt file, 2 is greater than 1, send email, exits

I write 2 to the file here. Otherwise if you check again and it's still 2 you get another notification even though there's nothing new.

> script runs - checks the txt file, 1 is less than 2 (no email needed), exits, logs the new current value of 1 to the txt file.

That will happen :)

Chris
0
 
Vjz1Author Commented:
hmm ok, doesn't seem to be updating the txt file anything past 1.

the txt file stays at 1, when there is 1, 2, 3, 4, etc messages, and it only sends the email on the first time there is an increment, i suspect because the txt file isn't writing the numbers 2, 3, 4, etc.

0
 
Chris DentPowerShell DeveloperCommented:
Modified so it tells us the current value as well.

That might help us identify a problem, if the current value is not being read properly.

Chris
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String

Write-Host "This was our response:" -ForegroundColor Yellow
$Response

#
# Pull the value for UNSEEN from the Response
#

$Response -Match '(?<=UNSEEN\s)\d*' | Out-Null
$Unseen = [UInt32]($Matches[0])

#
# Take action based on the value
#

If ($Unseen -eq 0) {
  Write-Host "Unseen is 0, doing nothing" -ForegroundColor Green
} ElseIf ($Unseen -ge 1) {
  Write-Host "Unseen is 1 or more" -ForegroundColor Cyan

  # Get the value from the file, if the file exists
  # Start by initialising the value as 0 (will be overwritten if a file exists)

  $LastUnseen = 0
  If (Test-Path LastUnseen.txt) {
    $LastUnseen = [UInt32](Get-Content LastUnseen.txt)
  }

  # Show us the value

  Write-Host "Value for Last Unseen is $LastUnseen. Current value is $Unseen." -ForegroundColor Magenta

  # Now we have both the old and new value, see if we've got more messages

  If ($Unseen -gt $LastUnseen) {

    Write-Host "Sending e-mail" -ForegroundColor Green

    Send-MailMessage -From "someone@domain.com" -To "someoneelse@domain.com" `
      -SmtpServer "mail.domain.com" -Subject "Some Subject" -Body "Something"

  }
}

# Finally, we need to update the value for LastUnseen
# This is outside the loop because we need to do it even if Unseen is 0

$Unseen | Out-File LastUnseen.txt -Encoding ASCII

Open in new window

0
 
Vjz1Author Commented:
ok this is my fault.  the response from the IMAP client isn't doing what i thought it would do.

I wrongfully assumed that [UNSEEN] and [UNSEEN 1] meant it was incrementing the amount of unseen messages.  I was wrong.  the 1 stays a 1, indicating there are any unseen messages at all.

slight change necessary here.

instead of keying in on the [UNSEEN 1], the field it looks like we need to key in on, is the following:

* SEARCH
-indicates 0 unseen messages

* SEARCH 1
-indicates there is 1 unseen message

* SEARCH 1 2
-indicates there are 2 unseen messages

and so on and so forth

how hard / possible is it to have your script key in on that line and use it?

I'm thinking the following;  start with a txt file that has a 0 in it.  run the script, have it read that line above, if the last digit in the line (or i suppose you could go for the largest too, whatever is easier from a coding perspective) is greater than the number in the log file, send email, exit, and log that new number to the txt file.

i apologize for this, i didn't realize until i started testing with more than 1 message that i noticed what it was doing.

your help is much appreciated.  i can give you more points if you want.  can that be done?

then ha
0
 
Vjz1Author Commented:
ok "then ha" was supposed to be "thank you"  not sure how my fingers did that one.  ha!
0
 
Vjz1Author Commented:
here's a screenshot of what i'm talking about.

the first section indicates 2 unheard messages

the second section indicates 0 unheard messages screenshot
0
 
Chris DentPowerShell DeveloperCommented:
Sure, we can pull that instead. I've left the variable name the same, this bit is the change:

$Response -Match '(?<=SEARCH\s\d*\s)\d*' | Out-Null

What we do here, is match this string:

SEARCH <SomeNumbers> <SomeMoreNumbers>

And we attempt to get back <SomeMoreNumbers>.

The ?<= bit tells our search to look for a thing, but ignore it when it comes to figuring out values (Positive Look-behind in RegEx parlance). \s matches spaces, \d numeric characters.

Chris
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String

Write-Host "This was our response:" -ForegroundColor Yellow
$Response

#
# Pull the value for UNSEEN from the Response
#

$Response -Match '(?<=SEARCH\s\d*\s)\d*' | Out-Null
$Unseen = [UInt32]($Matches[0])

#
# Take action based on the value
#

If ($Unseen -eq 0) {
  Write-Host "Unseen is 0, doing nothing" -ForegroundColor Green
} ElseIf ($Unseen -ge 1) {
  Write-Host "Unseen is 1 or more" -ForegroundColor Cyan

  # Get the value from the file, if the file exists
  # Start by initialising the value as 0 (will be overwritten if a file exists)

  $LastUnseen = 0
  If (Test-Path LastUnseen.txt) {
    $LastUnseen = [UInt32](Get-Content LastUnseen.txt)
  }

  # Show us the value

  Write-Host "Value for Last Unseen is $LastUnseen. Current value is $Unseen." -ForegroundColor Magenta

  # Now we have both the old and new value, see if we've got more messages

  If ($Unseen -gt $LastUnseen) {

    Write-Host "Sending e-mail" -ForegroundColor Green

    Send-MailMessage -From "someone@domain.com" -To "someoneelse@domain.com" `
      -SmtpServer "mail.domain.com" -Subject "Some Subject" -Body "Something"

  }
}

# Finally, we need to update the value for LastUnseen
# This is outside the loop because we need to do it even if Unseen is 0

$Unseen | Out-File LastUnseen.txt -Encoding ASCII

Open in new window

0
 
Vjz1Author Commented:
ok cool, got this into the script.  it runs, but doesn't seem to function quite right.

when there is 1 message, it says there is 0, and doesn't update the txt file.

see screenshot, the first time it ran, there were 0 messages, the 2nd time it ran, there was 1; screenshot
0
 
Chris DentPowerShell DeveloperCommented:
Hmm our SEARCH is returning on additional numbers in the first (so it'll be 0), and only one in the second (so it'll also be 0).

Chris
0
 
Chris DentPowerShell DeveloperCommented:
Er sorry, that's probably not very clear.

So the script is doing what we told it, but the output we're seeing is not as helpful as we hoped.

Do you know what the other numbers mean after SEARCH?

Chris
0
 
Vjz1Author Commented:
yes, they indicate the number of unread messages.  example outputs;

* SEARCH   (no messages)

* SEARCH 1  (1 message)

* SEARCH 1 2  (2 messags)

* SEARCH 1 2 3  (3 messages)


does that help?
0
 
Vjz1Author Commented:
so essentially we want it to pull the last number (as that is the relevant information here) compare that number to the number in the log file, if greater than, send email, finally log the new number in the txt file.
0
 
Chris DentPowerShell DeveloperCommented:
This catches the last number in the string. I hope it works, might not so be prepared for disappointment ;)

Chris
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String

Write-Host "This was our response:" -ForegroundColor Yellow
$Response

#
# Pull the value for UNSEEN from the Response
#

$Response -Match '(?<=SEARCH[\s\d]*)\d*$' | Out-Null
$Unseen = [UInt32]($Matches[0])

#
# Take action based on the value
#

If ($Unseen -eq 0) {
  Write-Host "Unseen is 0, doing nothing" -ForegroundColor Green
} ElseIf ($Unseen -ge 1) {
  Write-Host "Unseen is 1 or more" -ForegroundColor Cyan

  # Get the value from the file, if the file exists
  # Start by initialising the value as 0 (will be overwritten if a file exists)

  $LastUnseen = 0
  If (Test-Path LastUnseen.txt) {
    $LastUnseen = [UInt32](Get-Content LastUnseen.txt)
  }

  # Show us the value

  Write-Host "Value for Last Unseen is $LastUnseen. Current value is $Unseen." -ForegroundColor Magenta

  # Now we have both the old and new value, see if we've got more messages

  If ($Unseen -gt $LastUnseen) {

    Write-Host "Sending e-mail" -ForegroundColor Green

    Send-MailMessage -From "someone@domain.com" -To "someoneelse@domain.com" `
      -SmtpServer "mail.domain.com" -Subject "Some Subject" -Body "Something"

  }
}

# Finally, we need to update the value for LastUnseen
# This is outside the loop because we need to do it even if Unseen is 0

$Unseen | Out-File LastUnseen.txt -Encoding ASCII

Open in new window

0
 
Vjz1Author Commented:
ok so here's what i got, see screenshot.

1st thing, i think we need to get it to log the 0 character.  as we've been going through this, i've noticed that when the value is 0, it leaves the txt file empty.  the rest of the script was working as advertised though, so i didn't think it was an issue, now i think maybe it is.

can you tell from the output below where it may be going wrong?

for this example there is 1 message in the mailbox, you can see that as indicated by the "* SEARCH 1" line.

thanks. screenshot
0
 
Chris DentPowerShell DeveloperCommented:
Ah it's failing the match. I was hoping we'd be able to match on the end of line (that's what $ is in my regex), but apparently that won't behave.

Slight adjustment required as well to get rid of that indexing error.

Chris
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String

Write-Host "This was our response:" -ForegroundColor Yellow
$Response

#
# Pull the value for UNSEEN from the Response
#

$Unseen = 0
If ($Response -Match '(?<=SEARCH[\s\d]*)\d*(?=\s?[\r\n])') {
  $Unseen = [UInt32]($Matches[0])
}

#
# Take action based on the value
#

If ($Unseen -eq 0) {
  Write-Host "Unseen is 0, doing nothing" -ForegroundColor Green
} ElseIf ($Unseen -ge 1) {
  Write-Host "Unseen is 1 or more" -ForegroundColor Cyan

  # Get the value from the file, if the file exists
  # Start by initialising the value as 0 (will be overwritten if a file exists)

  $LastUnseen = 0
  If (Test-Path LastUnseen.txt) {
    $LastUnseen = [UInt32](Get-Content LastUnseen.txt)
  }

  # Show us the value

  Write-Host "Value for Last Unseen is $LastUnseen. Current value is $Unseen." -ForegroundColor Magenta

  # Now we have both the old and new value, see if we've got more messages

  If ($Unseen -gt $LastUnseen) {

    Write-Host "Sending e-mail" -ForegroundColor Green

    Send-MailMessage -From "someone@domain.com" -To "someoneelse@domain.com" `
      -SmtpServer "mail.domain.com" -Subject "Some Subject" -Body "Something"

  }
}

# Finally, we need to update the value for LastUnseen
# This is outside the loop because we need to do it even if Unseen is 0

$Unseen | Out-File LastUnseen.txt -Encoding ASCII

Open in new window

0
 
Vjz1Author Commented:
ok this is working just how i want.

i'm going to try moving this to a server, putting in the beginning lines;

Import-Module C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetShell
and
Set-ExecutionPolicy

I'll let you know if I have any problems.

my question is, every time i run the Set-ExecutionPolicy RemoteSigned  command, it requires a response of Y.  can i build that answer into the beginning of this script?  or is running this command a 1 time thing, even after reboots?

thanks.
0
 
Vjz1Author Commented:
oh also, how do i call this in a batch file?

i tried putting in the path to the powershell file, but all it did was open the file in notepad.

thanks.
0
 
Chris DentPowerShell DeveloperCommented:
> my question is, every time i run the Set-ExecutionPolicy

It's a one off, you won't have to do it every time so it won't need to go in the script.

> oh also, how do i call this in a batch file?

Like this in the Scheduled task, no need for a batch file:

PowerShell.exe ScriptName.ps1

It'll ask if you want to move the script name into the arguments, you do. You may have to tick the box about running with highest privileges, but check it out and see (depending on server OU version).

Chris
0
 
Vjz1Author Commented:
ok, having issues with the digitally signed thing now.

see screenshot.

any suggestions? screenshot
0
 
Chris DentPowerShell DeveloperCommented:
Ah yes, then we'll have to go with Unrestricted there as well for now I'm afraid.

Chris
0
 
Vjz1Author Commented:
ok, what does that mean / what do i need to do?
0
 
Chris DentPowerShell DeveloperCommented:

Ah sorry, back to the beginning, it's this command:


Set-ExecutionPolicy Unrestricted


Chris
0
 
Vjz1Author Commented:
got it thanks.

i'm going to try and put this all together and i'll let you know the outcome.

many thanks.
0
 
Vjz1Author Commented:
ok, one more bug to squash i think.

it doesn't seem to be behaving consistently.  

see my screen shot.

9 times out of 10, you see the correct output as you see in the top portion, then i run it again, and i get what you see in the bottom portion, which produces an incorrect value.

Any reason for this or anything we can do about it?  what about if something like if you don't see the word "SEARCH" at all, then do nothing and exit immediately, don't update the txt file? screenshot
0
 
Chris DentPowerShell DeveloperCommented:
Sure, we can do that. Take this one for a spin? We look for SEARCH first, and if we don't find it we Exit.

Chris
Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")
$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String

Write-Host "This was our response:" -ForegroundColor Yellow
$Response

#
# Pull the value for UNSEEN from the Response
#

If ($Response -Match 'SEARCH') {
  $Unseen = 0
  If ($Response -Match '(?<=SEARCH[\s\d]*)\d*(?=\s?[\r\n])') {
    $Unseen = [UInt32]($Matches[0])
  }
} Else {
  Exit
}

#
# Take action based on the value
#

If ($Unseen -eq 0) {
  Write-Host "Unseen is 0, doing nothing" -ForegroundColor Green
} ElseIf ($Unseen -ge 1) {
  Write-Host "Unseen is 1 or more" -ForegroundColor Cyan

  # Get the value from the file, if the file exists
  # Start by initialising the value as 0 (will be overwritten if a file exists)

  $LastUnseen = 0
  If (Test-Path LastUnseen.txt) {
    $LastUnseen = [UInt32](Get-Content LastUnseen.txt)
  }

  # Show us the value

  Write-Host "Value for Last Unseen is $LastUnseen. Current value is $Unseen." -ForegroundColor Magenta

  # Now we have both the old and new value, see if we've got more messages

  If ($Unseen -gt $LastUnseen) {

    Write-Host "Sending e-mail" -ForegroundColor Green

    Send-MailMessage -From "someone@domain.com" -To "someoneelse@domain.com" `
      -SmtpServer "mail.domain.com" -Subject "Some Subject" -Body "Something"

  }
}

# Finally, we need to update the value for LastUnseen
# This is outside the loop because we need to do it even if Unseen is 0

$Unseen | Out-File LastUnseen.txt -Encoding ASCII

Open in new window

0
 
Vjz1Author Commented:
ok almost there i think.  i found this bug after setting up a scheduled task.

every time i close the PS window, and open a new one (essentially every time the task is run), it wants a response to this question, see screenshot;

anyway around this? screenshot
0
 
Vjz1Author Commented:
in addition, i'm still seeing various results.

see screenshot.

i executed the script 3 times in a row, and got 3 different results.

any ideas? screenshot
0
 
Chris DentPowerShell DeveloperCommented:
The execution error and prompt. It'll do that as long as you're loading the script from a network path. Do you redirect desktop somewhere else? It should be possible to make it go away if you put the NetShell module in:

C:\Windows\System32\WindowsPowerShell\v1.0\Modules

That way it's loading from the local machine only and should stop bugging you.

It's difficult to know what to suggest when it comes to the intermittent results. If I were doing it I would try and trace the circumstances in which the responses vary. I'd probably have a packet sniffer up as well to make sure I was capturing everything.

Can you post the version you're using? Drop anything sensitive, of course, it's just in case there's something we can do there.

Chris
0
 
Vjz1Author Commented:
That location is where i have the netshell scripts;

C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetShell

see screenshot,

when i launch PS, i get that prompt every time screenshot
0
 
Chris DentPowerShell DeveloperCommented:
Oh, hmmm, that's really odd. It shouldn't be doing that, or I don't think it should.

Well we could take everything we're using out of there and put it at the top of your main script? Then you don't have to worry about loading it?

That means adding all of the stuff below to the top of your script, and getting rid of Import-Module NetShell.

Chris
Function New-Socket
{
  <#
    .Synopsis
      Creates a socket.
    .Description
      New-Socket creates a socket with System.Net.Sockets.Socket.
    .Parameter EnableBroadcast
      Whether or not the socket will send and receive using the a broadcast address.
    .Parameter IPAddress
      The local IP address used in the end-point for the socket when the socket is bound. [IPAddress]::Any is used by default.
    .Parameter NoTimeout
      If specified the timeout values will be ignored. Send and Receive operations will not timeout.
    .Parameter Port
      The local port if binding is used.
    .Parameter Protocol
      The protocol used by the socket, either TCP or UDP.
    .Parameter ReceiveTimeout
      How long to wait before timing out a receive operation in seconds
    .Parameter SendTimeout
      How long to wait before timing out a send operation in seconds
  #>

  [CmdLetBinding(DefaultParameterSetName = "Socket")]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ParameterSetName = "")]
    [ValidateSet("Tcp", "Udp")]
    [Net.Sockets.ProtocolType]$Protocol,
    
    [Parameter(Position = 1, ParameterSetName = "BoundSocket")]
    [Net.IPAddress]$IPAddress = [Net.IPAddress]::Any,
    
    [Parameter(Position = 2, ParameterSetName = "BoundSocket")]
    [UInt32]$Port,
    
    [Parameter(ParameterSetName = "")]
    [Switch]$EnableBroadcast,

    [Parameter(ParameterSetName = "")]
    [Switch]$NoTimeout,
    
    [Parameter(ParameterSetName = "")]
    [ValidateRange(1, 30)]
    [Int32]$ReceiveTimeOut = 5,
    
    [Parameter(ParameterSetName = "")]
    [ValidateRange(1, 30)]
    [Int32]$SendTimeOut = 5
  )

  Switch ($Protocol)
  {
    $([Net.Sockets.ProtocolType]::Tcp) { $SocketType = [Net.Sockets.SocketType]::Stream }
    $([Net.Sockets.ProtocolType]::Udp) { $SocketType = [Net.Sockets.SocketType]::Dgram } 
  }

  $Socket = New-Object Net.Sockets.Socket(
    "InterNetwork",
    $SocketType,
    $Protocol)
    
  If ($Protocol -eq [Net.Sockets.ProtocolType]::Udp) {
    $Socket.EnableBroadcast = $EnableBroadcast
  }
  $Socket.ExclusiveAddressUse = $False
  If (!$NoTimeout) {
    $Socket.SendTimeOut = $SendTimeOut * 1000
    $Socket.ReceiveTimeOut = $ReceiveTimeOut * 1000
  }

  If ($PsCmdLet.ParameterSetName -eq "BoundSocket") {
    $LocalEndPoint = [Net.EndPoint](New-Object Net.IPEndPoint($IPAddress, $Port))
    $Socket.Bind($LocalEndPoint)
  }

  Return $Socket
}

Function Send-Bytes {
  <#
    .Synopsis
      Sends bytes to the specified IP address and port.
    .Description
      Sends a byte array using the specified socket and end-point.
    .Parameter Data
      A byte array containing the encoded data to send. For TCP streams this CmdLet prefixes the data length.
    .Parameter IPAddress
      The remote IP address to the data to.
    .Parameter Port
      The remote port.
    .Parameter Socket
      The socket to use for this operation
  #>
  
  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0)]
    [Net.Sockets.Socket]$Socket,
    
    [Parameter(Mandatory = $True, Position = 1)]
    [Net.IPAddress]$IPAddress,
    
    [Parameter(Mandatory = $True, Position = 2)]
    [UInt32]$Port,
    
    [Parameter(Mandatory = $True)]
    [Byte[]]$Data
  )

  $ServerEndPoint = [Net.EndPoint](New-Object Net.IPEndPoint($IPAddress, $Port))
  
  If ($Socket.ProtocolType -eq [Net.Sockets.ProtocolType]::Tcp) {
    # Prefix the data length for Tcp. Reverse the array to account for endian order.
    # $Length = [BitConverter]::GetBytes([UInt16]$Data.Length); [Array]::Reverse($Length)
    # $Data = $Length + $Data
    
    If (!$Socket.Connected) {
      $Socket.Connect($ServerEndPoint)
    }

    If ($Socket.Connected) {
      [Void]$Socket.Send($Data)
    }
  } Else {
    [Void]$Socket.SendTo($Data, $ServerEndPoint)
  }
}

Function Receive-Bytes {
  <#
    .Synopsis
      Start a receive operation for the specified socket.
    .Description
      Receive-Bytes expects a bound socket as a parameter. This operation will fail if the socket is not bound to a local end-point.
    .Parameter BufferSize
      The size of the receive buffer, by default the buffer is 1024 bytes.
    .Parameter Continuous
      The CmdLet should continue listening until the script is forcefully terminated.
    .Parameter ExpectPackets
      The number of packets Receive-Bytes should expect when using TCP. By default Receive-Bytes keeps trying which may result in a 
      timeout error, closing the underlying connection. If the stream is not continuous, ExpectPackets should be set to 1.
    .Parameter ListenTimeout
      How long the CmdLet should listen for bytes on the connected socket
    .Parameter Socket
      A connected or disconnected socket. If an IP Address and Port is supplied a connection attempt will be made.
  #>
    
  [CmdLetBinding(DefaultParameterSetName = "Timeout")]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ParameterSetName = "")]
    [Net.Sockets.Socket]$Socket,
    
    [Parameter(Position = 1, ParameterSetName = "")]
    [UInt32]$BufferSize = 1024,

    [Parameter(ParameterSetName = "")]
    [Net.IPAddress]$IPAddress,

    [Parameter(ParameterSetName = "")]
    [UInt32]$Port,
    
    [Parameter(ParameterSetName = "Timeout")]
    [TimeSpan]$ListenTimeout = $(New-TimeSpan -Seconds 5),
    
    [Parameter(ParameterSetName = "Continuous")]
    [Switch]$Continuous,
    
    [Parameter(ParameterSetName = "")]
    [UInt32]$ExpectPackets = [UInt32]::MaxValue
  )

  $Buffer = New-Object Byte[] $BufferSize
  # A placeholder for the sender (conencting host)
  $EndPoint = [Net.EndPoint](New-Object Net.IPEndPoint([Net.IPAddress]::Any, 0))

  If ($Socket.ProtocolType -eq [Net.Sockets.ProtocolType]::Udp) {

    $StartTime = Get-Date
    While ($(New-TimeSpan $StartTime $(Get-Date)) -lt $ListenTimeout -Or $Continuous) {

      Try { $BytesReceived = $Socket.ReceiveFrom($Buffer, [Ref]$EndPoint) } Catch { }
      If ($?) { 
        Write-Verbose "Received $BytesReceived from $($EndPoint.Address.IPAddressToString)"
        "" | Select-Object @{n='Data';e={ @(,$Buffer[0..$($BytesReceived - 1)]) }}, @{n='Sender';e={ $EndPoint.Address.IPAddressToString }}
      }
    }
  } Else {
    If (!$Socket.Connected) {
      $ServerEndPoint = [Net.EndPoint](New-Object Net.IPEndPoint($IPAddress, $Port))
      $Socket.Connect($ServerEndPoint)
    }

    $PacketCount = 0  
    Do {
      $PacketCount++
      Try { $BytesReceived = $Socket.Receive($Buffer) } Catch { }
      If ($?) {
        Write-Verbose "Received $BytesReceived from $($Socket.RemoteEndPoint): Connection State: $($Socket.Connected)"
        "" | Select-Object @{n='Data';e={ $Buffer[0..$($BytesReceived - 1)] }}, @{n='Sender';e={ $EndPoint.Address.IPAddressToString }}
      }
    } While ($Socket.Connected -And $BytesReceived -gt 0 -And $PacketCount -lt $ExpectPackets)
  }
}

Function Remove-Socket
{
  <#
    .Synopsis
      Close an open socket
    .Description
      Remove-Socket will close down both send and receive channels then close a socket. Remove-Socket does not have a return value.
    .Parameter Socket
      Any running socket as System.Net.Sockets.Socket.
  #>

  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0)]
    [Net.Sockets.Socket]$Socket
  )

  $Socket.Shutdown("Both")
  $Socket.Close()
}

Function ConvertTo-Byte {
  <#
    .Synopsis
      Returns a byte array representing an ASCII string.
    .Parameter String
      The string value to convert.
  #>

  Param(
    [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
    [String]$String
  )
  
  Process {
    Return [Text.Encoding]::ASCII.GetBytes($String)
  }
}        

Function ConvertTo-String {
  <#
    .Synopsis
      Returns a byte array representing an ASCII string.
    .Parameter Data
      The Byte Array to convert
  #>

  Param(
    [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
    [Byte[]]$Data
  ) 
  
  Process {
    Return [Text.Encoding]::ASCII.GetString($Data)
  }
}

Open in new window

0
 
Vjz1Author Commented:
ok that seems to work fine - thanks.

on the last part of this, getting it into a scheduled task, i'm having problems with.

i'm worked the issue down to this;

--when i manually open PS, CD into the directory, and run the script, it runs fine
--when i call PS by path, and the script by path, it doesn't do what it should do;

in scenario 2, i'm doing this:

c:\> c:\windows\system32\windowspowershell\v1.0\powershell.exe c:\scripts\script.ps1

it seems to run the file ok, but it doesn't complete.  i get the print to screen output "this was our response" from the script itself, but nothing below that.  its as if it starts the script, but it fails to do something along the way.

I have changed every relative link to an absolute link in the script (i.e. changed  file.txt to c:\scripts\file.txt)  that didn't seem to do much.

any ideas on this, or am i just dense and am missing something obvious about running the script from a script?

thanks.
0
 
Vjz1Author Commented:
nevermind, i figured it out using a batch file.

thx
0
 
Vjz1Author Commented:
Hi Chris,

The script works great.  I've been using it for a few days now as proof of concept and haven't had any issues.

Wondering if you know how to do one more modification to this script.

This script is all based around logging into a server with a unique username and password, logging a unique value to a txt file, and also if certain criteria are met, sending out an email.

Is it possible to make the username/password combination, the text file (was thinking have it use the login username as the file name as that will always be unique) as well as a unique email string, variables in the script?

What I'm trying to achieve here is have the script be able to run through multiple sessions, so i don't need a separate script for every user that I need to run this for.

Something like this?
-define variables

-open socket
-login (using first set in variables)
-pull integer
-perform check
-send email if necessary (using variable)
-log integer to file (using username as file name)
-close socket

-move on to next set in variables and perform the same operation

If we could set this up in a way that it uses variables, I could just have 1 script that is easily modifiable when i need to add / delete users, rather than building and breaking down an entire script each time, as well as an associated scheduled task for each version.

Is this possible?

I will post the code here too so you can see where you got me last week.

also if you want this as a new question, i'd be more than happy to do that so i can get you 500 more points.

many thanks!

#
# Define IMAP (voicemail) server address
#

$IPAddress = "10.10.0.6"


#
# Define IMAP port
#

$Port = 143


#
# Create new TCP socket
#

$Socket = New-Socket -Protocol Tcp


#
# Login to Voicemail server via IMAP
#

Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a login 1234 62956728`r`n")


#
# Change directory to Inbox, search for unseen messages and capture the string
#

Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a select inbox`r`n")

Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a search unseen`r`n")


$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 -ListenTimeout (New-TimeSpan -Seconds 30) |
  ConvertTo-String

$Response = Receive-Bytes $Socket -IPAddress $IPAddress -Port $Port -ExpectPackets 1 `
  -ListenTimeout (New-TimeSpan -Seconds 30) | ConvertTo-String | Out-String


#
# Output the string to the console
#

Write-Host "Results of searching the inbox:" -ForegroundColor Yellow
$Response


#
# Pull the value for UNSEEN from the Response
#

If ($Response -Match 'SEARCH') {
  $Unseen = 0
  If ($Response -Match '(?<=SEARCH[\s\d]*)\d*(?=\s?[\r\n])') {
    $Unseen = [UInt32]($Matches[0])
  }
} Else {
  Exit
}



#
# Take action based on the value
#

If ($Unseen -eq 0) {
  Write-Host "Unseen is 0, doing nothing" -ForegroundColor Green
} ElseIf ($Unseen -ge 1) {
  Write-Host "Unseen is 1 or more" -ForegroundColor Cyan

  # Get the value from the file
  # Start by initialising the value as 0 (will be overwritten if a file exists)

  $LastUnseen = 0
  If (Test-Path C:\etc\VoicemailScripts\testing\LastUnseen.txt) {
    $LastUnseen = [UInt32](Get-Content C:\etc\VoicemailScripts\testing\LastUnseen.txt)
  }

  # Show the value

  Write-Host "Value for Last Unseen is $LastUnseen.  Current value is $Unseen." -ForegroundColor Magenta

  # Now we have both the old and new value, see if we've got more messages

  If ($Unseen -gt $LastUnseen) {

    Write-Host "Sending e-mail" -ForegroundColor Green

    Send-MailMessage -From "First Last <tbrady@patriots.com>" -To "First Last <tbrady@patriots.com>" `
      -SmtpServer "server.patriots.com" -Subject "You have a new unheard voicemail" -BodyAsHtml "You have a new unheard message in your voice mailbox.  To listen to your voicemail, call 123-555-1234 and follow the prompts."

  }
}



#
# Update the value for LastUnseen.txt file
# This is outside the loop because it needs to be done even if the Unseen value is 0
#

$Unseen | Out-File C:\etc\VoicemailScripts\testing\LastUnseen.txt -Encoding ASCII


#
# Close the open socket connection
#

Remove-Socket $Socket

Open in new window

0
 
Chris DentPowerShell DeveloperCommented:
It's pretty simple, fortunately :)

You can do a direct replacement in most cases here.

For example, if you want the username in a variable you'd replace this:

> Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a login 1234 62956728`r`n")

With this at the top:

$Username = "SomeUser"

And this lower down:

Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a login $Username 62956728`r`n")

It's quite happy with you including variables like that, provided the string is surrounded by " " rather than ' '.

For send-email, if you want to make it optional we could have:

$EmailNotify = $True

Then when it comes to seeing if we should send, we can add:

If ($Unseen -gt $LastUnseen -And $EmailNotify) {

And so on. Is that what you had in mind?

Cheers,

Chris
0
 
Vjz1Author Commented:
Sort of.

So I've been playing with variables, and i pretty much have the entire script running off variable values now.  The problem is, it's only good for 1 value.

Example:

$LoginIDandPW = "1234 password"

then i use that variable here;

Send-Bytes $Socket -IPAddress $IPAddress -Port $Port -Data $(ConvertTo-Byte "a login $LoginIDandPW`r`n")

and that works, does what it should do, but what i'd like, is to specify an array of users (username, password, email address and specific message body) and have the script perform the same function across every user in that array.

i also setup a variable for the log file we're using, so it's unique, just pulls the userID.

is that do-able?

thanks.
0

Featured Post

Prep for the ITIL® Foundation Certification Exam

December’s Course of the Month is now available! Enroll to learn ITIL® Foundation best practices for delivering IT services effectively and efficiently.

  • 44
  • 33
Tackle projects and never again get stuck behind a technical roadblock.
Join Now