' // ========================================================================
' // Upgrade 5.2 to 5.3.vbs
' // ------------------------------------------------------------------------
' // This script upgrades an existing 5.2 configuration to 5.3.
' // 
' // This script is part of the upgrade instructions found here: 
' // http://www.activexperts.com/mmserver/ ... 
' // 
' // Make sure all of the steps in the upgrade instructions that lead up to 
' // the execution of this script have been followed before executing this 
' // script.
' // ========================================================================
Option Explicit

Const OLD_VERSION = "5.2"
Const NEW_VERSION = "5.3.1"     ' TODO: 5.3.1 !!

Const DEFAULT_OLDPATH = "C:\Program Files\ActiveXperts\SMS Messaging Server 52\"
Const DEFAULT_NEWPATH = "C:\Program Files\ActiveXperts\SMS Messaging Server\"
Const CONFIGURATION_PATH = "\Cfg\Configuration.mdb"
Const CONFIGURATION_INTERIM = "\Cfg\Configuration_.mdb"
Const CONFIGURATION_TEMPL = "\Sys\Tpl\Cfg\Configuration.mdb"
Const PROJECTS_PATH = "\Projects"
Const PROJECTS_SCRIPTS_PATH = "\Program\Projects"

Const PASSWORD_PLACEHOLDER = "<%PASSWORD%>"
Const ROOTDIR_PLACEHOLDER = "<AXMMROOT>"

Const CONTENTTYPE_URLFORMENCODED = 0
Const REQDLR_MBLOXBIT = 2
Const DLRFORMAT_DECIMAL = 1
Const DLRFORMAT_TEXT = 0

Const DATABASETYPE_ACCESS = 0
Const DATABASETYPE_MSSQL = 1
Const DATABASETYPE_MYSQL = 2

Dim g_strOldPath, g_strNewPath
Dim g_strOldConfigDb, g_strOldMessageDb, g_strOldArchiveDb, g_strOldLoggingDb
Dim g_strNewConfigDb, g_strNewMessageDb, g_strNewArchiveDb, g_strNewLoggingDb
Dim g_bMigrateLog, g_bMigrateArchive, g_bMigrateMessages

' // ========================================================================
' // Upgrade
' // ========================================================================
Welcome
TestService

InputPaths

TestOldVersion g_strOldConfigDb, OLD_VERSION, "config database"
TestVersion g_strNewConfigDb, NEW_VERSION, "current config database"

ReadDatabaseConnectionStrings g_strOldConfigDb, g_strOldMessageDb, g_strOldArchiveDb, g_strOldLoggingDb
ReadDatabaseConnectionStrings g_strNewConfigDb, g_strNewMessageDb, g_strNewArchiveDb, g_strNewLoggingDb

VerifyConnectionStrings g_strOldMessageDb, g_strNewMessageDb, g_bMigrateMessages, "Message database"
VerifyConnectionStrings g_strOldArchiveDb, g_strNewArchiveDb, g_bMigrateArchive, "Archive database"
VerifyConnectionStrings g_strOldLoggingDb, g_strNewLoggingDb, g_bMigrateLog, "Log database"

If g_bMigrateMessages Then
  TestOldVersion g_strOldMessageDb, OLD_VERSION, "message database"
  TestVersion g_strNewMessageDb, NEW_VERSION, "current message database"
End If

If g_bMigrateArchive Then
  TestOldVersion g_strOldArchiveDb, OLD_VERSION, "archive database"
  TestVersion g_strNewArchiveDb, NEW_VERSION, "current archive database"
End If

If g_bMigrateLog Then
  TestOldVersion g_strOldLoggingDb, OLD_VERSION, "logging database"
  TestVersion g_strNewLoggingDb, NEW_VERSION, "current logging database"
End If

PrepareConfiguration

MigrateConfiguration
If g_bMigrateMessages Then
  MigrateMessages
End If
If g_bMigrateArchive Then
  MigrateArchiving
End If
If g_bMigrateLog Then
  MigrateLogging
End If

' Copy projects / verify triggers may not be neccissary at all when there are no triggers in the old database ..
If MsgBox("Do you want to copy the project directory from the old SMS Messaging Server location ('" & _
  g_strOldPath & "') to the new location ('" & g_strNewPath & "') ?" & vbCrlf & vbCrlf & _
  "A backup will be created of the existing projects.", vbYesNo, "Copy projects") = vbYes Then
  CopyProjects  
End If

If MsgBox("Do you want check the paths to the project trigger scripts ?", vbYesNo, "Check trigger paths") = vbYes Then
  VerifyTriggers
End If

CommitConfiguration

Conclude

' // ========================================================================
' // Show welcome message
' // ========================================================================
Sub Welcome
  WScript.Echo "This script upgrades an existing " & OLD_VERSION & " configuration to " & NEW_VERSION & "." & vbCrlf & vbCrlf & _
    "This script is part of the upgrade instructions found here: http://www.activexperts.com/mmserver/ ... " & vbCrlf & vbCrlf & _
    "Make sure all of the steps in the upgrade instructions that lead up to the execution of this script have been followed before executing this " & _
    "script." & vbCrlf & vbCrlf & _
    "Please make sure the SMS Messaging Server service is shutdown before continuing" & vbCrlf    
End Sub

' // ========================================================================
' // Test if the service is running
' // ------------------------------------------------------------------------
' // If the service is running, display message and exit
' // ========================================================================
Sub TestService
  Dim strServiceName, objWMIService, colListOfServices, objService
  
  strServiceName = "AxMmSvc"
  Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
  Set colListOfServices = objWMIService.ExecQuery("Select * from Win32_Service Where Name ='" & strServiceName & "'")
  For Each objService in colListOfServices
      If objService.Started Then
        WScript.Echo "Please shutdown the SMS Messaging Server service and run this script again."
        WScript.Quit
        ' objService.StopService()
      End If      
  Next
End Sub

' // ========================================================================
' // Get the old and new installation path
' // ========================================================================
Sub InputPaths
  g_strOldPath = InputBox("Enter the path for the old SMS Messaging Server " & OLD_VERSION & " installation." & _
    vbCrlf & vbCrlf & "Press cancel to exit.", "Old SMS Messaging Server " & OLD_VERSION & " Path", DEFAULT_OLDPATH)
  If g_strOldPath = "" Then
    WScript.Echo "Upgrade cancelled."
    WScript.Quit
  End If

  g_strNewPath = InputBox("Enter the path for the new SMS Messaging Server " & NEW_VERSION & " installation." & _
    vbCrlf & vbCrlf & "Press cancel to exit.", "New SMS Messaging Server " & NEW_VERSION & " Path", DEFAULT_NEWPATH)
  If g_strNewPath = "" Then
    WScript.Echo "Upgrade cancelled."
    WScript.Quit
  End If
 
  g_strOldConfigDb = "Driver={Microsoft Access Driver (*.mdb)}; DBQ=" & _
    g_strOldPath & CONFIGURATION_PATH & "; uid=; pwd=;"
  g_strNewConfigDb = "Driver={Microsoft Access Driver (*.mdb)}; DBQ=" & _
    g_strNewPath & CONFIGURATION_PATH & "; uid=; pwd=;"
End Sub

' // ========================================================================
' // Get the Message, Archive and Loggin connection strings from the database
' // ========================================================================
Sub ReadDatabaseConnectionStrings(strConfigDb, strMessageDb, strArchiveDb, strLoggingDb)
  Dim objConn
  Dim strQ, objRs
 
  Set objConn = CreateObject("ADODB.Connection")
    
  objConn.Open strConfigDb
  
  strQ = "SELECT LogDbConnectionString, MsgDbConnectionString, ArchiveDbConnectionString FROM Options"
  Set objRs = objConn.Execute(strQ)
  
  strMessageDb = objRs("MsgDbConnectionString")
  strArchiveDb = objRs("ArchiveDbConnectionString")
  strLoggingDb = objRs("LogDbConnectionString")
  
  objConn.Close
End Sub

' // ========================================================================
' // Have the user verify the old and new connection string for a database
' // ------------------------------------------------------------------------
' // Replace the placeholders that are in the connection string and notify
' // the user about the <USERNAME> and <PASSWORD> placeholders when 
' // applicable.
' // ========================================================================
Sub VerifyConnectionStrings(strOldDb, strNewDb, bMigrate, strDatabaseName)
  Dim strMessage
    
  strNewDb = Replace(strNewDb, ROOTDIR_PLACEHOLDER, g_strNewPath)
  strOldDb = Replace(strOldDb, ROOTDIR_PLACEHOLDER, g_strOldPath)
  
  strMessage = "Please check the connection string for the old (" & OLD_VERSION & ") " & strDatabaseName & "."
  If InStr(strOldDb, PASSWORD_PLACEHOLDER) <> 0 Then
    strMessage = strMessage & vbCrlf & vbCrlf & "Replace <%PASSWORD%> placeholder with your actual password !"
  End If
  strMessage = strMessage & vbCrlf & vbCrlf & "Press cancel to skip migrating the " & strDatabaseName & " database."
  
  strOldDb = InputBox(strMessage, "Old " & strDatabaseName & " " & OLD_VERSION & " Path", strOldDb)    
  If strOldDb = "" Then
    bMigrate = False    
  Else
    bMigrate = True
  End If
  
  strMessage = "Please check the connection string for the new (" & NEW_VERSION & ") " & strDatabaseName & "."
  If InStr(strNewDb, PASSWORD_PLACEHOLDER) <> 0 Then
    strMessage = strMessage & vbCrlf & vbCrlf & "Replace the <%PASSWORD%> placeholder with your actual password !"
  End If
  strMessage = strMessage & vbCrlf & vbCrlf & "Press cancel to skip migrating the " & strDatabaseName & " database."

  If bMigrate Then
    strNewDb = InputBox(strMessage, "New " & strDatabaseName & " " & NEW_VERSION & " Path", strNewDb)
    If strNewDb = "" Then
      bMigrate = False    
    End If  
  End If
End Sub

' // ========================================================================
' // Test the database version in an old database
' // ------------------------------------------------------------------------
' // Abort if the database version does not match.
' // ========================================================================
Sub TestOldVersion(strDb, strVersion, strDbName)
  Dim objConn
  Dim strQ, objRs
 
  Set objConn = CreateObject("ADODB.Connection")
  
  objConn.Open strDb

  If InStr(strDb, "SQL Server") > 0 Or InStr(strDb, "MySQL") > 0 Then
    ' // The 5.2 versions did not have a metasettings table in the message, archive or
    ' // log database when running on MSSQL or MySQL, just the MSAccess databases.
    objConn.Close
    Exit Sub
  End If
 
  strQ = "SELECT * FROM MetaSettings WHERE property = 'version'"
  
  On Error Resume Next
  Set objRs = objConn.Execute(strQ)
  If Err.Number <> 0 Then
    WScript.Echo "The " & strDbName & " is of an unknown version."
    WScript.Quit
  End If
  On Error GoTo 0  
  
  If objRs.Eof Or objRs("_value") <> strVersion Then
    WScript.Echo "The " & strDbName & " is of an unexpected version: " & objRs("_value") & ". Aborting.."
    WScript.Quit
  End If
  
  objConn.Close
End Sub

' // ========================================================================
' // Test the database version in the database
' // ------------------------------------------------------------------------
' // Abort if the database version does not match.
' // ========================================================================
Sub TestVersion(strDb, strVersion, strDbName)
  Dim objConn
  Dim strQ, objRs
 
  Set objConn = CreateObject("ADODB.Connection")
    
  objConn.Open strDb

  strQ = "SELECT * FROM MetaSettings WHERE PropertyName = 'dbversion'"
   
  On Error Resume Next
  Set objRs = objConn.Execute(strQ)
  If Err.Number <> 0 Then
    WScript.Echo "The " & strDbName & " is of an unknown version."
    WScript.Quit
  End If
  On Error GoTo 0
  
  If objRs.Eof Or objRs("PropertyValue") <> strVersion Then
    WScript.Echo "The " & strDbName & " is of an unexpected version: " & objRs("PropertyValue") & ". Aborting.."
    WScript.Quit
  End If  
  
  objConn.Close
End Sub

' // ========================================================================
' // Prepare configuration
' // ------------------------------------------------------------------------
' // Copy the template database to an interim file which is going to be used
' // during this script. 
' //
' // Update the connection strings accordingly so all function will open the
' // right configuration
' // ========================================================================
Sub PrepareConfiguration
  Dim objFs  

  Set objFs = CreateObject("Scripting.FileSystemObject")  
  
  objFs.CopyFile g_strNewPath & CONFIGURATION_TEMPL, g_strNewPath & CONFIGURATION_INTERIM, true
  
  g_strOldConfigDb = "Driver={Microsoft Access Driver (*.mdb)}; DBQ=" & _
    g_strOldPath & CONFIGURATION_PATH & "; uid=; pwd=;"  
  g_strNewConfigDb = "Driver={Microsoft Access Driver (*.mdb)}; DBQ=" & _
    g_strNewPath & CONFIGURATION_INTERIM & "; uid=; pwd=;"  
End Sub

' // ========================================================================
' // Migrate the configuration database
' // ========================================================================
Sub MigrateConfiguration
  Dim objConnOld, objConnNew
  Dim strQ, objRsOld, objRsNew
 
  Set objConnOld = CreateObject("ADODB.Connection")
  Set objConnNew = CreateObject("ADODB.Connection")
  
  objConnOld.Open g_strOldConfigDb
  objConnNew.Open g_strNewConfigDb
  
  ' // Copy Gsm channel records
  strQ = "SELECT * FROM Channels_GsmModem"
  Set objRsOld = objConnOld.Execute(strQ)
  While Not objRsOld.Eof

    Set objRsNew = CreateObject("ADODB.Recordset")
    objRsNew.Open strQ, objConnNew, 0, 3
    objRsNew.AddNew
    
    CopyIdenticalFields objRsNew, objRsOld, False
    
    objRsNew("RequestDeliveryReport") = objRsOld("RequestAckAfterSend")     ' // Renamed
    ' // AckTimeOut has been removed. This is now HKEY_LOCAL_MACHINE\Software\ActiveXperts\SMS Messaging Server\AdvancedSettings\SmsDeliveryRptTimeoutSecs
  
    objRsNew.Update
    objRsNew.Close
  
    objRsOld.MoveNext
  Wend

  ' // Copy Http channel records
  strQ = "SELECT * FROM Channels_Http"
  Set objRsOld = objConnOld.Execute(strQ)
  While Not objRsOld.Eof

    Set objRsNew = CreateObject("ADODB.Recordset")
    objRsNew.Open strQ, objConnNew, 0, 3
    objRsNew.AddNew
    
    CopyIdenticalFields objRsNew, objRsOld, False
    
    objRsNew("FromAddress") = objRsOld("SubscriberID")    ' // Renamed
    objRsNew("ContentType") = CONTENTTYPE_URLFORMENCODED  ' // New field; Form/Url Encoded was always assumed 
  
    objRsNew.Update
    objRsNew.Close
  
    objRsOld.MoveNext
  Wend
       
  ' // Copy Smpp channel records
  strQ = "SELECT * FROM Channels_Smpp"
  Set objRsOld = objConnOld.Execute(strQ)
  While Not objRsOld.Eof

    Set objRsNew = CreateObject("ADODB.Recordset")
    objRsNew.Open strQ, objConnNew, 0, 3
    objRsNew.AddNew
    
    CopyIdenticalFields objRsNew, objRsOld, False
    
    objRsNew("FromAddress") = objRsOld("SubscriberID")           ' // Renamed
    objRsNew("EnquireInterval") = objRsOld("ServerKeepAlive")    ' // Renamed
    objRsNew("AddressTON") = objRsOld("SystemSourceTON")         ' // Used to be combined Address / From
    objRsNew("AddressNPI") = objRsOld("SystemSourceNPI")         ' // Used to be combined Address / From
    objRsNew("FromTON") = objRsOld("SystemSourceTON")            ' // Used to be combined Address / From
    objRsNew("FromNPI") = objRsOld("SystemSourceNPI")            ' // Used to be combined Address / From
    objRsNew("ToTON") = objRsOld("SystemDestinationTON")         ' // Renamed
    objRsNew("ToNPI") = objRsOld("SystemDestinationNPI")         ' // Renamed
    objRsNew("ServiceType") = objRsOld("SystemServiceType")      ' // Renamed

    ' // The old 'RequestDeliveryReport' value also contained the mBlox compatibility bit
    If objRsOld("RequestDeliveryReport").Value And REQDLR_MBLOXBIT Then
      objRsNew("DeliveryReportFormat").Value = DLRFORMAT_DECIMAL
    Else 
      objRsNew("DeliveryReportFormat").Value = DLRFORMAT_TEXT
    End If
      
    ' // ServerTimeout has been removed, this is now: HKEY_LOCAL_MACHINE\Software\ActiveXperts\SMS Messaging Server\AdvancedSettings\
    ' //   - SmppConnectTimeoutSecs
    ' //   - SmppBindTimeoutSecs
    ' //   - SmppCommandTimeoutSecs
    ' // SystemThroughput has been removed. This should become 'MaxOutPendingPdus'; Now set to '10' TODO: !!
    ' // MultipartLength has been removed. The maximum part length is now calculated automatically
    ' // DeliveryReportTeimout has been removed. This is now HKEY_LOCAL_MACHINE\Software\ActiveXperts\SMS Messaging Server\AdvancedSettings\SmsDeliveryRptTimeoutSecs
    
    objRsNew.Update
    objRsNew.Close
  
    objRsOld.MoveNext
  Wend  

  ' // Pop3 and Smtp channels are unchanged...
  CopyIdenticalTable "Channels_Pop3", objConnNew, objConnOld, False
  CopyIdenticalTable "Channels_Smtp", objConnNew, objConnOld, False
 
  ' // Copy Blocking rules
  strQ = "SELECT * FROM Blocking"
  Set objRsOld = objConnOld.Execute(strQ)
  While Not objRsOld.Eof

    Set objRsNew = CreateObject("ADODB.Recordset")
    objRsNew.Open strQ, objConnNew, 0, 3
    objRsNew.AddNew
    
    CopyIdenticalFields objRsNew, objRsOld, True
    
    ' // TODO: Group TO, BCC, CC fields
 
    objRsNew.Update
    objRsNew.Close
  
    objRsOld.MoveNext
  Wend  
  
  ' // Copy Routing rules  
  strQ = "SELECT * FROM Routing"
  Set objRsOld = objConnOld.Execute(strQ)
  While Not objRsOld.Eof

    Set objRsNew = CreateObject("ADODB.Recordset")
    objRsNew.Open strQ, objConnNew, 0, 3
    objRsNew.AddNew
    
    CopyIdenticalFields objRsNew, objRsOld, True

    ' // TODO: Group TO, BCC, CC fields
 
    objRsNew.Update
    objRsNew.Close
  
    objRsOld.MoveNext
  Wend    
      
  CopyIdenticalTable "Triggers", objConnNew, objConnOld, True
    
  ' // The options record already exists
  strQ = "SELECT * FROM Options"
  Set objRsOld = objConnOld.Execute(strQ)
  Set objRsNew = CreateObject("ADODB.Recordset")
  objRsNew.Open strQ, objConnNew, 0, 3
  CopyIdenticalFields objRsNew, objRsOld, True
  objRsNew.Update
  
  objConnOld.Close
  objConnNew.Close
  
End Sub

' // ========================================================================
' // Migrate the messages database
' // ========================================================================
Sub MigrateMessages
  Dim objConnOld, objConnNew
  Dim strQ, objRsOld, objRsNew

  Set objConnOld = CreateObject("ADODB.Connection")
  Set objRsOld = CreateObject("ADODB.RecordSet")

  Set objConnNew = CreateObject("ADODB.Connection")
  Set objRsNew = CreateObject("ADODB.RecordSet")

  objConnOld.Open g_strOldMessageDb
  objConnNew.Open g_strNewMessageDb
 
  strQ = "SELECT * FROM Messages"
  objRsOld.Open strQ, objConnOld, 0, 1, 1                         ' // Open ReadOnly

  While Not objRsOld.EOF
    objRsNew.Open strQ, objConnNew, 0, 3, 1
    objRsNew.AddNew
    
    ' Changed fields
    objRsNew("TLVs") = objRsOld("TLVs")                           ' // Type changed ntext -> nvarchar(max)
    objRsNew("ToAddress") = objRsOld("ToAddress")                 ' // Type changed ntext -> nvarchar(max)
    objRsNew("Header") = objRsOld("Header")                       ' // Type changed ntext -> nvarchar(max)
    objRsNew("Body") = objRsOld("Body")                           ' // Type changed ntext -> nvarchar(max)
    objRsNew("Trace") = objRsOld("Trace")                         ' // Type changed ntext -> nvarchar(max)
    objRsNew("MessageReference") = objRsOld("MessageReference")   ' // Type changed varchar(50) -> nvarchar(max)
    objRsNew("BillingId") = objRsOld("BillingId")                 ' // Type changed varchar(255) -> nvarchar(255)
    objRsNew("FromAddress") = objRsOld("FromAddress")             ' // Type changed varchar(255) -> nvarchar(255)
    objRsNew("Subject") = objRsOld("Subject")                     ' // Type changed varchar(255) -> nvarchar(255)
    objRsNew("CustomField2") = objRsOld("CustomField2")           ' // Type changed varchar(255) -> nvarchar(255)
    objRsNew("sysHash") = objRsOld("sysHash")                     ' // Type changed varchar(50) -> nvarchar(50)
    objRsNew("sysGwReference") = objRsOld("sysGwReference")       ' // Type changed varchar(50) -> nvarchar(50)
    objRsNew("Priority") = objRsOld("Priority")                   ' // Default changed, from 0 to 3
    
    ' Identical fields
    objRsNew("DirectionID") = objRsOld("DirectionID")
    objRsNew("TypeID") = objRsOld("TypeID")
    objRsNew("StatusID") = objRsOld("StatusID")
    objRsNew("StatusDetailsID") = objRsOld("StatusDetailsID")
    objRsNew("ChannelID") = objRsOld("ChannelID")
    objRsNew("BillingID") = objRsOld("BillingID")
    objRsNew("ScheduledTimeSecs") = objRsOld("ScheduledTimeSecs")
    objRsNew("SentTimeSecs") = objRsOld("SentTimeSecs")
    objRsNew("ReceivedTimeSecs") = objRsOld("ReceivedTimeSecs")
    objRsNew("LastUpdateSecs") = objRsOld("LastUpdateSecs")  
    objRsNew("ReadReceipt") = objRsOld("ReadReceipt")
    objRsNew("BodyFormatID") = objRsOld("BodyFormatID")
    objRsNew("CharsetID") = objRsOld("CharsetID")
    objRsNew("Modifier") = objRsOld("Modifier")
    objRsNew("CustomField1") = objRsOld("CustomField1")
    objRsNew("sysForwarded") = objRsOld("sysForwarded")
    objRsNew("sysGwReference") = objRsOld("sysGwReference")
    objRsNew("sysCreator") = objRsOld("sysCreator")
    objRsNew("sysArchive") = objRsOld("sysArchive")
    
    ' Skip: ID - Autnumber field
    ' Skip: sysLock - Default value is unlocked
    
    objRsNew.Update
    objRsNew.Close
    
    objRsOld.MoveNext
  Wend

  objConnOld.Close
End Sub

' // ========================================================================
' // Migrate the archiving database
' // ========================================================================
Sub MigrateArchiving
  Dim objConnOld, objConnNew
  Dim strQ, objRsOld, objRsNew

  Set objConnOld = CreateObject("ADODB.Connection")
  Set objRsOld = CreateObject("ADODB.RecordSet")

  Set objConnNew = CreateObject("ADODB.Connection")
  Set objRsNew = CreateObject("ADODB.RecordSet")

  objConnOld.Open g_strOldArchiveDb
  objConnNew.Open g_strNewArchiveDb
 
  strQ = "SELECT * FROM Archive"
  objRsOld.Open strQ, objConnOld, 0, 1, 1                         ' // Open ReadOnly

  While Not objRsOld.EOF
    objRsNew.Open strQ, objConnNew, 0, 3, 1
    objRsNew.AddNew
    
    ' Changed fields
    objRsNew("TLVs") = objRsOld("TLVs")                           ' // Type changed ntext -> nvarchar(max)
    objRsNew("ToAddress") = objRsOld("ToAddress")                 ' // Type changed ntext -> nvarchar(max)
    objRsNew("Header") = objRsOld("Header")                       ' // Type changed ntext -> nvarchar(max)
    objRsNew("Body") = objRsOld("Body")                           ' // Type changed ntext -> nvarchar(max)
    objRsNew("Trace") = objRsOld("Trace")                         ' // Type changed ntext -> nvarchar(max)
    objRsNew("MessageReference") = objRsOld("MessageReference")   ' // Type changed varchar(50) -> nvarchar(max)
    objRsNew("BillingId") = objRsOld("BillingId")                 ' // Type changed varchar(255) -> nvarchar(255)
    objRsNew("FromAddress") = objRsOld("FromAddress")             ' // Type changed varchar(255) -> nvarchar(255)
    objRsNew("Subject") = objRsOld("Subject")                     ' // Type changed varchar(255) -> nvarchar(255)
    objRsNew("CustomField2") = objRsOld("CustomField2")           ' // Type changed varchar(255) -> nvarchar(255)
    objRsNew("Priority") = objRsOld("Priority")                   ' // Default changed, from 0 to 3
    
    ' Identical fields
    objRsNew("DirectionID") = objRsOld("DirectionID")
    objRsNew("TypeID") = objRsOld("TypeID")
    objRsNew("StatusID") = objRsOld("StatusID")
    objRsNew("StatusDetailsID") = objRsOld("StatusDetailsID")
    objRsNew("ChannelID") = objRsOld("ChannelID")
    objRsNew("BillingID") = objRsOld("BillingID")
    objRsNew("ScheduledTimeSecs") = objRsOld("ScheduledTimeSecs")
    objRsNew("SentTimeSecs") = objRsOld("SentTimeSecs")
    objRsNew("ReceivedTimeSecs") = objRsOld("ReceivedTimeSecs")
    objRsNew("LastUpdateSecs") = objRsOld("LastUpdateSecs")  
    objRsNew("ReadReceipt") = objRsOld("ReadReceipt")
    objRsNew("BodyFormatID") = objRsOld("BodyFormatID")
    objRsNew("CharsetID") = objRsOld("CharsetID")
    objRsNew("Modifier") = objRsOld("Modifier")
    objRsNew("CustomField1") = objRsOld("CustomField1")
    objRsNew("sysArchive") = objRsOld("sysArchive")
    
    ' Skip: ID - Autnumber field
    
    objRsNew.Update
    objRsNew.Close
    
    objRsOld.MoveNext
  Wend

  objConnOld.Close
End Sub

' // ========================================================================
' // Migrate the logging database
' // ========================================================================
Sub MigrateLogging
  Dim objConnOld, objConnNew
  Dim strQ, objRsOld, objRsNew

  Set objConnOld = CreateObject("ADODB.Connection")
  Set objRsOld = CreateObject("ADODB.RecordSet")

  Set objConnNew = CreateObject("ADODB.Connection")
  Set objRsNew = CreateObject("ADODB.RecordSet")

  objConnOld.Open g_strOldLoggingDb
  objConnNew.Open g_strNewLoggingDb
 
  strQ = "SELECT * FROM Log"
  objRsOld.Open strQ, objConnOld, 0, 1, 1                         ' // Open ReadOnly

  While Not objRsOld.EOF
    objRsNew.Open strQ, objConnNew, 0, 3, 1
    objRsNew.AddNew
    
    ' Identical fields
    objRsNew("Description") = objRsOld("Description")
    objRsNew("LogTimeSecs") = objRsOld("LogTimeSecs")
    objRsNew("LogTime") = objRsOld("LogTime")
    
    ' Skip: ID - Autnumber field
    
    objRsNew.Update
    objRsNew.Close
    
    objRsOld.MoveNext
  Wend

  objConnOld.Close
End Sub

' // ========================================================================
' // Copy an identical table
' // ------------------------------------------------------------------------
' // Only the fields that match in both tables are copied
' // ========================================================================
Sub CopyIdenticalTable(strName, objConDst, objConSrc, bSkipIdField)
  Dim strQ, objRsOld, objRsNew

  strQ = "SELECT * FROM " & strName
  Set objRsOld = objConSrc.Execute(strQ)
  While Not objRsOld.Eof

    Set objRsNew = CreateObject("ADODB.Recordset")
    objRsNew.Open strQ, objConDst, 0, 3
    objRsNew.AddNew
    
    CopyIdenticalFields objRsNew, objRsOld, bSkipIdField
 
    objRsNew.Update
    objRsNew.Close
  
    objRsOld.MoveNext
  Wend  
  
End Sub

' // ========================================================================
' // Copy a identical fields
' // ------------------------------------------------------------------------
' // Copy only the values of the fields that have the same name and type 
' // between two records.
' // ========================================================================
Sub CopyIdenticalFields(objRsDst, objRsSrc, bSkipIdField)
  Dim objDstField, objSrcField

  For Each objDstField in objRsDst.Fields
    On Error Resume Next
    Set objSrcField = objRsSrc(objDstField.Name)
    If Err.Number = 0 And (bSkipIdField = False Or objDstField.Name <> "ID")  Then 
      On Error GoTo 0
      If objSrcField.Type = objDstField.Type Then        
        objDstField.Value = objSrcField.Value
      End If      
    End If
    On Error GoTo 0
  Next 
End Sub

' // ========================================================================
' // Commit configuration
' // ------------------------------------------------------------------------
' // If everything was successful, commit the configuration by making a 
' // backup of the current new configuration and putting our interim
' // configuration database in it's place
' // ========================================================================
Sub CommitConfiguration
  Dim objFs, strBackupPath
  
  Set objFs = CreateObject("Scripting.FileSystemObject")  
  
  strBackupPath = g_strNewPath & CONFIGURATION_PATH & ".bak"
  If objFs.FileExists(strBackupPath) Then
    Dim i: i = 2
    strBackupPath = g_strNewPath & CONFIGURATION_PATH & ".bak" & i
    While objFs.FileExists(strBackupPath)
      i = i + 1
      strBackupPath = g_strNewPath & CONFIGURATION_PATH & ".bak" & i
    WEnd
  End If
  
  objFs.MoveFile g_strNewPath & CONFIGURATION_PATH, strBackupPath
  objFs.MoveFile g_strNewPath & CONFIGURATION_INTERIM, g_strNewPath & CONFIGURATION_PATH
End Sub

' // ========================================================================
' // Copy projects
' // ------------------------------------------------------------------------
' // Rename the current project directory and copy the projects from the 
' // previous installation. Also copy '.done' files from Program/Projects in
' // case the user has expanded on one of the example projects.
' // TODO: Change this to only copy the '.done' files over; since the old dir
' // no longer has the create scripts
' // ========================================================================
Sub CopyProjects
  Dim objFs, strNewProjectsPath, strOldProjectsPath, strBackupProjectsPath
  Dim strNewPScriptsPath, strOldPScriptsPath, strBackupPScriptsPath
  
  Set objFs = CreateObject("Scripting.FileSystemObject")  
  strNewProjectsPath = g_strNewPath & PROJECTS_PATH  
  strOldProjectsPath = g_strOldPath & PROJECTS_PATH
  strNewPScriptsPath = g_strNewPath & PROJECTS_SCRIPTS_PATH
  strOldPScriptsPath = g_strOldPath & PROJECTS_SCRIPTS_PATH
  
  strBackupProjectsPath = GetUniqueFolderName(strNewProjectsPath & "_")
  If objFs.FolderExists(strNewProjectsPath) Then
    ' If this script is run for the second time, this directory may not exist anymore
    objFs.MoveFolder strNewProjectsPath, strBackupProjectsPath
  End If  
  objFs.CopyFolder strOldProjectsPath, strNewProjectsPath

  If objFs.FolderExists(strOldPScriptsPath) Then
    strBackupPScriptsPath = GetUniqueFolderName(strNewPScriptsPath & "_")
    objFs.MoveFolder strNewPScriptsPath, strBackupPScriptsPath
    objFs.CopyFolder strOldPScriptsPath, strNewPScriptsPath
  End If
End Sub

' // ========================================================================
' // Get a unique folder name
' // ========================================================================
Function GetUniqueFolderName(strBase)
  Dim objFs, strBackupPath
  
  Set objFs = CreateObject("Scripting.FileSystemObject")  
  
  strBackupPath = strBase 
  If objFs.FolderExists(strBackupPath) Then
    Dim i: i = 2
    strBackupPath = strBase & i    
    While objFs.FolderExists(strBackupPath)
      i = i + 1
      strBackupPath = strBase & i
    WEnd
  End If
  GetUniqueFolderName = strBackupPath
End Function
  
' // ========================================================================
' // Copy projects
' // ------------------------------------------------------------------------
' // Walk through all of the trigger scripts. Test if they exist and present
' // them to the user to validate.
' // ========================================================================
Sub VerifyTriggers 
  Dim objConn, objFso
  Dim strQ, objRs, strMessage, strScript, strTest
 
  Set objConn = CreateObject("ADODB.Connection")  
  Set objRs = CreateObject("ADODB.Recordset")
  Set objFso = CreateObject("Scripting.FileSystemObject")  
  
  objConn.Open g_strNewConfigDb
  
  strQ = "SELECT * FROM Triggers"
  objRs.Open strQ, objConn, 0, 3

  While Not objRs.Eof    
    strScript = objRs("Script")
    strMessage = "Please verify path to this trigger:" & vbCrlf & vbCrlf & _
      "'" & objRs("Description") & "'" & vbCrlf & vbCrlf
    
    strTest = strScript
    If InStr(strScript, ":") = 0 Then
      strTest = g_strNewPath & "\" & strScript
    End If
    
    If objFso.FileExists(strTest) Then
      strMessage = strMessage & "The file does exist"
    Else
      strMessage = strMessage & "The file does NOT exist"
    End If
    
    strScript = InputBox(strMessage, "Verify script path", strScript)
    If strScript <> "" Then
      objRs("Script") = strScript
      objRs.Update
    End If
     
    objRs.MoveNext
  Wend  
    
  objConn.Close
End Sub

' // ========================================================================
' // Write a goodbye message
' // ========================================================================
Sub Conclude
  WScript.Echo "Update completed. Please continue with the upgrade instructions."
End Sub
