/*----------------------------------------------------------------------------*/
/*                                                                            */
/* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved.             */
/* Copyright (c) 2005-2014 Rexx Language Association. All rights reserved.    */
/*                                                                            */
/* This program and the accompanying materials are made available under       */
/* the terms of the Common Public License v1.0 which accompanies this         */
/* distribution. A copy is also available at the following address:           */
/* https://www.oorexx.org/license.html                                        */
/*                                                                            */
/* Redistribution and use in source and binary forms, with or                 */
/* without modification, are permitted provided that the following            */
/* conditions are met:                                                        */
/*                                                                            */
/* Redistributions of source code must retain the above copyright             */
/* notice, this list of conditions and the following disclaimer.              */
/* Redistributions in binary form must reproduce the above copyright          */
/* notice, this list of conditions and the following disclaimer in            */
/* the documentation and/or other materials provided with the distribution.   */
/*                                                                            */
/* Neither the name of Rexx Language Association nor the names                */
/* of its contributors may be used to endorse or promote products             */
/* derived from this software without specific prior written permission.      */
/*                                                                            */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS        */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT          */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS          */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT   */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,      */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED   */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,        */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING    */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS         */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.               */
/*                                                                            */
/*----------------------------------------------------------------------------*/
/****************************************************************************/
/* Name: eventlog.rex                                                       */
/* Type: Open Object Rexx Script                                            */
/*                                                                          */
/* Description: Example of how to use the WindowsEventLog class             */
/*                                                                          */
/****************************************************************************/

/* main program */

log = .WindowsEventLog~new

/******************************************************************************/
say "=============================================================================="
say "  Display some basic attibutes of a WindowsEventLog object.  Consult the"
say "  documentation for an explanation of the meaning of these attributes."
say
/******************************************************************************/
say "  Events attribute:                " log~events
say "  Current minimum read buffer size:" log~minimumReadBuffer "bytes"
say "  Current minimum read buffer size:" log~minimumRead "KBs"
say "  Minimum read buffer range:       " log~minimumReadMin "KBs to" log~minimumReadMax "KBs"
say

/******************************************************************************/
say "=============================================================================="
say "  Open each event log on this computer and display information about the"
say "  log.  Then close the log."
say
/******************************************************************************/
logNames = .array~new
ret = log~getLogNames(logNames)

if ret == 0 then do name over logNames
  ret = log~open( , name)

  if ret == 0 then do
    say "  Event log:         " name
    say "  Total records:     " log~getNumber
    say "  First record number" log~getFirst
    say "  Last record number " log~getLast
    say "  Log is full?       " logicalToText(log~isFull)
    log~close
  end
  else do
     say "  Error opening the" name "event log rc:" ret
  end
  say
end


/******************************************************************************/
say "=============================================================================="
say "Going to read the 3 oldest records in the Application event Log,"
say "oldest first.  The log is not opened before reading.  The open and"
say "close are done implicitly."
/******************************************************************************/
ok = doPause(.false)

count = 0

if log~getNumber < 3 then do
  say "To few records ("log~getNumber") for this demonstration."
end
else do
  -- If the user has set the overwrite option for the event log, the first
  -- record is not always 1.   FORWARDS is the default.
  firstRec = log~getFirst
  ret = log~readRecords( , , , firstRec, 3)

  if ret == 0 then do
    count = displayRecords(log~events)
  end
  else do
    say "Error reading the Application event log rc:" ret
  end
end

say "=========================================================================="
say count "records read"
say

/******************************************************************************/
say "=========================================================================="
say "Going to read the 3 most recent records in the System event Log, the"
say "most recent first.  The log is not opened before reading.  The open"
say "and close are done implicitly."
/******************************************************************************/
ok = doPause(.false)

count = 0

if log~getNumber("", "System") < 3 then do
  say "To few records (" || log~getNumber("", "System") || ") for this demonstration."
end
else do
  -- If the user has set the overwrite option for the event log, the most recent
  -- record is not always the same as the record number.
  lastRec = log~getLast( , "System")
  ret = log~readRecords("BACKWARDS", , "System", lastRec, 3)

  if ret == 0 then do
    count = displayRecords(log~events)
  end
  else do
    say "Error reading the System event log rc:" ret
  end
end

say "=========================================================================="
say count "records read"
say

/******************************************************************************/
say "=========================================================================="
say "Going to read the 3 oldest records in the System event Log, in reverse"
say "chronological older.  The log is opened before any operation, then"
say "closed when all operations are done.  This allows specifying the log"
say "name only once."
/******************************************************************************/
ok = doPause(.false)

count = 0

ret = log~open("", "System")

if ret == 0 then do
  totalRecs = log~getNumber

  if totalRecs < 3 then do
    say "To few records (" || totalRecs || ") for this demonstration."
  end
  else do
    -- If the user has set the overwrite option for the event log, the first
    -- record is not always number 1.
    firstRec = log~getFirst

    -- Now calculate the record number we are going to start at.
    startRec = firstRec + 3 - 1  -- Same as firstRec + 2 of course.

    ret = log~readRecords("BACKWARDS", , , startRec, 3)

    if ret == 0 then do
      count = displayRecords(log~events)
    end
    else do
      say "Error reading the System event log rc:" ret
    end
  end
end
else do
  say "Error opening the System event log rc:" ret
end

-- It is always safe to invoke the close() method.  If no log is open, then the
-- method does nothing.
log~close

say "=========================================================================="
say count "records read"
say


/******************************************************************************/
say "=========================================================================="
say "Going to read the complete Security event log forwards.  If there are"
say "a log of records, this will produce substantial output.  You can cancel"
say "if you wish."
/******************************************************************************/
ok = doPause(.true)
count = 0

if ok then do

  ret = log~readRecords("FORWARDS", , "Security")
  select
    when when ret == 0 & log~events~items == 0 then do
      -- On many systems the security log is empty.
      say "The Security event log has no event records."
    end

    when ret <> 0 then do
      say "Error reading the Security event log rc:" ret
    end

    otherwise do
      count = displayRecords(log~events)
    end
  end
  -- End select
end
else do
  say "Skipping this demonstartion."
end

say "=========================================================================="
say count "records read"
say


/******************************************************************************/
say "=========================================================================="
say "Going to read the complete System event log in reverse chronological"
say "order.  If there are a lot of records, this will produce substantial"
say "output.  You can cancel if you wish."
/******************************************************************************/
ok = doPause(.true)
count = 0

if ok then do

  ret = log~readRecords("BACKWARDS", , "System")
  select
    when when ret == 0 & log~events~items == 0 then do
      say "The System event log has no event records."
    end

    when ret <> 0 then do
      say "Error reading the System event log rc:" ret
    end

    otherwise do
      count = displayRecords(log~events)
    end
  end
  -- End select
end
else do
  say "Skipping this demonstartion."
end

say "=========================================================================="
say count "records read"
say


/******************************************************************************/
say "=========================================================================="
say "Going to write some records to the Application event log."
say "You can skip this step if you do not want records added to"
say "your Application event log."

/* Some notes on how the event logging service works.  Typically, applications
   that want to write records to one of the event logs, add entries to the
   registry.  These registry entries control how the event logging service
   handles the writing of a record.

   The registry entries are written to subkeys under the Event log service.

   If an application has not added the registry entries, then the event records
   are *always* written to the Application log.  This can not be over-ridden.

   So, say there is an application, MyApplication that wants to write a record
   using the source name of MyApplication to an event log.  To do that the
   source name must be a subkey of a logfile entry under the EventLog key in the
   registry.

   If MyApplication should be written to the System log the registry key must
   be added like this:

   HKEY_LOCAL_MACHINE
   	  System
   	     CurrentControlSet
   	        Services
           	   EventLog
   	              System
   							     MyApplication
   	           Security
           	   System

   If the key does not exists, the Application event log is always used.
*/

/******************************************************************************/
ok = doPause(.true)

if ok then do
  source     = "MyLog"
  type       = 4  -- Information
  category   = 22
  id         = 33
  binaryData = "01 1a ff 4b 0C"x

  ret = log~write( , source, type, category, id, binaryData, "String1", "sTring2")
  if ret == 0 then
    say "Record" source "successfully written"
  else
    say "Error writing record" source "rc:" ret

  -- The default type for writing a record is 'Error' This might look a little
  -- scary to a user, so we will use Information for the type.
  type = 4  -- Information

  ret = log~write(, "Application2", type,,, "1A 1B 1C 0000 00"x, "First String", "Second String")
  if ret == 0 then
    say "Record Application2 successfully written"
  else
    say "Error writing record Application2 rc:" ret

  ret = log~write(, "Security1", type,,,"1A1B1C000000"x, "Tom", "John", "Larry", "Frank")
  if ret == 0 then
    say "Record Security1 successfully written"
  else
    say "Error writing record Security1 rc:" ret

  ret = log~write(,"Security2", type,,,"04 03 01 1B 1C 00 FF 00 FE 00 FD"x, "One", "Two")
  if ret == 0 then
    say "Record Security2 successfully written"
  else
    say "Error writing record Security2 rc:" ret

  ret = log~write(, "System1", type,,,"1A 1B 1C 00 00 00"x, "1", "2", "3", "4")
  if ret == 0 then
    say "Record System1 successfully written"
  else
    say "Error writing record System1 rc:" ret

  ret = log~write(, "System2", type,,,"1A1B1C000000"x, "House", "Apartment")
  if ret == 0 then
    say "Record System2 successfully written"
  else
    say "Error writing record System2 rc:" ret

  ret = log~write(, "NotRegisteredAppliaction1", type,,,"1A1B1C0000FF00"x, "Cat", "Dog", "Rabbit")
  if ret == 0 then
    say "Record NotRegisteredAppliaction1 successfully written"
  else
    say "Error writing record NotRegisteredAppliaction1 rc:" ret

  rawData = "4d 61 72 6b 20 4d 69 65 73 66 65 6c 64 0d 0a"x
  ret = log~write(, "NotRegisteredAppliaction2", type,,, rawData, "ooRexx", "example")
  if ret == 0 then
    say "Record NotRegisteredAppliaction2 successfully written "
  else
    say "Error writing record NotRegisteredAppliaction2 rc:" ret
end
else do
  say "Skipping this demonstartion."
end

say "=========================================================================="
say


/******************************************************************************/
say "=============================================================================="
say "The next and last demonstration will clear the Application event log."
/******************************************************************************/

backupFile = getWriteableFileName()

if backupFile == "" then do
  say "Could not determine with certainty a writeable location for a backup file."
  say "You may want to skip this demonstration, the log will not be backed up."
end
else do
  say "The log will first be backed up to this file:"
  say " " backupFile
end

say
say "You can skip this step if you do not want to clear"
say "your Application event log."
say
say "If this is the first time you ran this example, you"
say "may want to skip this test so that you can see the"
say "records written to the log in the previous demonstration."
say "(If you elected to write any records.)"
say
say "If you previously ran this example and saved the Application"
say "log, you may want to move the back up file to a different"
say "location.  The same back up file name is used each time the"
say "example executes."

ok = doPause(.true)
if ok then do
  if backupFile \== "" then ret = log~clear(, , backupFile)
  else ret = log~clear

  if ret == 0 then say "The Application event log was cleared successfully."
  else say "Error clearing the Application event log rc:" ret
end

return 0   /* leave program */

::requires "winsystm.cls"

/* Routine to display the event log records */
::routine displayRecords
  use strict arg records

  say records~items "records read"

  do record over records
    say "=========================================================================="
    parse var record type date time "'" sourcename"'" id userid computer "'" string "'" "'" data "'"
    say 'Type     : 'type
    say 'Date     : 'date
    say 'Time     : 'time
    say 'Source   : 'sourcename
    say 'ID       : 'id
    say 'UserId   : 'userid
    say 'Computer : 'computer
    say 'Detail   : 'string
    say 'Data     : 'data
  end

return records~items

/* Simple routine to do a logical into text */
::routine logicalToText
  use strict arg logical
  if logical then return "True"
  else return "False"

/* Simple routine to pause the program */
::routine doPause
  use arg confirm

  say
  if confirm then do
    say "Continue? (y/n)"
    parse lower pull answer
    ok = (answer~left(1) == 'y')
  end
  else do
    say "Press Enter to continue"
    pull
    ok = .true
  end
  say

return ok

::routine getWriteableFileName

  -- Find this user's Documents directory, surely that is writeable.

  shell = .oleObject~new('Shell.Application')
  folderConstant = '5'~x2d()
  folderObj = shell~nameSpace(folderConstant)
  folderItem = folderObj~self
  documentsFolder = folderItem~path

  backupFile = ""

  -- Check that we got a good directory
  if SysIsFileDirectory(documentsFolder) then do
    -- Unfortunately, we can not use a network mapped drive and it is not
    -- uncommon for people to have their My Documents directory on a network
    -- share.
    drv = documentsFolder~left(2)
    if SysDriveMap(drv, "REMOTE")~caselessWordPos(drv) == 0 then do
      backupFile = documentsFolder || '\applicationBackup_01.evt'
    end
  end

  if backupFile == "" then do
    -- Keep looking, see if we can write to the current directory.
    currentDir = directory()
    if canWriteToDir(currentDir) then do
      backupFile = currentDir || "\applicationBackup_01.evt"
    end
    else do
      -- As a last resort, try the temp directory
      tempDir = value("TEMP", , 'ENVIRONMENT')
      if canWriteToDir(tempDir) then do
        backupFile = tempDir || "\applicationBackup_01.evt"
      end
    end
  end

return backupFile

::routine canWriteToDir
  use strict arg dir

  tmpFile = dir || '\' || "tmpFileWriteTest.deleteMe"

  -- Write to the file, close it, test it, delete it.
  j = lineout(tmpFile, "Delete this file if you find it")
  j = lineout(tmpFile)
  if SysIsFile(tmpFile) then do
    answer = .true
  end
  else do
    answer = .false
  end

  'del /F /Q' tmpFile '1>nul 2>&1'

return answer
