Changeset 601

Show
Ignore:
Timestamp:
06/21/07 16:37:41 (1 year ago)
Author:
kindlund
Message:

Merged DB branch back into trunk.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • honeyclient/trunk/bin/install_honeyclient_db.pl

    • Property svn:keywords set to Id "$file"
    r521 r601  
    1 #!/usr/bin/perl -w 
     1#!/usr/bin/perl -Ilib -w 
     2 
     3# $Id$ 
     4 
    25use strict; 
     6use warnings; 
     7use Carp (); 
    38 
     9use ExtUtils::MakeMaker qw(prompt); 
     10use Net::IP qw(ip_is_ipv4); 
    411use DBI; 
     12use DBI::Const::GetInfoType; 
    513use HoneyClient::Util::Config qw(getVar); 
    614 
    7 # Retrieve values from HoneyClient config file 
     15print "************************************************\n" . 
     16      "*** HoneyClient Database Installation Script ***\n" . 
     17      "************************************************\n" . 
     18      "\n" . 
     19      "This script will install and configure the\n" . 
     20      "HoneyClient database onto an existing MySQL server.\n" . 
     21      "\n" . 
     22      "Before running this script, you need to edit your\n" . 
     23      "etc/honeyclient.xml global configuration file and\n" . 
     24      "make sure that the <HoneyClient/><DB/> section\n" . 
     25      "contains valid database connection information.\n" . 
     26      "\n"; 
     27 
     28# Retrieve values from the global configuration file. 
    829my $host = getVar(name => "host", namespace => "HoneyClient::DB"); 
    930my $user = getVar(name => "user", namespace => "HoneyClient::DB"); 
    1031my $pass = getVar(name => "pass", namespace => "HoneyClient::DB"); 
    1132my $database_name = getVar(name => "dbname", namespace => "HoneyClient::DB"); 
     33my ($question, $root_password); 
    1234 
    13 print "Attempting to Create a HoneyClient database with the name: ". 
    14     "${database_name}\n"; 
    15 my $r_pass = retrieve_pw(); 
     35print "The following database configuration was found:\n\n"; 
    1636 
    17 my $dsn = "DBI:mysql:database=mysql;host=".$host; 
     37my $buf = sprintf("%s %s\t= '%s'", " " x 4,  "host", $host) . "\n" . 
     38          sprintf("%s %s\t= '%s'", " " x 4,  "user", $user) . "\n" . 
     39          sprintf("%s %s\t= '%s'", " " x 4,  "pass", $pass) . "\n" . 
     40          sprintf("%s %s\t= '%s'", " " x 4,  "dbname", $database_name) . "\n"; 
     41 
     42print $buf . "\n"; 
     43 
     44$question = prompt("Is this correct?", "yes"); 
     45print "\n"; 
     46 
     47if ($question !~ /^y.*/i) { 
     48    print "Please edit the etc/honeyclient.xml file\n" . 
     49          "accordingly re-run this script.\n"; 
     50    exit; 
     51
     52 
     53# Get the root password. 
     54system("stty -echo"); 
     55$root_password = prompt("Please enter your database 'root' password:"); 
     56system("stty echo"); 
     57print "\n"; 
     58print "\n"; 
     59 
     60my $sql = undef; 
     61my $dsn = "DBI:mysql:database=mysql;host=" . $host; 
    1862 
    1963eval { 
    2064    # Connect and Create Database 
    21     my $dbh = DBI->connect($dsn,'root',$r_pass,{'RaiseError' => 1}); 
    22     Carp::croak "Connect Failed: $DBI::errstr" if ($dbh eq '');  
    23      
    24     $dbh->do("CREATE DATABASE ".$database_name); 
    25      
    26     my $mgr_address = (get_mgr_addr() or '127.0.0.1'); 
    27      
    28     # Create A User Account to Access and Manage Database 
    29     print "Attempting to Create database user: ${user}\n"; 
    30     $dbh->do("GRANT ALL PRIVILEGES ON ".$database_name.".* TO '".$user."\'@\'". 
    31         $mgr_address."' IDENTIFIED BY '".$pass."'"); 
     65    my $dbh = DBI->connect($dsn, 'root', $root_password, {'RaiseError' => 1}); 
     66    if ($dbh eq '') { 
     67        Carp::croak("Installation Failed: " . $DBI::errstr); 
     68    } 
     69 
     70    my $database_system_name = $dbh->get_info($GetInfoType{SQL_DBMS_NAME}); 
     71    my $database_system_version = $dbh->get_info($GetInfoType{SQL_DBMS_VER}); 
     72 
     73    # Extract the major version number. 
     74    $database_system_version =~ s/^(\d.*?)\..*/$1/; 
     75 
     76    if (($database_system_name !~ /^mysql/i) or 
     77        ($database_system_version < 5)) { 
     78 
     79        print "Your database does not appear to be running MySQL v5.0\n" . 
     80              "or greater.  This code will only work properly on databases\n" . 
     81              "with this type and version.\n"; 
     82 
     83        $dbh->disconnect() if $dbh; 
     84        exit; 
     85    } 
     86 
     87    # Create the database. 
     88    print "* Creating database name '" . $database_name . "'.\n\n"; 
     89    $sql = "CREATE DATABASE " . $database_name; 
     90    print "Issuing SQL Command:\n" . $sql . "\n"; 
     91    proceed(); 
     92    $dbh->do($sql); 
     93 
     94    # Get the IP address of the host system where the Manager will be 
     95    # installed to. 
     96    my $manager_address = "127.0.0.1"; 
     97    $question = prompt("Will the database and the HoneyClient::Manager\n" . 
     98                       "run on the same host system?", "yes"); 
     99    print "\n"; 
     100 
     101    if ($question !~ /^y.*/i) { 
     102        my $ip = "x"; 
     103        my $is_valid = 0; 
     104        while (!$is_valid) { 
     105            $manager_address = prompt("Enter the IP address of the host system\n" . 
     106                                      "that the HoneyClient::Manager will run\n" . 
     107                                      "from (wildcard is %):\n", "172.16.164.%"); 
     108 
     109            $ip = $manager_address; 
     110            $ip =~ s/%/0/g; 
     111            $is_valid = ip_is_ipv4($ip); 
     112 
     113            if (!$is_valid) { 
     114                print "\n"; 
     115                print "* Error: Address is not valid! Try again.\n"; 
     116            } 
     117            print "\n"; 
     118        } 
     119    } 
     120 
     121    # Create a user account to access and manage the database. 
     122    print "* Creating database user '". $user . "'.\n\n"; 
     123    $sql = "GRANT ALL PRIVILEGES ON " . $database_name .".* TO '" . $user . "\'@\'" . 
     124           $manager_address . "' IDENTIFIED BY '" . $pass . "'"; 
     125    print "Issuing SQL Command:\n" . $sql . "\n"; 
     126    proceed(); 
     127    $dbh->do($sql); 
     128 
     129    # Flush privileges, in order to get MySQL to re-read the GRANT table. 
     130    print "* Flushing privileges, in order to activate the newly added user.\n\n"; 
     131    $sql = "FLUSH PRIVILEGES"; 
     132    print "Issuing SQL Command:\n" . $sql . "\n"; 
     133    proceed(); 
     134    $dbh->do($sql); 
    32135         
    33136    $dbh->disconnect() if $dbh; 
    34137}; 
    35138if ($@) { 
    36     die "Failed to initialize Database Connection:\n\t$@"; 
     139    Carp::croak("Installation Failed: " . $@); 
     140
     141 
     142sub proceed { 
     143    print "\n"; 
     144    my $question = prompt("Proceed?", "yes"); 
     145    print "\n"; 
     146    if ($question !~ /^y.*/i) { 
     147        print "Aborting Installation.\n"; 
     148        exit; 
     149    } 
    37150} 
    38151 
    39152print "Database and user installed successfully.\n"; 
    40  
    41 # Retrieve a password from the user while hiding it from the display 
    42 sub retrieve_pw { 
    43     my $p; 
    44     print "Please enter your database 'root' password: "; 
    45     system("stty -echo"); 
    46     chomp($p=<STDIN>); 
    47     print "\n"; 
    48     system("stty echo"); 
    49     return $p; 
    50 } 
    51  
    52 my $re_ip_num = qr{([01]?\d\d?|2[0-4]\d|25[0-5]|%)}; 
    53 my $re_ip = qr{^$re_ip_num\.$re_ip_num\.$re_ip_num\.$re_ip_num$}; 
    54  
    55 sub get_mgr_addr { 
    56     print "Will the database and the manager run on the same system? [yes] "; 
    57     while(1) { 
    58         my $in; 
    59         chomp($in = <STDIN>); 
    60         if ($in eq "" or $in eq "yes") { 
    61             return ""; 
    62         } 
    63         elsif ($in eq "no") { 
    64             print "Enter the address the manager will connect from.\n". 
    65                 "(wildcard is %): "; 
    66             while (1) { 
    67                 my $addr; 
    68                 chomp($addr = <STDIN>); 
    69                 print "\n"; 
    70                 # TODO: Need to check for a valid IP address/netmask. 
    71                 #if ($addr =~ /$re_ip/) { 
    72                     return $addr; 
    73                 #} 
    74                 print "Invalid Entry. Enter the manager address: "; 
    75             } 
    76         } 
    77         else { 
    78             print "Invalid Entry. (type yes or no): "; 
    79         } 
    80     } 
    81 } 
  • honeyclient/trunk/etc/honeyclient.xml

    r586 r601  
    301301        </enable> 
    302302        <host description="The system providing the HoneyClient database.  If the database is installed on the same host system as the Manager, then localhost should be used." default="127.0.0.1"> 
    303            172.16.164.1 
     303            172.16.164.1 
    304304        </host> 
    305305        <dbname description="The name of the HoneyClient database." default="HoneyClient"> 
  • honeyclient/trunk/etc/honeyclient_log.conf

    r409 r601  
    6363# Screen Logging Settings 
    6464#log4perl.logger.HoneyClient.Agent.Integrity.Registry=DEBUG, Screen 
     65#log4perl.logger.HoneyClient.DB=DEBUG, Screen 
    6566# Suppress Parser Debugging Messages 
    6667#log4perl.logger.HoneyClient.Agent.Integrity.Registry.Parser=INFO, Screen 
  • honeyclient/trunk/lib/HoneyClient/DB.pm

    • Property svn:executable deleted
    r521 r601  
    33# Package:     HoneyClient::DB 
    44# File:        DB.pm 
    5 # Description: Abstract class for controlling HoneyClient Database Access 
     5# Description: Abstract class for controlling storage of HoneyClient 
     6#              data into a database. 
    67# 
    78# CVS: $Id$ 
     
    1516# as published by the Free Software Foundation, using version 2 
    1617# of the License. 
    17 #  
     18# 
    1819# This program is distributed in the hope that it will be useful, 
    1920# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    2021# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    2122# GNU General Public License for more details. 
    22 #  
     23# 
    2324# You should have received a copy of the GNU General Public License 
    2425# along with this program; if not, write to the Free Software 
     
    2829####################################################################### 
    2930 
     31=pod 
     32 
    3033=head1 NAME 
    3134 
    32 HoneyClient::DB - Abstract HoneyClient Database Class 
     35HoneyClient::DB - Perl extension to provide an abstract interface for 
     36storing HoneyClient data into a database. 
     37 
     38=head1 VERSION 
     39 
     40This documentation refers to HoneyClient::DB version 0.97. 
    3341 
    3442=head1 SYNOPSIS 
    3543 
    36 HoneyClient::DB is an abstract class that is used to store HoneyClient 
    37 compromise data, system configurations, network traffic, or any other desired 
    38 data. A class can be created as follows: 
    39    
     44As a generic example, let's store data about superheroes. 
     45 
     46=head2 DEFINE SCHEMAS 
     47 
     48  # First, we define a schema for each superhero ability (child object). 
    4049  use HoneyClient::DB; 
    4150  package HoneyClient::DB::SuperHero::Ability; 
    4251  use base("HoneyClient::DB"); 
    43     
    44   our %fields => { 
    45       string => { 
    46           name => { 
    47               required => 1, 
    48           }, 
    49           real_name => { 
    50               required => 1 
    51           } 
    52       }, 
    53       text => { 
    54           description => { 
    55           }, 
    56       }, 
    57       int => [recharge_time], 
    58   }; 
    59     
     52 
     53  # Define Ability Schema 
     54  our %fields = ( 
     55      string => { 
     56          # Each ability should have a name.  
     57          name => { 
     58              # This name should be required. 
     59              required => 1, # Must exist and is not null 
     60          }, 
     61      }, 
     62 
     63      # Each ability may have an optional description. 
     64      text => [ 'description' ], 
     65 
     66      # Each ability may have an optional recharge time. 
     67      int  => [ 'recharge_time' ], 
     68  ); 
     69   
     70  # Next, we define a schema for each superhero (parent object). 
    6071  package HoneyClient::DB::SuperHero; 
    6172  use base("HoneyClient::DB"); 
    62     
    63   our %fields => { 
    64       string => { 
    65           name => { 
    66               required => 1, 
    67           }, 
    68           real_name => {}, 
    69       }, 
    70       int => [height,weight], 
    71       array => { 
    72           abilities => { 
    73               objclass=> "HoneyClient::DB::SuperPower", 
    74           }, 
    75       }, 
    76       timestamp => { 
    77           birth_date => { 
    78             required => 1, 
    79           }, 
    80       }, 
    81   }; 
     73   
     74  # Define SuperHero Schema  
     75  our %fields = ( 
     76      string => { 
     77          # Each superhero should have a name. 
     78          name => { 
     79              # This name should be required. 
     80              required => 1, 
     81              key => $HoneyClient::DB::KEY_UNIQUE_MULT, 
     82          }, 
     83          # Each superhero may have an optional real name. 
     84          real_name => { 
     85              key => $HoneyClient::DB::KEY_UNIQUE_MULT, 
     86          }, 
     87          # If 2 SuperHero Objects have the same 'name' and 'real_name' 
     88          # fields, then only the first object will be inserted succesfully 
     89      }, 
     90       
     91      # Each superhero may have optional height and weight stats.  
     92      int => [ 'height', 'weight' ], 
     93 
     94      # Each superhero must have a primary ability. 
     95      ref => { 
     96          primary_ability => { 
     97              # Reference child object type. 
     98              objclass=> "HoneyClient::DB::SuperHero::Ability", 
     99 
     100              # This should be required. 
     101              required => 1, 
     102          }, 
     103      }, 
     104 
     105      # Each superhero may have optional abilities. 
     106      array => { 
     107          abilities => { 
     108              # Reference child object type. 
     109              objclass=> "HoneyClient::DB::SuperHero::Ability", 
     110          }, 
     111      }, 
     112 
     113      # Each superhero should have a birth date. 
     114      timestamp => { 
     115          birth_date => { 
     116              required => 1, 
     117          }, 
     118      }, 
     119  ); 
     120   
     121  1; 
     122 
     123=head2 USE SCHEMAS 
     124 
     125  # Now, we start generating data to insert into our database. 
    82126 
    83127  use HoneyClient::DB::SuperHero; 
    84  
    85   my $hero = {  
    86       name => 'Superman', 
    87       real_name => 'Clark Kent' 
    88       weight => 225, 
    89       height => 75, 
    90       birth_date => '06011938 12:34:56', 
    91       ability => [ 
     128  use Data::Dumper; 
     129 
     130  # Create a new superhero. 
     131  my $hero = { 
     132      name       => 'Superman', 
     133      real_name  => 'Clark Kent', 
     134      weight     => 225, 
     135      height     => 75, 
     136      birth_date => '1998-06-01 12:34:56', # YYYY-MM-DD HH:MM:SS 
     137      primary_ability => { 
     138          name              => 'Super Strength', 
     139          description       => 'More powerful than a locomotive.', 
     140      }, 
     141      abilities  => [ 
    92142          { 
    93               name => 'Super Strength', 
    94               description => 'More powerful than a locomotive'
     143              name          => 'Flight', 
     144              description   => "It's a bird, it's a plane."
    95145          }, 
    96146          { 
    97               name => 'Flight', 
    98               description => 'It's a bird, it's a plane...', 
     147              name          => 'Heat Vision', 
     148              recharge_time => 5, # in seconds 
    99149          }, 
    100           { 
    101               name => 'Heat Vision', 
    102               recharge_time => 5, 
    103           }, 
    104       ] 
     150      ], 
    105151  }; 
    106  
    107   my $hero = HoneyClient::DB::SuperHero->new($hero); 
     152     
     153  # Instantiate a new SuperHero object. 
     154  # Upon creation, the data will be checked against the schema. 
     155  # This call will croak if any errors occur.  
     156  $hero = HoneyClient::DB::SuperHero->new($hero); 
     157 
     158  # Insert the data into the database. 
     159  $hero->insert(); 
    108160   
    109   $hero->insert(); 
    110  
     161  # Retrieve the superhero. 
    111162  my $filter = { 
    112163      name => 'Superman', 
    113164  }; 
    114  
    115   my $hero1 = HoneyClient::DB::SuperHero->select($filter); 
     165     
     166  # Retrieves rows in the SuperHero table where name is 'Superman'. 
     167  # NOTE: At this time, the returned data is NOT identical to 
     168  # the object inserted. 
     169  my $inserted_hero = HoneyClient::DB::SuperHero->select($filter); 
     170 
     171  # Printing the contents of the returned content should clarify 
     172  # how the data looks. 
     173  $Data::Dumper::Indent = 1; 
     174  $Data::Dumper::Terse = 0; 
     175  print Dumper($inserted_hero) . "\n"; 
    116176 
    117177=head1 DESCRIPTION 
    118178 
    119 B<HoneyClient::DB> is an abstract class used to access the HoneyClient 
    120 Database. The class is not to be used directly, but can be used to derive 
    121 classes of objects that will be inserted into the HoneyClient Database during 
    122 the operation of a HoneyClient component. 
    123  
    124 B<NOTE:> It is important to note that HoneyClient::DB will die if it is used 
    125 without the prior existence of the database described in /etc/honeyclient.xml or 
    126 if the module cannot establish a connection. 
    127  
    128 =head1 CONFIG 
    129  
    130 Configuration options used by HoneyClient::DB to establish connections are 
    131 stored in /etc/honeyclient.xml. 
     179This library is an abstract class used to access and store HoneyClient 
     180within a database. The class is not to be used directly, but can be inherited 
     181by sub-classes, in order to indirectly store specific types of data into a 
     182database. 
     183 
     184B<Note>: Any calls made to this library will fail, if a database is not properly 
     185described in the <HoneyClient/><DB/> section of the etc/honeyclient.xml 
     186configuration file or if the library cannot establish a connection to the 
     187database. 
     188 
     189=head2 SCHEMA DEFINITION 
     190 
     191The schema for a HoneyClient::DB subclass is created from the B<%fields> 
     192variable, a multi-level hash table. 
     193 
     194=head3 FIRST LEVEL: DATA TYPE 
     195 
     196The keys at the first level of B<%fields> define the data types to be used 
     197for each column, which are named as keys in the second level.  The following 
     198is a list of acceptable data types: 
     199 
     200=over 4 
     201 
     202=item * B<'int'> 
     203 
     204An integer. 
     205 
     206=item * B<'string'> 
     207 
     208A string no longer than 255 characters. 
     209 
     210=item * B<'text'> 
     211 
     212A string no longer than 65,535 characters. 
     213 
     214=item * B<'timestamp'> 
     215 
     216An ISO8601 compliant timestamp (i.e., 'MMDDYYYY HH:MM:SS'). 
     217 
     218=item * B<'array'> 
     219 
     220An array.  Used to represent one-to-many relationships. 
     221 
     222B<Note>: If this type is specified, then the L<'objclass'> option 
     223must be set within each column name. 
     224 
     225=item * B<'ref'> 
     226 
     227A reference.  Used to represent one-to-one relationships. 
     228 
     229B<Note>: If this type is specified, then the L<'objclass'> option 
     230must be set within each column name. 
     231 
     232=back 
     233 
     234=head3 SECOND LEVEL: COLUMN NAMES 
     235 
     236Column names are defined as keys in the second level of B<%fields>.  If 
     237each column does not need any special options (e.g., making the column 
     238required), then an array reference can hold all the column names.  
     239For example, the following schema defines 3 default integer fields: 
     240 
     241  %our fields = { 
     242      int => [ 
     243          'col_a', 
     244          'col_b', 
     245          'col_c', 
     246      ], 
     247  }; 
     248 
     249However, if some of the columns need special options set (e.g., making 'col_b' 
     250required), then a sub-hash table should be defined instead, as follows: 
     251 
     252  %our fields = { 
     253      int => {  
     254          'col_a' => {}, 
     255          'col_b' => { 
     256              'required' => 1, 
     257          }, 
     258          'col_c' => {}, 
     259      }, 
     260  }; 
     261 
     262=head3 THIRD LEVEL: OPTIONS 
     263 
     264If needed, options are defined in the third level of B<%fields>.  These 
     265options are described as follows: 
     266 
     267=over 4 
     268 
     269=item * B<'check_func'> 
     270 
     271If defined, then this contains a reference to a subroutine that will 
     272verify the actual column data is in a proper format.  This overrides the 
     273default internal check function for the data type of that column. 
     274 
     275=item * B<'init_val'> 
     276 
     277If defined, then this value will be the default value that the database 
     278will assign to the column, if empty data is inserted into this column. 
     279 
     280=item * B<'key'> 
     281 
     282If defined, then this value creates an index for the column. 
     283Possible values are: 
     284 
     285=over 4 
     286 
     287=item * B<$HoneyClient::DB::KEY_INDEX> 
     288 
     289If set, an index will be created in the database to improve the search 
     290time of this column.  This option is only recommended for very frequently 
     291searched columns. 
     292 
     293=item * B<$HoneyClient:DB::KEY_UNIQUE> 
     294 
     295If set, a UNIQUE index is created for the column. If a record is inserted that 
     296has a match with this column in a previously existing record, the insert will 
     297fail on the database side, but the 'id' of that existing record will be 
     298returned. 
     299 
     300=item * B<$HoneyClient:DB::KEY_UNIQUE_MULT> 
     301 
     302If set, the column is added to a UNIQUE index comprised of all other columns 
     303with the KEY_UNIQUE_MULT key option. This index is used to ensure ALL VALUES for 
     304the columns in the index are distinct. An insert of a record matching an 
     305existing record will return the ID of that record. 
     306 
     307=back 
     308 
     309=item * B<'objclass'> 
     310 
     311This option is required and only used by the L<'array'> and L<'ref'> 
     312data types.  The value should be a string which contains the package name of 
     313the schema to include as a child. 
     314 
     315=item * B<'required'>  
     316 
     317If defined and set to 1, then this option will cause all subsequent 
     318B<HoneyClient::DB::*-E<gt>L<new>($data)> operations to fail, if the B<$data> 
     319does not contain the required field. 
     320 
     321=back 
     322 
     323=head1 DATABASE CONFIGURATION 
     324 
     325This library expects to connect to a MySQL v5.0 or greater database. 
     326To specify which database this library should use, see the 
     327<HoneyClient/><DB/> section of the etc/honeyclient.xml configuration file 
     328for further details. 
    132329 
    133330=cut 
     
    135332package HoneyClient::DB; 
    136333 
     334use Data::Dumper qw(Dumper); 
    137335use strict 'vars', 'subs'; 
    138336use warnings; 
    139337 
    140338BEGIN { 
     339 
    141340    #Dependencies 
    142341    use DBI; 
     
    144343    use HoneyClient::Util::Config; 
    145344    use DateTime::Format::ISO8601; 
    146     use Data::Dumper; 
    147     use Math::BigInt; 
    148     require Exporter; 
     345    use Math::BigInt; 
     346    use Log::Log4perl qw(:easy); 
     347 
     348    require Exporter; 
    149349 
    150350    # Traps signals, allowing END: blocks to perform cleanup. 
    151351    use sigtrap qw(die untrapped normal-signals error-signals); 
    152     $SIG{PIPE} = 'IGNORE'; # Do not exit on broken pipes. 
     352    $SIG{PIPE} = 'IGNORE';    # Do not exit on broken pipes. 
    153353 
    154354    #Globals 
    155     our @ISA = qw(Exporter); 
     355    our @ISA    = qw(Exporter); 
    156356    our @EXPORT = qw(); 
    157357    our @EXPORT_OK; 
    158358    our $VERSION = 0.9; 
    159359 
    160     my $database_version; #  = $dbh->get_info(  18 ); # SQL_DBMS_VER 
    161 
     360    my $database_version;     #  = $dbh->get_info(  18 ); # SQL_DBMS_VER 
     361
     362 
     363# The global logging object. 
     364our $LOG = get_logger(); 
    162365 
    163366our $dbhandle; 
     367 
    164368# To be used ONLY INTERNALLY! 
    165 our (%_types,%_check,%_required,%_init_val,%_keys,%defaults); 
     369our ( %_types, %_check, %_required, %_init_val, %_keys, %defaults ); 
     370 
    166371# %fields must be defined by all children classes 
    167372our %fields; 
    168373 
    169374#constants 
    170 our ($STATUS_DELETED, $STATUS_ADDED, $STATUS_MODIFIED) = (0,1,2); # Integrity status field 
    171 our ($KEY_NONE,$KEY_INDEX,$KEY_UNIQUE,$KEY_UNIQUE_MULT) = (0,1,2,3); # Uniqueness of Attributes 
     375our ( $STATUS_DELETED, $STATUS_ADDED, $STATUS_MODIFIED ) = 
     376  ( 0, 1, 2 );    # Integrity status field 
     377our ( $KEY_INDEX, $KEY_UNIQUE, $KEY_UNIQUE_MULT ) = 
     378  ( 0, 1, 2 );    # Uniqueness of Attributes 
    172379our $debug = 0; 
     380 
    173381# Initialize Connection 
    174382our %config; 
    175383$config{driver} = "mysql"; 
    176 $config{host} = getVar(name => "host", namespace => "HoneyClient::DB"); 
    177 $config{port} = getVar(name => "port", namespace => "HoneyClient::DB"); 
    178 $config{user} = getVar(name => "user", namespace => "HoneyClient::DB"); 
    179 $config{pass} = getVar(name => "pass", namespace => "HoneyClient::DB"); 
    180 $config{dbname} = getVar(name => "dbname", namespace => "HoneyClient::DB"); 
    181  
    182 if (!db_exists(%config)) { 
     384$config{host}   = getVar( name => "host", namespace => "HoneyClient::DB" ); 
     385$config{port}   = getVar( name => "port", namespace => "HoneyClient::DB" ); 
     386$config{user}   = getVar( name => "user", namespace => "HoneyClient::DB" ); 
     387$config{pass}   = getVar( name => "pass", namespace => "HoneyClient::DB" ); 
     388$config{dbname} = getVar( name => "dbname", namespace => "HoneyClient::DB" ); 
     389 
     390if ( !db_exists(%config) ) { 
    183391    die; 
    184392} 
     
    188396} 
    189397 
     398=pod 
     399 
    190400=head1 METHODS 
    191401 
     
    194404=over 4 
    195405 
    196 =item new HoneyClient::DB 
    197  
    198 new Receives an unblessed hash, imports the schema (if necessary), checks that 
     406=item new 
     407 
     408Receives an unblessed hash, imports the schema (if necessary), checks that 
    199409required fields contain the proper data, and returns the blessed object. 
    200410 
     
    203413 
    204414  $my_obj = new HoneyClient::DB::SomeObj->new({ 
    205   field_a => "foo", 
    206   field_b => "bar" 
     415          field_a => "foo", 
     416          field_b => "bar" 
    207417  }); 
    208418 
     
    210420 
    211421sub new { 
    212     my ($class,$self) = @_; 
    213      
    214     bless($self,$class); 
     422    my ( $class, $self ) = @_; 
     423 
     424    bless( $self, $class ); 
     425 
    215426    # Check if Schema has been imported 
    216     if (!exists($_types{$class})) { 
    217         _import_schema($class); 
    218     } 
     427    _import_schema($class) if ( !exists( $_types{$class} ) ); 
     428 
    219429    # Make sure required Attributes are set. Fail if not. 
    220     my @missing = $self->_check_required(); 
    221     if (scalar @missing) { 
    222         Carp::croak("Object missing required attribute(s): ".join(', ',@missing).'.\n\t'); 
     430    my @missing = $self->_check_required(); 
     431    if ( scalar @missing ) { 
     432        $LOG->fatal( "Object missing required attribute(s): " 
     433              . join( ', ', @missing ) 
     434              . '.' ); 
     435        Carp::croak( "Object missing required attribute(s): " 
     436              . join( ', ', @missing ) 
     437              . '.\n' ); 
    223438    } 
    224439 
    225440    # Check if ref and array objects have been initialized. If not call new 
    226     foreach my $key (keys %$self){ 
    227         eval { 
    228             if ($self->{$key}) { 
    229                 $self->{$key} = $_check{$class}{$key}->($self->{$key}); 
     441    foreach my $key ( keys %$self ) { 
     442        eval { 
     443            if ( $self->{$key} ) 
     444            { 
     445                $self->{$key} = $_check{$class}{$key}->( $self->{$key} ); 
    230446            } 
    231         }; 
    232         if ($@) { 
    233             Carp::croak "Invalid Object!\n\t$@"; 
    234         } 
    235         if ($_types{$class}{$key} =~ m/(array|ref):(.*)/) { 
    236             my $ref = ref ($self->{$key}); 
    237             my $childType = $1; 
     447        }; 
     448        if ($@) { 
     449            $LOG->fatal("Invalid Object $key\t$@"); 
     450            Carp::croak "Invalid Object $key\n\t$@"; 
     451        } 
     452        if ( $_types{$class}{$key} =~ m/(array|ref):(.*)/ ) { 
     453            my $ref        = ref( $self->{$key} ); 
     454            my $childType  = $1; 
    238455            my $childClass = $2; 
    239             if ($childClass->can('new')) { 
    240                if( $ref eq 'HASH' and $childType eq 'ref') { 
    241                     $self->{$key} = $childClass->new($self->{$key}); 
     456            if ( $childClass->can('new') ) { 
     457                if ( $ref eq 'HASH' and $childType eq 'ref' ) { 
     458                    $self->{$key} = $childClass->new( $self->{$key} ); 
    242459                } 
    243                 if ($ref eq 'ARRAY' and $childType eq 'array') { 
    244                    foreach my $obj (@{$self->{$key}}) { 
    245                        $obj = $childClass->new($obj); 
    246                    } 
     460                if ( $ref eq 'ARRAY' and $childType eq 'array' ) { 
     461                    foreach my $obj ( @{ $self->{$key} } ) { 
     462                        $obj = $childClass->new($obj); 
     463                    } 
    247464                } 
    248            
     465           
    249466            else { 
    250                 Carp::croak "Invalid Object! $childType does not exist"; 
     467                $LOG->fatal("Invalid Object! $childType does not exist"); 
     468                Carp::croak "Invalid Object! $childType does not exist"; 
    251469            } 
    252470        } 
     
    258476 
    259477sub _check_required { 
    260     my $self = shift; 
     478    my $self = shift; 
    261479    my $class = ref $self; 
     480 
    262481    # make sure field is not undef if 'required' option is set 
    263     if (exists $_required{$class}) { 
    264         my @missing; 
    265         foreach ( keys %{$_required{$class}} ) { 
    266             push(@missing, $_) if (!defined($self->{$_}) or ($self->{$_} eq "")); 
    267         } 
    268         return @missing; 
     482    if ( exists $_required{$class} ) { 
     483        my @missing; 
     484        foreach ( keys %{ $_required{$class} } ) { 
     485            push( @missing, $_ ) 
     486              if ( !defined( $self->{$_} ) or ( $self->{$_} eq "" ) ); 
     487        } 
     488        return @missing; 
    269489    } 
    270490    return; 
     
    272492 
    273493sub _import_schema { 
    274     my $class = shift; 
    275     my $schema = \%{$class."::fields"}; 
     494    my $class  = shift; 
     495    my $schema = \%{ $class . "::fields" }; 
     496 
    276497    # Parase Attributes; store types and options. 
    277     while(my ($type,$attrib) = each(%$schema)) { 
     498    while ( my ( $type, $attrib ) = each(%$schema) ) { 
    278499        my $ref = ref $attrib; 
     500 
    279501        # Attributes in array format use default options 
    280         if($ref eq 'ARRAY') { 
    281             foreach (@{$attrib}) { 
    282                 $_types{$class}{$_}=$type; 
    283                 if ($type =~ m/(ref|array)/) { 
     502        if ( $ref eq 'ARRAY' ) { 
     503            foreach ( @{$attrib} ) { 
     504                $_types{$class}{$_} = $type; 
     505                if ( $type =~ m/(ref|array)/ ) { 
    284506                    delete $_types{$class}; 
    285                     Carp::croak "Invalid Object Type. ref AND array types must ". 
    286                         "be defined as a hash containing 'objclass'"; 
     507                    $LOG->fatal("Invalid Object Type. ref AND array types must " 
     508                      . "be defined as a hash containing 'objclass'"); 
     509                    Carp::croak "Invalid Object Type. ref AND array types must " 
     510                      . "be defined as a hash containing 'objclass'"; 
    287511                } 
    288                 $_check{$class}{$_} = $defaults{$type}{check_func} or 
    289                   $_check{$class}{$_} = \&check_nothing; 
     512                $_check{$class}{$_} = $defaults{$type}{check_func} 
     513                  or $_check{$class}{$_} = \&check_nothing; 
    290514            } 
    291515        } 
     516 
    292517        # Parse options for attributes in hash table format 
    293         elsif ($ref eq 'HASH') { 
    294             while (my ($a,$opts) = each %$attrib) { 
     518        elsif ( $ref eq 'HASH' ) { 
     519            while ( my ( $a, $opts ) = each %$attrib ) { 
    295520                $_types{$class}{$a} = $type; 
    296                 if ($opts->{required}) { 
     521                if ( $opts->{required} ) { 
    297522                    $_required{$class}{$a} = 1; 
    298523                } 
     524 
    299525                # array and ref types require the objclass option 
    300                 if ($type =~ m/^(array|ref)$/) { 
    301                    die "$1 of unknown class: $a" if(!exists $opts->{objclass}); 
    302                     if (!exists $_types{$opts->{objclass}}) { 
    303                         _import_schema($opts->{objclass})
     526                if ( $type =~ m/^(array|ref)$/ ) { 
     527                    if ( !exists $opts->{objclass} ) { 
     528                        $LOG->fatal("$1 of unknown class: $a"); 
     529                        Carp::croak "$1 of unknown class: $a"
    304530                    } 
    305                     $_types{$class}{$a} .= ':'.$opts->{objclass}; 
     531                    if ( !exists $_types{ $opts->{objclass} } ) { 
     532                        _import_schema( $opts->{objclass} ); 
     533                    } 
     534                    $_types{$class}{$a} .= ':' . $opts->{objclass}; 
    306535                } 
     536 
    307537                # Check function will ensure data is of proper format 
    308                 if ($opts->{check_func}) { 
     538                if ( $opts->{check_func} ) { 
    309539                    $_check{$class}{$a} = $opts->{check_func}; 
    310540                } 
    311541                else { 
    312                     $_check{$class}{$a} = $defaults{$type}{check_func} or 
    313                       $_check{$class}{$a} = \&check_nothing; 
     542                    $_check{$class}{$a} = $defaults{$type}{check_func} 
     543                      or $_check{$class}{$a} = \&check_nothing; 
    314544                } 
     545 
    315546                # key option determines if attribute is an index 
    316                 if ($opts->{key}) { 
    317                    $_keys{$class}{$a} = $opts->{key}; 
     547                if ( $opts->{key} ) { 
     548                    $_keys{$class}{$a} = $opts->{key}; 
    318549                } 
    319                 if ($opts->{init_val}) { 
    320                    $_init_val{$class}{$a} = $opts->{key}; 
     550                if ( $opts->{init_val} ) { 
     551                    $_init_val{$class}{$a} = $opts->{key}; 
    321552                } 
    322553            } 
    323554        } 
    324555        else { 
    325             Carp::carp("$class\{$type\} is defined improperly"); 
    326         } 
    327     } 
     556            $LOG->warn("$class\{$type\} is defined improperly"); 
     557        } 
     558    } 
     559 
    328560    # Add the table to the DB if necessary 
    329561    # TODO: Move to install script?? 
    330     $class->deploy_table() or Carp::croak("${class}->_import_schema: ". 
    331         "Failed to deploy table"); 
     562    if (!$class->deploy_table()) { 
     563        $LOG->fatal("${class}->_import_schema: " . "Failed to deploy table"); 
     564        Carp::croak("${class}->_import_schema: " . "Failed to deploy table"); 
     565    } 
    332566} 
    333567 
     
    345579  $my_obj->insert(); 
    346580 
     581B<Input> 
     582 
     583There are no parameters, however the calling object is used as input for the 
     584insert operation. 
     585 
     586B<Return Value> 
     587 
     588Returns the 'id' of the (parent) object inserted. 
     589 
    347590=cut 
    348591 
    349592sub insert { 
    350593    my $obj = shift; 
    351     my $id = undef; 
    352      
     594    my $id = undef; 
     595 
    353596    $dbhandle = HoneyClient::DB::_connect(%config); 
     597 
    354598    # Attempt insert; commit if succeeds, else rollback 
    355     eval { 
    356         $id = _insert($obj, undef); 
    357     }; 
    358     if($@) { 
    359         Carp::carp ("insert failed, Rolling Back: $@");  
    360         $dbhandle->rollback(); 
     599    $LOG->debug("Attempting insert operation."); 
     600    eval { $id = _insert( $obj, undef ); }; 
     601    if ($@) { 
     602        $LOG->warn("insert failed, Rolling Back: $@"); 
     603        $dbhandle->rollback(); 
    361604    } 
    362605    else { 
    363        $dbhandle->commit(); 
    364     } 
    365     print "\n" if ($debug); 
     606        $dbhandle->commit(); 
     607    } 
    366608    $dbhandle->disconnect() if $dbhandle; 
    367609    return $id; 
     
    371613 
    372614sub _insert { 
    373     my ($obj, $fk_col, $fk_id) = @_; 
     615    my ( $obj, $fk_col, $fk_id ) = @_; 
    374616    my $ref = ref $obj; 
    375617 
    376     if ($ref eq 'ARRAY') { 
    377         return _insert_array($obj, $fk_col, $fk_id); 
    378     } 
    379     elsif (exists $_types{$ref}) { 
    380         return _insert_obj($obj, $fk_col, $fk_id); 
     618    if ( $ref eq 'ARRAY' ) { 
     619        return _insert_array( $obj, $fk_col, $fk_id ); 
     620    } 
     621    elsif ( exists $_types{$ref} ) { 
     622        return _insert_obj( $obj, $fk_col, $fk_id ); 
    381623    } 
    382624    elsif ($ref) { 
    383         Carp::carp ("Can't insert object of type $ref"); 
     625        $LOG->warn("Can't insert object of type $ref"); 
    384626    } 
    385627    else { 
    386         Carp::carp ("Attempted to insert scalar value into the database"); 
     628        $LOG->warn("Attempted to insert scalar value into the database"); 
    387629    } 
    388630    return undef; 
    389631} 
     632 
    390633sub _insert_array { 
    391     my ($obj, $fk_col, $fk_id) = @_; 
     634    my ( $obj, $fk_col, $fk_id ) = @_; 
    392635    my @entries; 
    393636    foreach (@$obj) { 
    394         my $id = _insert($_, $fk_col, $fk_id); 
    395         ref ($id) eq 'ARRAY' ? push(@entries,@$id) : push(@entries,$id); 
    396     }#} 
     637        my $id = _insert( $_, $fk_col, $fk_id ); 
     638        ref($id) eq 'ARRAY' ? push( @entries, @$id ) : push( @entries, $id ); 
     639    }    #} 
    397640    return \@entries; 
    398641} 
     642 
    399643sub _insert_obj { 
    400     my ($obj, $fk_col, $fk_id) = @_; 
    401     my ($class, $table) = (ref($obj), _get_table($obj)); 
    402     my ($id, %insert, %index, %children); 
    403     # Process object attributes 
    404     while (my ($col,$data) = each %$obj) { 
    405         if (!$_types{$cla