Changeset 601
- Timestamp:
- 06/21/07 16:37:41 (1 year ago)
- Files:
-
- honeyclient/trunk/bin/install_honeyclient_db.pl (modified) (1 diff, 1 prop)
- honeyclient/trunk/etc/honeyclient.xml (modified) (1 diff)
- honeyclient/trunk/etc/honeyclient_log.conf (modified) (1 diff)
- honeyclient/trunk/lib/HoneyClient/DB.pm (modified) (21 diffs, 1 prop)
- honeyclient/trunk/lib/HoneyClient/DB/File.pm (modified) (2 diffs, 1 prop)
- honeyclient/trunk/lib/HoneyClient/DB/Fingerprint.pm (modified) (1 prop)
- honeyclient/trunk/lib/HoneyClient/DB/Note.pm (modified) (2 diffs, 1 prop)
- honeyclient/trunk/lib/HoneyClient/DB/Regkey.pm (modified) (1 diff, 1 prop)
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 2 5 use strict; 6 use warnings; 7 use Carp (); 3 8 9 use ExtUtils::MakeMaker qw(prompt); 10 use Net::IP qw(ip_is_ipv4); 4 11 use DBI; 12 use DBI::Const::GetInfoType; 5 13 use HoneyClient::Util::Config qw(getVar); 6 14 7 # Retrieve values from HoneyClient config file 15 print "************************************************\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. 8 29 my $host = getVar(name => "host", namespace => "HoneyClient::DB"); 9 30 my $user = getVar(name => "user", namespace => "HoneyClient::DB"); 10 31 my $pass = getVar(name => "pass", namespace => "HoneyClient::DB"); 11 32 my $database_name = getVar(name => "dbname", namespace => "HoneyClient::DB"); 33 my ($question, $root_password); 12 34 13 print "Attempting to Create a HoneyClient database with the name: ". 14 "${database_name}\n"; 15 my $r_pass = retrieve_pw(); 35 print "The following database configuration was found:\n\n"; 16 36 17 my $dsn = "DBI:mysql:database=mysql;host=".$host; 37 my $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 42 print $buf . "\n"; 43 44 $question = prompt("Is this correct?", "yes"); 45 print "\n"; 46 47 if ($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. 54 system("stty -echo"); 55 $root_password = prompt("Please enter your database 'root' password:"); 56 system("stty echo"); 57 print "\n"; 58 print "\n"; 59 60 my $sql = undef; 61 my $dsn = "DBI:mysql:database=mysql;host=" . $host; 18 62 19 63 eval { 20 64 # 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); 32 135 33 136 $dbh->disconnect() if $dbh; 34 137 }; 35 138 if ($@) { 36 die "Failed to initialize Database Connection:\n\t$@"; 139 Carp::croak("Installation Failed: " . $@); 140 } 141 142 sub 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 } 37 150 } 38 151 39 152 print "Database and user installed successfully.\n"; 40 41 # Retrieve a password from the user while hiding it from the display42 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 301 301 </enable> 302 302 <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.1303 172.16.164.1 304 304 </host> 305 305 <dbname description="The name of the HoneyClient database." default="HoneyClient"> honeyclient/trunk/etc/honeyclient_log.conf
r409 r601 63 63 # Screen Logging Settings 64 64 #log4perl.logger.HoneyClient.Agent.Integrity.Registry=DEBUG, Screen 65 #log4perl.logger.HoneyClient.DB=DEBUG, Screen 65 66 # Suppress Parser Debugging Messages 66 67 #log4perl.logger.HoneyClient.Agent.Integrity.Registry.Parser=INFO, Screen honeyclient/trunk/lib/HoneyClient/DB.pm
- Property svn:executable deleted
r521 r601 3 3 # Package: HoneyClient::DB 4 4 # 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. 6 7 # 7 8 # CVS: $Id$ … … 15 16 # as published by the Free Software Foundation, using version 2 16 17 # of the License. 17 # 18 # 18 19 # This program is distributed in the hope that it will be useful, 19 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 22 # GNU General Public License for more details. 22 # 23 # 23 24 # You should have received a copy of the GNU General Public License 24 25 # along with this program; if not, write to the Free Software … … 28 29 ####################################################################### 29 30 31 =pod 32 30 33 =head1 NAME 31 34 32 HoneyClient::DB - Abstract HoneyClient Database Class 35 HoneyClient::DB - Perl extension to provide an abstract interface for 36 storing HoneyClient data into a database. 37 38 =head1 VERSION 39 40 This documentation refers to HoneyClient::DB version 0.97. 33 41 34 42 =head1 SYNOPSIS 35 43 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 44 As 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). 40 49 use HoneyClient::DB; 41 50 package HoneyClient::DB::SuperHero::Ability; 42 51 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). 60 71 package HoneyClient::DB::SuperHero; 61 72 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. 82 126 83 127 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 => [ 92 142 { 93 name => 'Super Strength',94 description => 'More powerful than a locomotive',143 name => 'Flight', 144 description => "It's a bird, it's a plane.", 95 145 }, 96 146 { 97 name => 'Flight',98 description => 'It's a bird, it's a plane...',147 name => 'Heat Vision', 148 recharge_time => 5, # in seconds 99 149 }, 100 { 101 name => 'Heat Vision', 102 recharge_time => 5, 103 }, 104 ] 150 ], 105 151 }; 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(); 108 160 109 $hero->insert(); 110 161 # Retrieve the superhero. 111 162 my $filter = { 112 163 name => 'Superman', 113 164 }; 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"; 116 176 117 177 =head1 DESCRIPTION 118 178 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. 179 This library is an abstract class used to access and store HoneyClient 180 within a database. The class is not to be used directly, but can be inherited 181 by sub-classes, in order to indirectly store specific types of data into a 182 database. 183 184 B<Note>: Any calls made to this library will fail, if a database is not properly 185 described in the <HoneyClient/><DB/> section of the etc/honeyclient.xml 186 configuration file or if the library cannot establish a connection to the 187 database. 188 189 =head2 SCHEMA DEFINITION 190 191 The schema for a HoneyClient::DB subclass is created from the B<%fields> 192 variable, a multi-level hash table. 193 194 =head3 FIRST LEVEL: DATA TYPE 195 196 The keys at the first level of B<%fields> define the data types to be used 197 for each column, which are named as keys in the second level. The following 198 is a list of acceptable data types: 199 200 =over 4 201 202 =item * B<'int'> 203 204 An integer. 205 206 =item * B<'string'> 207 208 A string no longer than 255 characters. 209 210 =item * B<'text'> 211 212 A string no longer than 65,535 characters. 213 214 =item * B<'timestamp'> 215 216 An ISO8601 compliant timestamp (i.e., 'MMDDYYYY HH:MM:SS'). 217 218 =item * B<'array'> 219 220 An array. Used to represent one-to-many relationships. 221 222 B<Note>: If this type is specified, then the L<'objclass'> option 223 must be set within each column name. 224 225 =item * B<'ref'> 226 227 A reference. Used to represent one-to-one relationships. 228 229 B<Note>: If this type is specified, then the L<'objclass'> option 230 must be set within each column name. 231 232 =back 233 234 =head3 SECOND LEVEL: COLUMN NAMES 235 236 Column names are defined as keys in the second level of B<%fields>. If 237 each column does not need any special options (e.g., making the column 238 required), then an array reference can hold all the column names. 239 For 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 249 However, if some of the columns need special options set (e.g., making 'col_b' 250 required), 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 264 If needed, options are defined in the third level of B<%fields>. These 265 options are described as follows: 266 267 =over 4 268 269 =item * B<'check_func'> 270 271 If defined, then this contains a reference to a subroutine that will 272 verify the actual column data is in a proper format. This overrides the 273 default internal check function for the data type of that column. 274 275 =item * B<'init_val'> 276 277 If defined, then this value will be the default value that the database 278 will assign to the column, if empty data is inserted into this column. 279 280 =item * B<'key'> 281 282 If defined, then this value creates an index for the column. 283 Possible values are: 284 285 =over 4 286 287 =item * B<$HoneyClient::DB::KEY_INDEX> 288 289 If set, an index will be created in the database to improve the search 290 time of this column. This option is only recommended for very frequently 291 searched columns. 292 293 =item * B<$HoneyClient:DB::KEY_UNIQUE> 294 295 If set, a UNIQUE index is created for the column. If a record is inserted that 296 has a match with this column in a previously existing record, the insert will 297 fail on the database side, but the 'id' of that existing record will be 298 returned. 299 300 =item * B<$HoneyClient:DB::KEY_UNIQUE_MULT> 301 302 If set, the column is added to a UNIQUE index comprised of all other columns 303 with the KEY_UNIQUE_MULT key option. This index is used to ensure ALL VALUES for 304 the columns in the index are distinct. An insert of a record matching an 305 existing record will return the ID of that record. 306 307 =back 308 309 =item * B<'objclass'> 310 311 This option is required and only used by the L<'array'> and L<'ref'> 312 data types. The value should be a string which contains the package name of 313 the schema to include as a child. 314 315 =item * B<'required'> 316 317 If defined and set to 1, then this option will cause all subsequent 318 B<HoneyClient::DB::*-E<gt>L<new>($data)> operations to fail, if the B<$data> 319 does not contain the required field. 320 321 =back 322 323 =head1 DATABASE CONFIGURATION 324 325 This library expects to connect to a MySQL v5.0 or greater database. 326 To specify which database this library should use, see the 327 <HoneyClient/><DB/> section of the etc/honeyclient.xml configuration file 328 for further details. 132 329 133 330 =cut … … 135 332 package HoneyClient::DB; 136 333 334 use Data::Dumper qw(Dumper); 137 335 use strict 'vars', 'subs'; 138 336 use warnings; 139 337 140 338 BEGIN { 339 141 340 #Dependencies 142 341 use DBI; … … 144 343 use HoneyClient::Util::Config; 145 344 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; 149 349 150 350 # Traps signals, allowing END: blocks to perform cleanup. 151 351 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. 153 353 154 354 #Globals 155 our @ISA = qw(Exporter);355 our @ISA = qw(Exporter); 156 356 our @EXPORT = qw(); 157 357 our @EXPORT_OK; 158 358 our $VERSION = 0.9; 159 359 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. 364 our $LOG = get_logger(); 162 365 163 366 our $dbhandle; 367 164 368 # To be used ONLY INTERNALLY! 165 our (%_types,%_check,%_required,%_init_val,%_keys,%defaults); 369 our ( %_types, %_check, %_required, %_init_val, %_keys, %defaults ); 370 166 371 # %fields must be defined by all children classes 167 372 our %fields; 168 373 169 374 #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 375 our ( $STATUS_DELETED, $STATUS_ADDED, $STATUS_MODIFIED ) = 376 ( 0, 1, 2 ); # Integrity status field 377 our ( $KEY_INDEX, $KEY_UNIQUE, $KEY_UNIQUE_MULT ) = 378 ( 0, 1, 2 ); # Uniqueness of Attributes 172 379 our $debug = 0; 380 173 381 # Initialize Connection 174 382 our %config; 175 383 $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 390 if ( !db_exists(%config) ) { 183 391 die; 184 392 } … … 188 396 } 189 397 398 =pod 399 190 400 =head1 METHODS 191 401 … … 194 404 =over 4 195 405 196 =item new HoneyClient::DB197 198 newReceives an unblessed hash, imports the schema (if necessary), checks that406 =item new 407 408 Receives an unblessed hash, imports the schema (if necessary), checks that 199 409 required fields contain the proper data, and returns the blessed object. 200 410 … … 203 413 204 414 $my_obj = new HoneyClient::DB::SomeObj->new({ 205 field_a => "foo",206 field_b => "bar"415 field_a => "foo", 416 field_b => "bar" 207 417 }); 208 418 … … 210 420 211 421 sub new { 212 my ($class,$self) = @_; 213 214 bless($self,$class); 422 my ( $class, $self ) = @_; 423 424 bless( $self, $class ); 425 215 426 # 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 219 429 # 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' ); 223 438 } 224 439 225 440 # 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} ); 230 446 } 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; 238 455 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} ); 242 459 } 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 } 247 464 } 248 }465 } 249 466 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"; 251 469 } 252 470 } … … 258 476 259 477 sub _check_required { 260 my $self = shift;478 my $self = shift; 261 479 my $class = ref $self; 480 262 481 # 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; 269 489 } 270 490 return; … … 272 492 273 493 sub _import_schema { 274 my $class = shift; 275 my $schema = \%{$class."::fields"}; 494 my $class = shift; 495 my $schema = \%{ $class . "::fields" }; 496 276 497 # Parase Attributes; store types and options. 277 while (my ($type,$attrib) = each(%$schema)) {498 while ( my ( $type, $attrib ) = each(%$schema) ) { 278 499 my $ref = ref $attrib; 500 279 501 # 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)/ ) { 284 506 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'"; 287 511 } 288 $_check{$class}{$_} = $defaults{$type}{check_func} or289 $_check{$class}{$_} = \&check_nothing;512 $_check{$class}{$_} = $defaults{$type}{check_func} 513 or $_check{$class}{$_} = \&check_nothing; 290 514 } 291 515 } 516 292 517 # 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 ) { 295 520 $_types{$class}{$a} = $type; 296 if ( $opts->{required}) {521 if ( $opts->{required} ) { 297 522 $_required{$class}{$a} = 1; 298 523 } 524 299 525 # 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"; 304 530 } 305 $_types{$class}{$a} .= ':'.$opts->{objclass}; 531 if ( !exists $_types{ $opts->{objclass} } ) { 532 _import_schema( $opts->{objclass} ); 533 } 534 $_types{$class}{$a} .= ':' . $opts->{objclass}; 306 535 } 536 307 537 # Check function will ensure data is of proper format 308 if ( $opts->{check_func}) {538 if ( $opts->{check_func} ) { 309 539 $_check{$class}{$a} = $opts->{check_func}; 310 540 } 311 541 else { 312 $_check{$class}{$a} = $defaults{$type}{check_func} or313 $_check{$class}{$a} = \&check_nothing;542 $_check{$class}{$a} = $defaults{$type}{check_func} 543 or $_check{$class}{$a} = \&check_nothing; 314 544 } 545 315 546 # 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}; 318 549 } 319 if ( $opts->{init_val}) {320 $_init_val{$class}{$a} = $opts->{key};550 if ( $opts->{init_val} ) { 551 $_init_val{$class}{$a} = $opts->{key}; 321 552 } 322 553 } 323 554 } 324 555 else { 325 Carp::carp("$class\{$type\} is defined improperly"); 326 } 327 } 556 $LOG->warn("$class\{$type\} is defined improperly"); 557 } 558 } 559 328 560 # Add the table to the DB if necessary 329 561 # 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 } 332 566 } 333 567 … … 345 579 $my_obj->insert(); 346 580 581 B<Input> 582 583 There are no parameters, however the calling object is used as input for the 584 insert operation. 585 586 B<Return Value> 587 588 Returns the 'id' of the (parent) object inserted. 589 347 590 =cut 348 591 349 592 sub insert { 350 593 my $obj = shift; 351 my $id = undef;352 594 my $id = undef; 595 353 596 $dbhandle = HoneyClient::DB::_connect(%config); 597 354 598 # 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(); 361 604 } 362 605 else { 363 $dbhandle->commit(); 364 } 365 print "\n" if ($debug); 606 $dbhandle->commit(); 607 } 366 608 $dbhandle->disconnect() if $dbhandle; 367 609 return $id; … … 371 613 372 614 sub _insert { 373 my ( $obj, $fk_col, $fk_id) = @_;615 my ( $obj, $fk_col, $fk_id ) = @_; 374 616 my $ref = ref $obj; 375 617 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 ); 381 623 } 382 624 elsif ($ref) { 383 Carp::carp("Can't insert object of type $ref");625 $LOG->warn("Can't insert object of type $ref"); 384 626 } 385 627 else { 386 Carp::carp("Attempted to insert scalar value into the database");628 $LOG->warn("Attempted to insert scalar value into the database"); 387 629 } 388 630 return undef; 389 631 } 632 390 633 sub _insert_array { 391 my ( $obj, $fk_col, $fk_id) = @_;634 my ( $obj, $fk_col, $fk_id ) = @_; 392 635 my @entries; 393 636 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 } #} 397 640 return \@entries; 398 641 } 642 399 643 sub _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
