Monday, December 06, 2004

Setting Outlook 2003 Junk Email Options Programmatically

[If you are trying to do this using Exchange 2007 please see http://gsexdev.blogspot.com/2007/07/turning-on-filter-junk-email-in.html ]


The Outlook 2003 Junk email filter has a number of different options that can be configured through (tools-options) that determine how the filter will treat email it detects as spam in your inbox. The configuration settings are stored on a hidden extendedrule message in a user’s inbox. The options settings are held in two MAPI properties on this hidden message the first one being

http://schemas.microsoft.com/mapi/proptag/0x61010003 which stores a long value that sets that level of junk email protection you want the long values for each of the setting are

No Automatic filtering = -1
Low = 6
High = 3
Safe Lists only = -2147483648

The “Permanently delete suspected junk e-mail instead of moving it to the Junk E-mail folder” is stored in the http://schemas.microsoft.com/mapi/proptag/0x61020003 as

Disabled = 0
Enabled = 1

On a new mailbox or a mailbox where Outlook 2003 is not being used (and this setting hasn’t been turned on with OWA2003) this hidden message won’t exist in the user inbox (also the junk e-mail folder won't have been created). One way to create this rule (and folder) is to turn on “filter junk email” in OWA2003. You can do this programmatically using a Webdav post and some OWA commands. Eg

xmlstr = ""
xmlstr = xmlstr & "Cmd=options" & vbLf
xmlstr = xmlstr & "junkemailstate=1" & vbLf
xmlstr = xmlstr & "cmd=savejunkemailrule" & vbLf
Set ObjxmlHttp = CreateObject("Microsoft.XMLHTTP")
ObjxmlHttp.Open "POST", "http://server/exchange/mailbox/", False,
"domain\user", "password"
ObjxmlHttp.setRequestHeader "Accept-Language:","en-us"
ObjxmlHttp.setRequestHeader "Content-type:","application/x-www-UTF8-encoded"
ObjxmlHttp.setRequestHeader "Content-Length:", Len(xmlstr)
ObjxmlHttp.Send xmlstr
Wscript.echo ObjxmlHttp.responseText

Once you know for sure that the rule message is going to be there you can go about using a script to modify the Outlook Junk-email settings. The script I use to do this is based on Exoledb (you could also use Webdav if you wanted to do it remotely). What the script does is searches the users inbox for any IPM.ExtendedRule.Message messages that has the http://schemas.microsoft.com/mapi/proptag/0x65EB001E property (which I’m not really sure does) set to JunkEmailRule. Once this message is identified it’s opened up using ADO/Exoledb and the two properties I mentioned above are modified. In the sample script it sets the junk email option to high. I’ve posted a copy of the two scripts in this article here

The code for the above script looks like this

mailbox = "yourmailbox"
Set Rs = CreateObject("ADODB.Recordset")
Set msgobj = CreateObject("CDO.Message")
set Rec = CreateObject("ADODB.Record")
set Rec1 = CreateObject("ADODB.Record")
Set Conn = CreateObject("ADODB.Connection")
mailboxurl = "file://./backofficestorage/yourdomain.com/MBX/" & mailbox & "/"
Conn.Provider = "ExOLEDB.DataSource"
Rec.Open mailboxurl, ,3
mailboxurl = "file://./backofficestorage/ yourdomain.com /MBX/" & mailbox & "/inbox/"
SSql = "SELECT ""DAV:href"", ""DAV:uid"", ""DAV:contentclass"" FROM scope('shallow traversal of """ & mailboxurl & """') "
SSql = SSql & " WHERE ""http://schemas.microsoft.com/mapi/proptag/0x65EB001E"" = 'JunkEmailRule' and ""http://schemas.microsoft.com/exchange/outlookmessageclass"" = 'IPM.ExtendedRule.Message' "
Rs.CursorLocation = 3 'adUseServer = 2, adUseClient = 3
Rs.CursorType = 3
rs.open SSql, rec.ActiveConnection, 3
if Rs.recordcount <> 0 then
Rs.movefirst
while not rs.eof
wscript.echo Rs.Fields("DAV:href").Value
rec1.open Rs.Fields("DAV:href").Value,,3
wscript.echo rec1.fields("http://schemas.microsoft.com/mapi/proptag/0x61010003").Value
wscript.echo rec1.fields("http://schemas.microsoft.com/mapi/proptag/0x61020003").Value
rec1.fields("http://schemas.microsoft.com/mapi/proptag/0x61010003").Value = 3
rec1.fields("http://schemas.microsoft.com/mapi/proptag/0x61020003").Value = 0
rec1.fields.update
rec1.close
rs.movenext
wend
end if
rs.close

14 comments:

Anonymous said...

Hello Glen
Very nice article.
Do you of any way that we can let a specific e-mail come thru and by-pass IMF.

My problem has to do with activating black berries. Every time a message comes from the handheld to user mail box imf treats it as spam and puts it in junk mail folder instead of leaving it in Inbox
What we have to do is just before activation a user has to go to OWA uncheck junk mail setting save it and then message comes to inbox. IMF assings a value of 4 to the blackberry message.

can you help
thanks

Glen said...

If you want to bypass the IMF you need to add the IP address of the sending server to the Global Accept list in Connection filtering (under Global Setting-Message Delivery). This will mean that connection filtering will be bypass for anymail sent from that IP address. The other way would be to setup a seperate Virtual SMTP Server on a another Ipaddress where the IMF isn't enabled and then see if you can configure the Blackberry to send via this Ip.

Cheers
Glen

Anonymous said...

Glen, I can't tell you how much this site has helped with understanding and implementing IMF. Do you know of any way to read the junkmailstate parameter. I'd like to create a script that every so often makes sure that folks haven't turned it off.

Glen said...

To see if someone has diabled the junkemail setting one way to do this is to check the PR_Rule_state property on the Junk Email Rule object. To do this you can write a script that queries this property for example have a look at

http://msgdev.mvps.org/exdevblog/tempbin/webdavjmailshow.zip

Briam said...

This is great stuff. We have used your script for looking at settings to get an idea of who is using JunkF and who is not. Now we want to at least enable this for everyone who does not have it on.

I have an account that has 'full' rights to all the mailbox... is there an easy way to modify this to recurse all the mailboxes on a given server?

Glen said...

You might want to have look at the version on Evans blog http://blogs.technet.com/evand/archive/2005/01/31/363935.aspx. This uses a text file of all the account you want to modify. You could easly edit this and use an ADSI query just to suppy the names of the mailboxes you want to run it against. Just have a look at any of my other samples that do this i posted some in the last couple of weeks that contains the query you need.

Briam said...

Thank you for pointing me in the right direction. That is what I needed.

John said...

Hi Glen,

I was just wondering if you could give me some insight as to why this part of your script successfully creates the junk e-mail folder in a user's mailbox, but does not create the hidden Junk E-mail Rule.EML in the user's inbox? Thanks for all the great postings on Exchange programming.

xmlstr = ""
xmlstr = xmlstr & "Cmd=options" & vbLf
xmlstr = xmlstr & "junkemailstate=1" & vbLf
xmlstr = xmlstr & "cmd=savejunkemailrule" & vbLf
Set ObjxmlHttp = CreateObject("Microsoft.XMLHTTP")
ObjxmlHttp.Open "POST", "http://server/exchange/mailbox/", False,
"domain\user", "password"
ObjxmlHttp.setRequestHeader "Accept-Language:","en-us"
ObjxmlHttp.setRequestHeader "Content-type:","application/x-www-UTF8-encoded"
ObjxmlHttp.setRequestHeader "Content-Length:", Len(xmlstr)
ObjxmlHttp.Send xmlstr
Wscript.echo ObjxmlHttp.responseText

Glen said...

It should work okay it still seems to work okay for me, all it does is simulates what happens via OWA have you tried settig that option in OWA does it create the rule ?. You might also what to look at the information on Evan Dodd's blog there's also a much better scirpt posted there. http://blogs.technet.com/evand/archive/2005/01/31/363935.aspx

John said...

Hi Glenn, I did eventually get the code to work. It was an error in my .NET code. Here is essentially the same thing in C#:

public void TurnOnFilterJunkEmail(string url)
{
try
{
string xmlHttpString = string.Empty;
xmlHttpString = xmlHttpString + "Cmd=options\n";
xmlHttpString = xmlHttpString + "junkemailstate=1\n";
xmlHttpString = xmlHttpString + "cmd=savejunkemailrule\n";
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);
Byte[] xmlHttpStringByte = System.Text.Encoding.UTF8.GetBytes(xmlHttpString);
httpRequest.PreAuthenticate = true;
httpRequest.Method = "POST";
httpRequest.ContentLength = xmlHttpString.Length;
httpRequest.ContentType = "application/x-www-UTF8-encoded";
httpRequest.Accept = "*.*";
httpRequest.Headers.Add("Accept-Language","en-us");
httpRequest.Headers.Add("Cache-Control","no-cache");
httpRequest.Credentials = CredentialCache.DefaultCredentials;

Stream outputStream = httpRequest.GetRequestStream();
outputStream.Write(xmlHttpStringByte, 0, xmlHttpStringByte.Length);
outputStream.Close();
HttpWebResponse outputHttpResponse = (HttpWebResponse)httpRequest.GetResponse();

if (outputHttpResponse.StatusCode != HttpStatusCode.OK)
{
Console.WriteLine("Failed at {0}", url);
Console.WriteLine("HttpStatusCode {0}", outputHttpResponse.StatusCode);
return;
}
ModifyOutlookJunkEmailSettings(url);
}
catch (Exception e)
{
Console.WriteLine("Failed at {0}", url);
Console.WriteLine("Message: {0}", e.Message);
}
}

public void ModifyOutlookJunkEmailSettings(string url)
{
ADODB.Connection conn = null;
ADODB.Recordset rs = null;

try
{

conn = new ADODB.ConnectionClass();
ADODB.Record updateRecord = new ADODB.RecordClass();
rs = new ADODB.RecordsetClass();
url = url + "/inbox/";
string sql = "SELECT \"DAV:href\", \"DAV:uid\", \"DAV:contentclass\" ";
sql = sql + "FROM scope('shallow traversal of \"" + url + "\"') ";
sql = sql + "WHERE \"http://schemas.microsoft.com/mapi/proptag/0x65EB001E\" = 'JunkEmailRule' ";
sql = sql + "AND \"http://schemas.microsoft.com/exchange/outlookmessageclass\" = 'IPM.ExtendedRule.Message'";

conn.Provider = "msdaipp.dso";
conn.Open(url, "", "", -1);

if(conn.State == 1)
{
//Console.WriteLine("Good Connection");
}
else
{
Console.WriteLine("Bad Connection");
}


rs.CursorLocation = ADODB.CursorLocationEnum.adUseClient;
rs.Open(sql, conn, ADODB.CursorTypeEnum.adOpenUnspecified, ADODB.LockTypeEnum.adLockOptimistic, 1);
if (rs.RecordCount != 0)
{
rs.MoveFirst();
while (!rs.EOF)
{
string davHref = (string)rs.Fields["DAV:href"].Value;
updateRecord.Open(davHref,
conn, ADODB.ConnectModeEnum.adModeReadWrite, ADODB.RecordCreateOptionsEnum.adFailIfNotExists,
ADODB.RecordOpenOptionsEnum.adOpenSource, "", "");
updateRecord.Fields["http://schemas.microsoft.com/mapi/proptag/0x61010003"].Value = 3;
updateRecord.Fields["http://schemas.microsoft.com/mapi/proptag/0x61020003"].Value = 0;
updateRecord.Fields.Update();
updateRecord.Close();
rs.MoveNext();
//Console.WriteLine("Successful update!");
}
rs.Close();
}
}
catch (Exception e)
{
Console.WriteLine("Failed at {0}. Failed to modify Outlook junk email settings.", url);
Console.WriteLine("Message: {0}", e.Message);
}
}

AHMED said...

Hello Glen,
Thanks for the script. That really saved my day. Is there any way to run this script for all my AD users or will i have to run this manually for each?

Waiting for your prompt response

Thanks

Glen said...

Hi Ahmed,

Have a read though the other comments this was asked previosly i mentioned a few solutions like using a batch file or creating a ADSI query to set all users

Cheers
Glen

Billy said...

Simple question... can anyone tell me what I'd have to change to just get what it's currently set to? I want to see how much of my environment already have this set, so I can tell if we need to push it on a large scale. If it's just a few, then I'll avoid resetting those who may have purposely set this the other way. Thanks!!

Glen said...

I have a separate script for doing this that basically produces a report of all the setting of all the users on a server have a look at http://gsexdev.blogspot.com/2006/03/displaying-junk-email-setting-of-all.html

Cheers
Glen (ps the mail you tried to send me was blocked by you outgoing mail filter)