Alternatív feladó

Azt hiszem sokaknak okozott már problémát az Exchange Server azon működésmódja, hogy egy adott felhasználóhoz több e-mail címet rendelve nem tudjuk elérni, hogy a felhasználó az alternatív címével tudjon levelet küldeni. Ha csak egyszerűen beírjuk az Outlook From: mezőjébe az alternatív címet akkor egy hibaüzenetet kapunk:
Your message did not reach some or all of the intended recipients.

      Subject:	test
      Sent:	2005. 01. 20.  7:52

The following recipient(s) could not be reached:

      suf@freemail.hu on 2005. 01. 20.  7:52
            You do not have permission to send to this recipient.  For assistance, contact your system administrator.
            MSEXCH:MSExchangeIS:/DC=local:MAIL
Ezt nekem idáig kétféleképpen sikerült megkerülnöm:

1. Felveszünk egy másik mailbox-al rendelkező felhasználót az alternatív címmel. Átirányítjuk az alternatív felhasználó levelezését az eredeti felhasználónkra. Az eredeti felhasználónak Send As jogot adunk az alternatív felhasználóra. A From: mezőbe beírjuk az alternatív felhasználó címét küldésnél.
Ez a megoldással a következő problémák vannak: Kérdéses, hogy az így felvett plusz felhasználó igényel-e plusz Exchange licenszet. A küldésnél be kell írni a From: mezőbe a címet és nem tudunk választani a lehetőségeink közül.

2. Ez a megoldás némi fejlesztést is tartalmaz. Az alapelv az, hogy az Exchange-en áthaladó levél feladója az eredeti marad, majd az alternatív feladót akkor adjuk meg amikor a levél elhagyja a szervert. A levél a szerverről való távozáskor viszonylag egyszeruen módosítható egy SMTP Event Sink-kel, ugyanakkor gondot okoz, hogy ebben az esetben olyan adat alapján kell a levelet módosítanunk amit a felhasználó ad meg. Ennek az adatnak (az alternatív címnek) együtt kell utaznia a levéllel. Legegyszerubb az lenne, hogy a levél SMTP fejlécében elhelyezzük valamilyen X-Valami mezôben az alternatív címet a felhasználói programban. Ezt az Outlookban sajnos nem tudjuk megtenni, mert a MAPI levélben még nincs SMTP fejléc. Ezért azt a megoldást választottam, hogy egy GUID nevű rejtett csatolt fájlban helyezem el az alternatív címet. A megoldás alapvető működéséhez két modul szükséges. Az egyik egy Outlook form ami megnyitáskor az Active Directory-ból felveszi a felhasználó lehetséges címeit és elhelyezi őket egy lenyíló listában. A felhasználó kiválasztja a feladó címet. Küldéskor generálódik egy csatolás aminek a neve egy meghatározott GUID. A másik egy SMTP Event Sink ami figyeli, hogy a levél tartalmaz-e egy, a speciális GUID névvel rendelkező csatolást, ha igen, akkor kiolvassa belőle az alternatív címet, ellenőrzi, hogy az adott felhasználónak van-e ilyen címe, és ha igen, akkor kicseréli az összes feladó mezőben (From, Sender, ReplyTo).

A második megoldáshoz használatához szükséges dolgok:
Az SMTP Event Sink regisztrációja a szokásos módon történik a szükséges parancsokhoz mellékeltem mintát. Az Outlook form egy teljesen más eset. Ahhoz, hogy a felhasználó a megszokott levélküldésnek megfelelő módon tudja használni két dologra van szükség.
1. A form-ot publikálni kell az Outlookban. Ezt több különböző helyre megtehetjük, ezeknek a helyeknek megvannak a maguk előnyei és hátrányai. Erről most nem kívánok bővebben értekezni, akit komolyabban érdekel itt talál egy angol nyelvű cikket a témáról.
2. Tudatnunk kell az Outlook-kal, hogy a saját form-unkat használja küldésnél. Ezt egy registry bejegyzéssel tudjuk megtenni. A bejegyzés utasítja az Outlookot, hogy a levél küldésre az alapértelmezett IPM.Note form helyett, az általunk publikált IPM.Note.Alternate form-ot használja.
Mellékeltem egy pici script-et ami képes a felhasználó Outlookjában a fenti két lépést elvégezni. A script alapvetően Outlook 2003-hoz készült, és a form-ot a felhasználó Beérkezett üzenetek (Inbox) mappájába publikálja. Ha valakinek ez a megoldás nem jó, akkor vagy kézzel kell elvégezni a szükséges lépéseket, vagy módosítani kell a "telepítő" scriptet.

A megoldáshoz tartozik még egy opcionális elem, ami megoldja, hogy a felhasználó Elküldött üzenetek (Sent Items) mappája alatt különböző mappákba szortírozza feladó szerint a leveleket. Ez a kiegészítő elem egy időzítendő JScript formájában készült el és a cikk végén található a dokumentációjával együtt.

Hiányosságok:
Számomra tudottan ennek az "Alternatív Feladó" nevezetű script csomagnak van néhány hiányossága amiket mindenképp figyelembe kell vennie annak aki használni szeretné. Terveim közt szerepel, hogy ezen hiányosságok egy részét a jövőben kijavítsam, ugyanakkor biztos vagyok abban, hogy ezek a javítások nem fogják teljeskörűen megoldani az itt jelzett problémákat:
- Nem működik RPC over HTTP környezetben
- Nem műödik az OWA, az OMA valamint az ActiveSync for Exchange (PDA, SmartPhone) környezetben
- Válasz üzenetként mindíg a felhasználó elsődleges e-mail címét jelöli ki a listán (nem tudja megállapítani, hogy az eredeti levél melyik címre érkezett)
- Az elküldött üzenetben a Display Name mindíg a felhasználó eredeti AD-ban levő neve, tehát nem tudunk különböző neveket rendelni a különböző levélcímekhez
- Az elküldött üzenetben a Sender, a From és a ReplyTo mezőkben cseréljük ki az címet. Az EventSink script futási pontján az SMTP boríték küldő mezője (http://schemas.microsoft.com/cdo/smtpenvelope/senderemailaddress) már csak olvasható, így az SMTP borítékban mindíg az eredeti cím marad. Ez különböző levelező listákon és SPAM szűrőknél gondot okozhat.
Az SMTP Event Sink:
as.js:
<SCRIPT LANGUAGE="JScript">
/* 
 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
 EITHER EXPRESSED OR IMPLIED,  INCLUDING BUT NOT LIMITED TO THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

 This code is free for both personal and commercial use, but you are
 expressly forbidden from selling.

 **************************************************************************

 FILE NAME:
   as.js

 DESCRIPTION:
   This is an SMTP transport event sink used to help the impelentation of the
   alternative sender function for the Exchange Server
   
 COPYRIGHT:
   Copyright (c) Zoltán Gömöri 2004-2006.
   All rights reserved.
   
 NOTES:
   The original version of this source code and the article that
   includes this source code can be found at:
     http://www.gomori.hu

 CREATED:
   2005.01.20
   
 LAST MODIFIED:
   2006.03.06
   
 VERSION:
   v2.0 Build 2
   
TO DO:

 **************************************************************************
*/
// declare "constants"
var cdoRunNextSink = 0;
var cdoSkipRemainingSinks = 1;

var ForReading = 1;
var ForAppending = 8;

function ISMTPOnArrival::OnArrival(Msg, EventStatus)
{
    var i;
    var AttIndex;
    var AttStream;
    var AlternateMail;
    var OrigMail;
    var arrEMail = new Array();
    AttIndex = 0;
    for(i=1;i <= Msg.Attachments.Count;i++)
        if(Msg.Attachments(i).FileName == "{4F729183-BFB2-4cdc-9648-1A6E600BAB9A}.txt")
            AttIndex = i;
    if(AttIndex != 0)
    {
        AttStream = Msg.Attachments(AttIndex).GetDecodedContentStream();
        AlternateMail = AttStream.ReadText();
        AttStream.Close();
        Msg.Attachments.Delete(AttIndex);

        AlternateMail = AlternateMail.match(/<[^<>]*@[^<>]*>/);
        OrigMail = Msg.From.match(/<[^<>]*@[^<>]*>/);
        if(AlternateMail != null && OrigMail != null)
            if(ValidateAS(ExtractMail(OrigMail[0]),ExtractMail(AlternateMail[0])))
            {
                Msg.From = Msg.From.replace(OrigMail[0],AlternateMail[0]);
                Msg.ReplyTo = Msg.ReplyTo.replace(OrigMail[0],AlternateMail[0]);
                Msg.Sender = Msg.Sender.replace(OrigMail[0],AlternateMail[0]);
            }
        Msg.DataSource.Save();
    }
    EventStatus = cdoRunNextSink;
}

/*
 This function Validates against the AD if the Sender has a right to change
 the address to the supplied one
*/
function ValidateAS(OrigSender, AltSender)
{
    var i;
    var MailArr;
    // Get actual AD domain
    rootDSE = GetObject("LDAP://rootDSE");
    ADDN = rootDSE.Get("defaultNamingContext");

    // Open an ADSI OleDB connection to the AD
    objConnection = new ActiveXObject("ADODB.Connection")
    objConnection.Open("Provider=ADsDSOObject;");
    objCommand = new ActiveXObject("ADODB.Command");
    objCommand.ActiveConnection = objConnection;
    // Find the user object
    objCommand.CommandText = "<LDAP://" + ADDN + ">;" +
                             "(&(objectCategory=User)(mail=" + OrigSender + "));" +
                             "proxyAddresses;" + 
                             "subtree";
    objRecordSet = objCommand.Execute();
    // Determine if the user object has a proxyAddress witch equals the
    // AltSender parameter
    if(!objRecordSet.EOF)
    {
        MailArr=objRecordSet.Fields("proxyAddresses").Value.toArray();
        for(i=0;i<MailArr.length;i++)
        {
            if((MailArr[i].substr(0,5).toLowerCase() == "smtp:") && (MailArr[i].substr(5) == AltSender))
                return true;
        }
    }
    return false;
}

function ExtractMail(mailaddress)
{
    return mailaddress.substring(1,mailaddress.length-1);
}

</SCRIPT>
Az SMPT Event Sink regisztrációja:
cscript smtpreg.vbs /add 2 OnArrival AlternateSender CDO.SS_SMTPOnArrivalSink "mail from=*"
cscript smtpreg.vbs /setprop 2 OnArrival AlternateSender Sink ScriptName "c:\Scripts\SMTP\AS\as.js"
Az Outlook form script:
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
' EITHER EXPRESSED OR IMPLIED,  INCLUDING BUT NOT LIMITED TO THE IMPLIED
' WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

' This code is free for both personal and commercial use, but you are
' expressly forbidden from selling.

' **************************************************************************

' FILE NAME:
'  Mail.Alternate.En.oft

' DESCRIPTION:
'   This is an Outlook Form used as part of the impelentation of the
'   alternative sender function for the Exchange Server
   
' COPYRIGHT:
'   Copyright (c) Zoltán Gömöri 2004-2005.
'   All rights reserved.
   
' NOTES:
'   The original version of this source code and the article that
'   includes this source code can be found at:
'     http://www.gomori.hu

' CREATED:
'   2004.06.26
   
' LAST MODIFIED:
'   2005.04.21
   
' VERSION:
'   v1.1 Build 1
   
'TO DO:

' **************************************************************************

Function Item_Open()
    Dim DefMailIndex
    Dim CurrMailIndex
    Const ADS_SCOPE_SUBTREE = 2
    Set myNamespace = Application.GetNameSpace("MAPI")

    Set cbxSender = Item.GetInspector.ModifiedFormpages(1).Controls("cbxSender")

    Set rootDSE = GetObject("LDAP://rootDSE")
    ADDN = rootDSE.Get("defaultNamingContext")

    Set objConnection = CreateObject("ADODB.Connection")
    Set objCommand =   CreateObject("ADODB.Command")
    objConnection.Provider = "ADsDSOObject"
    objConnection.Open "Active Directory Provider"
    Set objCommand.ActiveConnection = objConnection
    objCommand.Properties("Page Size") = 1000
    objCommand.Properties("Timeout") = 30 
    objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 
    objCommand.Properties("Cache Results") = False

    objCommand.CommandText = "Select proxyAddresses, mail from 'LDAP://" & ADDN & "' where legacyExchangeDN='" & myNameSpace.CurrentUser.Address & "'"
    Set objRecordSet = objCommand.Execute
    If Not objRecordSet.EOF Then
        AddrArr = objRecordSet.Fields("proxyAddresses")
        For i = 0 to UBound(AddrArr)
            If UCase(Left(AddrArr(i),5)) = "SMTP:" Then
                cbxSender.AddItem Mid(AddrArr(i),6,Len(AddrArr(i)))
                CurrMailIndex = cbxSender.ListCount - 1
            End If
            If LCase(Mid(AddrArr(i),6,Len(AddrArr(i)))) = LCase(objRecordSet.Fields("mail").Value) Then
                DefMailIndex = CurrMailIndex
            End If
        Next
    End If
    If cbxSender.ListCount > 0 Then
        cbxSender.ListIndex = DefMailIndex
    End If
End Function

Function Item_Send()
    Const olByReference = 4
    Const olByValue = 1
    Const olEmbeddedItem = 5
    Const olOLE = 6

    Set cbxSender = Item.GetInspector.ModifiedFormpages(1).Controls("cbxSender")
    Set FSO = CreateObject("Scripting.FileSystemObject")
    Set WshShell = CreateObject("WScript.Shell")

    Set AttachFile = FSO.CreateTextFile(WshShell.ExpandEnvironmentStrings("%Temp%") & "\{4F729183-BFB2-4cdc-9648-1A6E600BAB9A}.txt")
    AttachFile.WriteLine("<" & cbxSender.Text & ">")
    AttachFile.Close

    Item.Attachments.Add WshShell.ExpandEnvironmentStrings("%Temp%") & "\{4F729183-BFB2-4cdc-9648-1A6E600BAB9A}.txt", olByValue, 0, "{4F729183-BFB2-4cdc-9648-1A6E600BAB9A}.txt"
End Function
    
Az ezzel a scriptel és a megfelelő design-al ellátott Outlook form-ot Outlook 2003 Angol és Outlook 2003 Magyar verzióban mellékeltem a letölthető csomagban.
Annak aki korábbi verziójú Outlook-ot használ, létre kell hoznia a form-ot a minta form-ok és a fenti script alapján, valamint módosítania kell a form telepítőt, mert a registry bejegyzések verziófüggőek.

A telepítő script:
as_publish_en.js:
/* 
 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
 EITHER EXPRESSED OR IMPLIED,  INCLUDING BUT NOT LIMITED TO THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

 This code is free for both personal and commercial use, but you are
 expressly forbidden from selling.

 **************************************************************************

 FILE NAME:
   as_publish.js

 DESCRIPTION:
   This is the setup script of the Alternate Sender Outlook Form
   
 COPYRIGHT:
   Copyright (c) Zoltán Gömöri 2005.
   All rights reserved.
   
 NOTES:
   The original version of this source code and the article that
   includes this source code can be found at:
     http://www.gomori.hu

 CREATED:
   2005.04.21
   
 LAST MODIFIED:
   2005.04.22
   
 VERSION:
   v1.0 Build 1
   
TO DO:

 **************************************************************************
*/

// Script parameters
var OftFileName = "IPM.Note.Alternate.En.oft";
var FormDisplayName = "Alternate Sender";
var FormName = "Alternate";
var OutlookVersion = "11.0";

// declare "constants"
var olDefaultRegistry = 0;
var olFolderRegistry = 3;
var olOrganizationRegistry = 4;
var olPersonalRegistry = 2;

var olFolderInbox = 6;

// declare variables
var OftFile;
var OlApp;
var OlItem;
var OlForm;
var OlFolder;
var OlNamespace;
var ScriptFolder;
var WshShell;
var RegKey;

// Get the script's path
ScriptFolder = WScript.ScriptFullName.substring(0, WScript.ScriptFullName.lastIndexOf("\\") + 1);

// Set the name of the installable Outlook Form File
OftFile = ScriptFolder + OftFileName;

// Publish the form
OlApp = new ActiveXObject("Outlook.Application");
OlNamespace = OlApp.GetNameSpace("MAPI");
OlFolder = OlNamespace.GetDefaultFolder(olFolderInbox);
OlItem = OlApp.CreateItemFromTemplate(OftFile);
OlForm = OlItem.FormDescription;
OlForm.Name = FormName;
OlForm.DisplayName = FormDisplayName;
OlForm.PublishForm(olFolderRegistry,OlFolder);

// Register the form as the default IPM.Note
WshShell = WScript.CreateObject("WScript.Shell");
RegKey = "HKCU\\Software\\Microsoft\\Office\\" + OutlookVersion + "\\Outlook\\Custom Forms\\";
WshShell.RegWrite(RegKey, "", "REG_SZ");
WshShell.RegWrite(RegKey + "Compose\\", "", "REG_SZ");
WshShell.RegWrite(RegKey + "Read\\", "", "REG_SZ");
WshShell.RegWrite(RegKey + "Compose\\IPM.Note", "IPM.Note." + FormName, "REG_SZ");
Itt következik az ígért opcionális elem. Feladata, hogy Eredetileg Exchange Store Sink formájában készült el, de sajnos ebben a formában nem működik, miután a Sent Items mappára telepített Store Sink nem hajtódik végre a MAPI kliens által a levél küldéskor oda berakott levelekre (http://support.microsoft.com/?id=297274).
Használatához a következők szükségesek:
- abba a mappába ahol a script található el kell helyezni egy a script nevével azonos ini fájlt (SentCategorizer.ini). Ennek a fájlnak a tartalma soronként azon felhasználók Sent Items mappájának a címe akikre le akarjuk futtatni a scriptet(pl. http://mailserver.domain.hu/exchange/user@domain.hu/Sent Items )
- Annak a felhasználónak akinek a nevében futtatni akarjuk a scriptet megfelelő jogokat kell adni a felhasználó levelesládájához
- Megfelelően időzíteni kell, hogy az elvárt intervallumonként lefusson

SentCategorizer.js
/* 
 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
 EITHER EXPRESSED OR IMPLIED,  INCLUDING BUT NOT LIMITED TO THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

 This code is free for both personal and commercial use, but you are
 expressly forbidden from selling.

 **************************************************************************

 FILE NAME:
   SentCategorizer.js

 DESCRIPTION:
   This is the Sent Items Categorizer script. It is an optional component for the
   alternative sender solution for the Exchange Server.
   It can be used to put the messages sent by various alternative e-mail addresses
   to folders coresponding to the sender e-mail.
   
 COPYRIGHT:
   Copyright (c) Zoltán Gömöri. 2005.
   All rights reserved.
   
 NOTES:
   The original version of this source code and the article that
   includes this source code can be found at:
     http://www.gomori.hu

 CREATED:
   2005.04.25
   
 LAST MODIFIED:
   2005.05.04
   
 VERSION:
   v1.0 Build 1
   
TO DO:

 **************************************************************************
*/
// declare "constants"

// ADO constants
var adModeReadWrite = 3;
var adCreateCollection = 8192;

// Error constants
var ERR_PERMISSION_DENIED = -2147217911;
var ERR_ACCESS_DENIED = -2147024891;
var ERR_ALREADY_EXISTS = -2147217768;

// MAPI constants
var MAPI_NS_PROPTAG = "http://schemas.microsoft.com/mapi/proptag/";
var PR_SENDER_NAME = "x0C1A001E";
var PR_SENDER_ADDRTYPE = "x0C1E001E";
var PR_SENDER_EMAIL_ADDRESS = "x0C1F001E";

// Intialize components
ExStore_Init();

// Main Code

var FSO;
var f;
var StorePath;
var Store;
// Create a Store Object
Store = new ExStore();
// Add to the Mail Event an Eventhandler. This will move the mail to
// the target folder
Store.Event_Mail = MoveSentItem;
FSO = new ActiveXObject("Scripting.FileSystemObject");
// Open the list of the Sent Items folders
f = FSO.OpenTextFile(ScriptPath() + "\\SentCategorizer.ini");
// Iterate thru the Sent Items folders
while(!f.AtEndOfStream)
{
    StorePath = f.ReadLine();
    if(StorePath != "") Store.ProcessMailFolder(StorePath); // Process the Sent Items folders
}
f.Close();

/*
    MoveSentItem(Url)
    This is an event handler function called by the Store object on each message located in the
    Sent Items folder.
    This function:
    - gets the alternate e-mail address from the message
    - creates a folder named after the alternate e-mail address under the Sent Items if it doesn't exists
    - removes the attachment containing the alternates e-mail address
    - changes the from address of the e-mail to the alternate address
    - saves the mail to the folder named after the alternate e-mail
    - deletes the original e-mail
    
    Parameters:
        Url - Url of the message to process
    Returns:
        Nothing
*/
function MoveSentItem(Url)
{
    var Msg;            // The message object (CDO.Message)
    var Sender;         // The real sender of the mail
    var AttStream;      // The special GUID named attachment's stream
    var AlternateMail;  // The alternate mail string
    var ParentFolder;   // The original folder of the mail (Sent Items)
    var FolderName;     // The name of the target folder (it is equals to the alternate sender)
    var i,j;
    j = 0;
    Msg = new ActiveXObject("CDO.Message"); // Initialize the Message object
    Msg.DataSource.Open(Url);       // Bind to the Message
    ParentFolder = Msg.Fields("DAV:parentname").Value;  // Get the Url of the folder containing the message
    Sender = Msg.Fields("urn:schemas:httpmail:fromemail").Value;    // Set the initial value of the sender
    // Iterate thru the attachments
    for(i=1;i<=Msg.Attachments.Count;i++)
    {
        if(Msg.Attachments(i).FileName == "{4F729183-BFB2-4cdc-9648-1A6E600BAB9A}.txt")
        {   // If we found the attachment contains the alternate sender
            AttStream = Msg.Attachments(i).GetDecodedContentStream();
            AlternateMail = AttStream.ReadText();   // read the mail address from this attachment
            AttStream.Close();
            AlternateMail = AlternateMail.match(/<[^<>]*@[^<>]*>/); // extract the mail address
            if(AlternateMail != null)
            {   // if we found a correctly formated mail address
                Sender = AlternateMail[0];  // replace the original sender with the real one
                j = i;
            }
        }
        if(j!=0) Msg.Attachments.Delete(j); // remove the special GUID named attachment
    }

    FolderName = ExtractMail(Sender);   // Generate the target folder name from the mail address
    CreateMailFolder(ParentFolder, FolderName); // and create it
    if(Msg.Fields("urn:schemas:mailheader:from").Value.match(Sender) == null)
    {   // if the real sender not equals to the original one
        // replace the coresponding mail fields
        Msg.Fields("urn:schemas:mailheader:from").Value = Msg.Fields("urn:schemas:mailheader:from").Value.replace(/<[^<>]*@[^<>]*>/, Sender);
        Msg.Fields("urn:schemas:httpmail:from").Value = Msg.Fields("urn:schemas:httpmail:from").Value.replace(/<[^<>]*@[^<>]*>/, Sender);
        Msg.Fields(MAPI_NS_PROPTAG + PR_SENDER_ADDRTYPE).Value = "SMTP";
        Msg.Fields(MAPI_NS_PROPTAG + PR_SENDER_EMAIL_ADDRESS).Value = FolderName;
        Msg.Fields.Update();    // Write the fields into the memory stream
    }
    Msg.DataSource.SaveToContainer(ParentFolder + "/" + FolderName); // Save the message to the target folder
    DeleteMail(Url);    // Delete the original mail
}

/*
    ScriptPath()
    This function returns the path of the running script's in the filesystem
    Parameters:
        Nothing
    Returns:
        The path string
*/
function ScriptPath()
{
    var retvalue;
    retvalue = WScript.ScriptFullName;
    return retvalue.substring(0,retvalue.lastIndexOf("\\"));
}

/*
    ExtractMail(mailaddress)
    This function extracts the e-mail address from the <name@domain> format
    Parameters:
        mailaddress - E-mail address in <name@domain> format
    Returns:
        E-mail address in name@domain format
*/
function ExtractMail(mailaddress)
{
    return mailaddress.substring(1,mailaddress.length-1);
}

/*
    CreateMailFolder(Url, Name)
    This function creates a new mail folder in the Exchange Store if it doesn't exists
    Parameters:
        Url - Url of the parent folder
        Name    - Name of the folder
    Returns:
        0 - If the operation successfull
        ERR_ACCESS_DENIED - If you don't have right to create folder
        ERR_ALREADY_EXISTS - If the folder already exists
*/
function CreateMailFolder(Url, Name)
{
    var ExFolder;
    var ExConn;
    var retvalue;
    retvalue = 0;
    ExConn = new ActiveXObject("ADODB.Connection");
    ExFolder = new ActiveXObject("ADODB.Record");
    ExConn.Provider = "ExOLEDB.DataSource";
    ExConn.Open(Url);
    try
    {
        ExFolder.Open(Url + "/" + Name, ExConn, adModeReadWrite, adCreateCollection);
        ExFolder.Fields("DAV:contentclass") = "urn:content-classes:mailfolder";
        ExFolder.Fields("http://schemas.microsoft.com/exchange/outlookfolderclass") = "IPF.Note";
        ExFolder.Fields.Update();
        ExFolder.Close();
    }
    catch(e)
    {
        retvalue = e.number;
        if(e.number != ERR_ALREADY_EXISTS && e.number != ERR_ACCESS_DENIED)
            throw(e); // Rethrow the error
    }
    ExConn.Close();
    return retvalue;
}

/*
    DeleteMail(Url)
    This function deletes a mail from the Exchange Store
    Parameters:
        Url - Url of the message
    Returns:
        Nothing
*/
function DeleteMail(Url)
{
    var ExConn;
    var ExRec;
    ExConn = new ActiveXObject("ADODB.Connection");
    ExRec = new ActiveXObject("ADODB.Record");
    ExConn.Provider = "ExOLEDB.DataSource";
    ExConn.Open(Url);
    ExRec.Open(Url,ExConn,adModeReadWrite);
    ExRec.DeleteRecord(Url);
}

/*
    ExStore
    This object can be used to navigate thru the Exchange Store
    Properties:
    Methodes:
        ProcessFolder - Iterate thru an Exchange Folder
        ProcessMailFolder - Iterate thru an Exchange Mail Folder
    Event Handlers:
        Event_Mail - Called by the ProcessFolder and the ProcessMailFolder on every containing item
*/
// Initialization
function ExStore_Init()
{
    ExStore.prototype.ProcessMailFolder = _ExStore_ProcessMailFolder;
    ExStore.prototype.ProcessFolder = _ExStore_ProcessFolder;
}
// Constructor
function ExStore()
{
}
/*
    ProcessMailFolder(FolderUrl)
    Iterate thru an Exchange Mail Folder
    Parameters:
        FolderUrl - Url of the Mail folder
    Returns:
        0 if succed, error code if failed
*/
function _ExStore_ProcessMailFolder(FolderUrl)
{
    return this.ProcessFolder(FolderUrl,"\"DAV:ishidden\" = false AND \"DAV:contentclass\" = 'urn:content-classes:message'");
}
/*
    ProcessFolder(FolderUrl, Filter)
    Iterate thru an Exchange Folder
    Parameters:
        FolderUrl - Url of the Mail folder
        Filter - SQL like filter expression wtich filters the type of the folder
    Returns:
        0 if succed, error code if failed
*/
*/
function _ExStore_ProcessFolder(FolderUrl, Filter)
{
    var SelectStr;
    var ExConn;
    var retvalue;
    retvalue = 0;

    if(this.Event_Mail != null)
    {
        SelectStr = "SELECT \"DAV:href\" FROM SCOPE('SHALLOW TRAVERSAL OF \"" + encodeURI(FolderUrl) + "\"') WHERE " + Filter;
        ExConn = new ActiveXObject("ADODB.Connection");
        ExRecords = new ActiveXObject("ADODB.RecordSet");
        ExConn.Provider = "ExOLEDB.DataSource";
        ExConn.Open(FolderUrl);
        try
        {
            ExRecords.Open(SelectStr, ExConn);
            for(;!ExRecords.EOF;ExRecords.MoveNext())
                this.Event_Mail(ExRecords.Fields("DAV:href").Value);
        }
        catch(e)
        {
            retvalue = e.number;            
            if(e.number != ERR_PERMISSION_DENIED)
                throw e;        // Rethrow the error
        }
        ExConn.Close();
    }
    return retvalue;
}