From eae584cf140b1f7128073a377ba9a13a0ac99211 Mon Sep 17 00:00:00 2001 From: Will Budicm Date: Sat, 2 Jan 2021 02:19:13 +1100 Subject: [PATCH] Bug 34 fix. --- Current Development Check List.md | 2 +- ReleaseTestCheckupList-v-2.2.md | 2 +- htdocs/cgi-bin/config.cgi | 330 +++++++++++++++------- htdocs/cgi-bin/data.cgi | 19 +- htdocs/cgi-bin/json.cgi | 68 +++-- htdocs/cgi-bin/login_ctr.cgi | 10 +- htdocs/cgi-bin/main.cgi | 20 +- htdocs/cgi-bin/system/modules/Settings.pm | 44 +-- htdocs/cgi-bin/testSettings.pl | 166 ++++++----- htdocs/cgi-bin/wsrc/main.js | 1 + 10 files changed, 418 insertions(+), 244 deletions(-) diff --git a/Current Development Check List.md b/Current Development Check List.md index 33bb69d..96b3236 100644 --- a/Current Development Check List.md +++ b/Current Development Check List.md @@ -6,7 +6,7 @@ ### v.2.2 EARTH RC 1 -* Bug 34 - DB fix in config, removes associated RTF documents to logs, for some reason. +* ✔ Bug 34 - DB fix in config, removes associated RTF documents to logs, for some reason. * Page section plugins. * Configured in main.cnf. * Accessed via side menu to appear. diff --git a/ReleaseTestCheckupList-v-2.2.md b/ReleaseTestCheckupList-v-2.2.md index f811f55..c827158 100644 --- a/ReleaseTestCheckupList-v-2.2.md +++ b/ReleaseTestCheckupList-v-2.2.md @@ -39,7 +39,7 @@ ### Config Page config.cgi -- ☑ Look & Feel, autonom variables, like release version showing at end of System Configuration, clustered as uneditable. +- ☑ Look & Feel, autonom variables, like release version showing at end of System Configuration, clustered as edit enabled. - ☑ Editing and changing works. - ☐ Categories, adding, deleting, renaming works. - ☐ Database fixes all work. diff --git a/htdocs/cgi-bin/config.cgi b/htdocs/cgi-bin/config.cgi index b63228c..f18bf1b 100755 --- a/htdocs/cgi-bin/config.cgi +++ b/htdocs/cgi-bin/config.cgi @@ -13,6 +13,7 @@ use Syntax::Keyword::Try; use DateTime::Format::SQLite; use Date::Language; +use Capture::Tiny ':all'; use Text::CSV; use Scalar::Util qw(looks_like_number); use Sys::Syslog qw(:DEFAULT :standard :macros); #openLog, closelog macros @@ -808,7 +809,6 @@ sub encryptPassw { } - sub processDBFix { my $rs_syst = $cgi->param("reset_syst"); @@ -822,16 +822,20 @@ sub processDBFix { try{ - my %dates = (); - my @dlts = (); - #Hash is unreliable for returning sequential order of keys so array must do. - my @updts = (); - my $cntr_del =0; - my $existing; - my @row; + my %dates = (); + #Hash is unreliable for returning sequential order of keys so array must do. + my @dlts = (); + my $cntr_del =0; + my $existing; + my @row; + + getHeader() if(&Settings::debug); + print "

Database Records Fix Result

\n
" if(&Settings::debug); + print "
Started transaction!\n" if(&Settings::debug);
 
         $db->do('BEGIN TRANSACTION;');
-        #Check for duplicates, which are possible during imports or migration as internal rowid is not primary in log.
+        # Check for duplicates, which are possible during imports or migration as internal rowid is not primary in log.
+        # @TODO This should be selecting an cross SQL compatibe view.
         if(Settings::isProgressDB()){
             $dbs = Settings::selectRecords($db, 'SELECT ID, DATE FROM LOG ORDER BY DATE;');
         }else{
@@ -844,7 +848,6 @@ try{
             }
             else{
                 $dates{$row[0]} = $row[1];
-                $updts[$cntr_upd++] = $row[0];
             }
         }
 
@@ -854,29 +857,32 @@ try{
              }else{
                  $sql = "DELETE FROM LOG WHERE ID=$del;";
              }
-                    #print "$sql\n
"; - my $st_del = $db->prepare($sql); - $st_del->execute(); + print "$sql\n
"; + my $st_del = $db->prepare($sql); + $st_del->execute(); } - + print "Doing renumerate next...\n" if(&Settings::debug); &renumerate; + print "done!\n"; + print "Doing removeOldSessions next..." if(&Settings::debug); &Settings::removeOldSessions; + print "done!\n " if(&Settings::debug); &resetCategories if $rs_cats; &resetSystemConfiguration($db) if $rs_syst; &wipeSystemConfiguration if $wipe_ss; - - - $db->do('COMMIT;'); - $db->disconnect(); + $db->do('COMMIT;')if(&Settings::debug); + print "Commited ALL!
"if(&Settings::debug); + # $db->disconnect(); $db = Settings::connectDB(); - $dbs = $db->do("VACUUM;"); - - + $dbs = $db->do("VACUUM;")if(&Settings::debug); + print "Issued VACUUM!
"if(&Settings::debug); + if($LOGOUT){ &logout; } + exit if(&Settings::debug); } catch{ @@ -885,6 +891,99 @@ catch{ } } +sub renumerate { + + # NOTE: This is most likelly all performed under an transaction. + my $sql; + # Fetch list by date identified rtf attached logs, with possibly now an old LID, to be updated to new one. + if(Settings::isProgressDB()){ + $sql = "SELECT ID, DATE FROM LOG WHERE RTF > 0;" + }else{ + $sql = "SELECT rowid, DATE FROM LOG WHERE RTF > 0;" + } + my @row = Settings::selectRecords($db, $sql)->fetchrow_array(); + my %notes = (); + if (scalar @row > 0){ + $notes{$row[1]} = $row[0]; #<- This is current LID, will change. + print "Expecting Note entry for ".$row[1]." LOG.ID[".$row[0]."]<- LID...\n"; + } + + ### RENUMERATE LOG + $db->do("CREATE TABLE life_log_temp_table AS SELECT * FROM LOG;"); + if(Settings::isProgressDB()){ + $db->do('DROP TABLE LOG CASCADE;'); + } + else{ + $db->do('DROP TABLE LOG;'); + } + $db->do(&Settings::createLOGStmt); + $db->do(q(INSERT INTO LOG (ID_CAT, DATE, LOG, RTF, AMOUNT,AFLAG) + SELECT ID_CAT, DATE, LOG, RTF, AMOUNT, AFLAG FROM life_log_temp_table ORDER by DATE;)); + $db->do('DROP TABLE life_log_temp_table;'); + ### + + # Update notes with new log id, if it changed. + + foreach my $date (keys %notes){ + my $old = $notes{$date}; + #my $sql_date = DateTime::Format::SQLite->parse_datetime($date); + + if(Settings::isProgressDB()){ + $sql = "SELECT ID FROM LOG WHERE RTF > 0 AND DATE = '".$date."';"; + }else{ + $sql = "SELECT rowid FROM LOG WHERE RTF > 0 AND DATE = '".$date."';"; + } + print "Selecting -> $sql\n"; + $dbs = Settings::selectRecords($db, $sql); + @row = $dbs->fetchrow_array(); + if(scalar @row > 0){ + my $new = $row[0]; + if($new ne $old){ + $db->do("UPDATE NOTES SET LID =$new WHERE LID=$old;"); + print "Updated Note in LID[$old] to be LID[$new]\n"; + } + else{ + print "All fine skipping LID[$new]\n"; + } + }else{ + print "ERROR NOT FOUND: $date for LID:$old!\n"; + } + + } + + # Delete Orphaned Notes entries if any? + if(Settings::isProgressDB()){ + $dbs = Settings::selectRecords($db, "SELECT LID, LOG.ID from NOTES LEFT JOIN LOG ON + NOTES.LID = LOG.ID WHERE LOG.ID is NULL;"); + }else{ + $dbs = Settings::selectRecords($db, "SELECT LID, LOG.rowid from NOTES LEFT JOIN LOG ON + NOTES.LID = LOG.rowid WHERE LOG.rowid is NULL;"); + } + if ($dbs) { foreach (@row = $dbs->fetchrow_array()) { + $db->do("DELETE FROM NOTES WHERELID=$row[0];") if $row[0]; # 0 is the place keeper for the shared zero record. + }} + +} + + + + + + + + + + + + + + + + + + + + sub resetCategories { $db->do("DELETE FROM CAT;"); $db->do("DROP TABLE CAT;"); @@ -1036,23 +1135,51 @@ sub backup { while(<$TAR>){print $_;} close $TAR; exit; - } +package DBMigStats { + + + sub new { + my $class = shift; + my $self = bless {cats_ins => 0, cats_upd => 0, logs_ins => 0, logs_upd => 0, notes => 0}, $class; + } + + sub cats_inserts(){my $s = shift;return $s->{cats_ins}} + sub cats_inserts_incr() {my $s = shift; $s->{cats_ins}++} + sub cats_updates(){my $s = shift;return $s->{cats_upd}} + sub cats_updates_incr() {my $s = shift; $s->{cats_upd}++} + + sub logs_inserts(){my $s = shift;return $s->{logs_ins}} + sub logs_inserts_incr(){my $s = shift; $s->{logs_ins}++} + sub logs_updates(){my $s = shift;return $s->{logs_upd}} + sub logs_updates_incr(){my $s = shift; $s->{logs_upd}++} + + sub notes() {my $s = shift;return $s->{notes}} + sub notes_incr() {my $s = shift; $s->{notes}++} + +} sub restore { + my $file = shift; - my ($tar,$pipe,@br); + my ($tar,$pipe,@br,$stdout,$b_db); my $pass = Settings::pass(); my $hndl = $cgi->param('data_bck'); my $dbck = &Settings::logPath."bck/"; `mkdir $dbck` if (!-d $dbck); + try{ getHeader(); print $cgi->start_html; + +my $stdout = capture_stdout { + + print "

Restore Result

\n
"; + print "Restore started: ".Settings::today(), "\n"; if($file){ #Open handle on server to backup to be restored. my $f = &Settings::logPath.$file; open($hndl, '<', $f) or die "Can't open $f: $!"; - print "
Reading on server -> $file
"; + print "
Reading on server backup file -> $file
"; $tar = $dbck.$file; } else{ @@ -1067,72 +1194,94 @@ sub restore { close $pipe; close $hndl; print "
\n";
+        
+
         my $m1 = "it is not permitted to restore another aliases log backup.";
         $m1= "has your log password changed?" if ($tar=~/_data_$alias/);
 
         my $cmd = `tar tvf $tar 2>/dev/null` 
          or die qq(, possible an security issue, $m1\nBACKUP FILE INVALID! $tar\nYour data alias is: $alias\nYour LifeLog version is:), Settings::release()."\n";
 
-        print "Contents->".$cmd."\n\n";
+        print "Contents->\n".$cmd."\n\n";
         $cmd = `tar xzvf $tar -C $dbck --strip-components 1 2>/dev/null` or die "Failed extracting $tar";
         print "Extracted->\n".$cmd."\n" or die "Failed extracting $tar";;
 
         my $b_base = $dbck.'data_'.$dbname.'_log.db'; 
         my $dsn= "DBI:SQLite:dbname=$b_base";
-        my $b_db = DBI->connect($dsn, $alias, $pass, { RaiseError => 1 }) or LifeLogException->throw(error=>"Invalid database! $dsn->$hndl [$@]", show_trace=>&Settings::debug);
+        $b_db = DBI->connect($dsn, $alias, $pass, { RaiseError => 1 }) or LifeLogException->throw(error=>"Invalid database! $dsn->$hndl [$@]", show_trace=>&Settings::debug);
         print "Connected to -> $dsn\n";
 
-        print "Merging from backup categories table...\n";
+        print "Merging from backup categories table...";
+        my $stats =  DBMigStats -> new();
         my $insCAT   = $db->prepare('INSERT INTO CAT (ID, NAME, DESCRIPTION) VALUES(?,?,?);') or die "Failed CAT prepare.";
         my $b_pst = Settings::selectRecords($b_db,'SELECT ID, NAME, DESCRIPTION FROM CAT;');
+        
         while ( @br = $b_pst->fetchrow_array() ) {
-            my $pst = Settings::selectRecords($db, "SELECT ID,NAME,DESCRIPTION FROM CAT WHERE ID=".$br[0].";");
+            my $pst = Settings::selectRecords($db, "SELECT ID, NAME, DESCRIPTION FROM CAT WHERE ID=".$br[0].";");
             my @ext = $pst->fetchrow_array();
             if(scalar(@ext)==0){
                 $insCAT->execute($br[0],$br[1],$br[2]);
-                print "Added CAT->".$br[0]."|".$br[1]."\n";
+                print "\nAdded CAT->".$br[0]."|".$br[1]; $stats->DBMigStats::cats_inserts_incr();
             }
             elsif($br[0] ne $ext[0] or $br[1] ne $ext[1]){
                 $db->do("UPDATE CAT SET NAME='".$br[1]."', DESCRIPTION='".$br[2]."' WHERE ID=$br[0];") or die "Cat update failed!";
-                print "Updated->".$br[0]."|".$br[1]."|".$br[2]."\n";
+                print "\nUpdated->".$br[0]."|".$br[1]."|".$br[2]; $stats->DBMigStats::cats_updates_incr();
             }
 
         }
+        
         print "\nFinished with merging CAT table.\n";
+        print "There where -> ". $stats->cats_inserts(). " inserts, and ". $stats->cats_updates(). " updates.\n";
 
         print "\n\nMerging from backup LOG table...\n";
-        my $insLOG   = $db->prepare('INSERT INTO LOG (ID_CAT, ID_RTF, DATE, LOG, AMOUNT, AFLAG, STICKY) VALUES(?,?,?,?,?,?,?);')or die "Failed LOG prepare.";
+        my %backupLIDS =();
+        my $insLOG   = $db->prepare('INSERT INTO LOG (ID_CAT, DATE, LOG, RTF, AMOUNT, AFLAG, STICKY) VALUES(?,?,?,?,?,?,?);')or die "Failed LOG prepare.";
 
-        $b_pst = Settings::selectRecords($b_db,'SELECT ID, ID_CAT, ID_RTF, DATE, LOG, AMOUNT, AFLAG, STICKY FROM '.Settings->VW_LOG);
+        $b_pst = Settings::selectRecords($b_db,'SELECT ID, ID_CAT, DATE, LOG, RTF, AMOUNT, AFLAG, STICKY FROM '.Settings->VW_LOG);
         while ( @br = $b_pst->fetchrow_array() ) {
-            my $pst = Settings::selectRecords($db,"SELECT DATE FROM ".Settings->VW_LOG." WHERE DATE='".$br[3]."';");
+            my $pst = Settings::selectRecords($db,"SELECT DATE FROM ".Settings->VW_LOG." WHERE DATE='".$br[2]."';");
             my @ext = $pst->fetchrow_array();
             if(scalar(@ext)==0){
                 $insLOG->execute($br[1],$br[2],$br[3],$br[4],$br[5],$br[6],$br[7]);
-                print "Added->".$br[0]."|".$br[3]."|".$br[4]."\n";
+                print "Added->".$br[0]."|".$br[2]."|".$br[3]."\n"; $stats->logs_inserts_incr();
+                if($br[5]){                    
+                    $pst = Settings::selectRecords($db, "SELECT max(id) FROM ".Settings->VW_LOG);
+                    my @r = $pst->fetchrow_array();
+                    $backupLIDS{$br[0]} = $r[0];
+                }
             }
-
         }
         print "\nFinished with merging LOG table.\n";
+        print "There where -> ". $stats->logs_inserts(). " inserts.\n";
 
-        print "\n\nMerging from backup NOTES table...\n";
-        my $insNOTES   = $db->prepare('INSERT INTO NOTES (LID, DOC) VALUES(?,?);')or die "Failed NOTESprepare.";
+        print "\nMerging from backup NOTES table...\n";
+        my $insNOTES   = $db->prepare('INSERT INTO NOTES (LID, DOC) VALUES(?,?);')or die "Failed NOTES prepare.";
         $b_pst = Settings::selectRecords($b_db,'SELECT LID, DOC FROM NOTES;');
         while ( @br = $b_pst->fetchrow_array() ) {
-            my $pst = Settings::selectRecords($db,"SELECT LID FROM NOTES WHERE LID=".$br[0].";");
-            my @ext = $pst->fetchrow_array();
-            if(@ext==0&&$br[0]&&$br[1]){
-                $insNOTES->execute($br[0], $br[1]) or die "Failed NOTES INSERT[".$br[0]."]";
-                print "Added NOTES->".$br[0]."\n";
+            my $in_id = $backupLIDS{$br[0]};
+            if($in_id){
+                my $pst = Settings::selectRecords($db,"SELECT LID FROM NOTES WHERE LID=".$br[0].";");
+                my @ext = $pst->fetchrow_array();                
+                if(@ext==0&&$br[1]){
+                   $insNOTES->execute($in_id, $br[1]) or die "Failed NOTES INSERT[".$br[0]."]";
+                    print "Added NOTES -> LID:".$br[0]."\n";
+                }
             }
-
         }
         print "\nFinished with merging NOTES table.\n";
-
-        $b_db->disconnect();
-        $db->disconnect();
+        print "Note that the merge didn't recover documents for any existing log entries.\n";
+        print "To do this, delete those log entries, then run restore again.\n";
         `rm -rf $dbck/`;
         print "Done!";
+        print "Restore ended: ".Settings::today(), "\n";
+};      print $stdout;
+
+        my $fh; open( $fh, ">>", Settings::logPath()."backup_restore.log");
+                print $fh $stdout;
+                close $fh;
+
+        $b_db->disconnect();
+        $db->disconnect();       
     }
     catch{
         $ERROR = "Restore Failed!hndl->$hndl $@ \n";
@@ -1260,22 +1409,22 @@ sub updateLOGDB {
     if(scalar(@fields)>6){
 
         my $i = 0;
-        my $id_cat = $fields[$i++];
-        my $id_rtf = $fields[$i++];
+        my $id_cat = $fields[$i++];        
         my $date   = $fields[$i++];
         my $log    = $fields[$i++];
+        my $rtf    = $fields[$i++];
         my $amv    = $fields[$i++];
         my $amf    = $fields[$i++];
         my $sticky = $fields[$i++];
         # Is it old pre. 1.8 format -> ID, DATE, LOG, AMOUNT, AFLAG, RTF, STICKY
-        if(!looks_like_number($id_rtf)){
+        if(!looks_like_number($rtf)){
             $i = 0;
             $id_cat = $fields[$i++];
-            $date   = $fields[$i++];
+            $rtf    = $fields[$i++];
+            $date   = $fields[$i++];            
             $log    = $fields[$i++];
             $amv    = $fields[$i++];
-            $amf    = $fields[$i++];
-            $id_rtf = $fields[$i++];
+            $amf    = $fields[$i++];            
             $sticky = $fields[$i++];
         }
         my $pdate = DateTime::Format::SQLite->parse_datetime($date);
@@ -1288,7 +1437,7 @@ sub updateLOGDB {
         my @rows = $dbs->fetchrow_array();
         if(scalar @rows == 0){
                     $dbs = $db->prepare('INSERT INTO LOG VALUES (?,?,?,?,?,?,?)');
-                    $dbs->execute($id_cat, $id_rtf, $pdate, $log, $amv, $amf, $sticky);
+                    $dbs->execute($id_cat, $pdate, $log, $rtf, $amv, $amf, $sticky);
         }
         $dbs->finish();
     }
@@ -1324,55 +1473,34 @@ sub error {
 }
 
 
-sub renumerate {
-    #Renumerate Log! Copy into temp. table.
-    my $sql;
-    $db->do("CREATE TABLE life_log_temp_table AS SELECT * FROM LOG;");
-    if(Settings::isProgressDB()){
-        $dbs = Settings::selectRecords($db, 'SELECT ID, DATE FROM LOG WHERE ID_RTF > 0 ORDER BY DATE;');
-    }else{
-        $dbs = Settings::selectRecords($db, 'SELECT rowid, DATE FROM LOG WHERE ID_RTF > 0 ORDER BY DATE;');
-    }
-    #update  notes with new log id
-    while(my @row = $dbs->fetchrow_array()) {
-        my $sql_date = $row[1];
-        if($sql_date){#could be an improperly deleted record in there? Skip if there is!
-                        #$sql_date =~ s/T/ /;
-                        $sql_date = DateTime::Format::SQLite->parse_datetime($sql_date);
-                         if(Settings::isProgressDB()){
-                            $sql = "SELECT ID, DATE FROM life_log_temp_table WHERE ID_RTF > 0 AND DATE = '".$sql_date."';";
-                         }else{
-                            $sql = "SELECT rowid, DATE FROM life_log_temp_table WHERE ID_RTF > 0 AND DATE = '".$sql_date."';";
-                         }
-                        $dbs = Settings::selectRecords($db, $sql);
-                        my @new  = $dbs->fetchrow_array();
-                        if(scalar @new > 0){
-                            $db->do("UPDATE NOTES SET LID =". $new[0]." WHERE LID==".$row[0].";");
-                        }
+    my %dates  = ();
+    #Hash is unreliable for returning sequential order of keys so array must do.
+    my @dlts = ();    
+    my $cntr_del =0;
+    my $existing;
+    my @row;
+
+        getHeader();
+        print "
Started transaction!\n";
+
+        $db->do('BEGIN TRANSACTION;');
+        # Check for duplicates, which are possible during imports or migration as internal rowid is not primary in log.
+        # @TODO This should be done through an view?
+        if(Settings::isProgressDB()){
+            $dbs = Settings::selectRecords($db, 'SELECT ID, DATE FROM LOG ORDER BY DATE;');
+        }else{
+            $dbs = Settings::selectRecords($db, 'SELECT rowid, DATE FROM LOG ORDER BY DATE;');
+        }
+        while(@row = $dbs->fetchrow_array()) {
+            my $existing = $dates{$row[0]};
+            if($existing && $existing eq $row[1]){
+                $dlts[$cntr_del++] = $row[0];
+            }
+            else{
+                $dates{$row[0]} = $row[1];
+            }
         }
-    }
 
-    # Delete Orphaned Notes entries.
-    if(Settings::isProgressDB()){
-        $dbs = Settings::selectRecords($db, "SELECT LID, LOG.ID from NOTES LEFT JOIN LOG ON
-                                        NOTES.LID = LOG.ID WHERE LOG.ID is NULL;");
-    }else{
-    $dbs = Settings::selectRecords($db, "SELECT LID, LOG.rowid from NOTES LEFT JOIN LOG ON
-                                        NOTES.LID = LOG.rowid WHERE LOG.rowid is NULL;");
-    }
-    while(my @row = $dbs->fetchrow_array()) {
-        $db->do("DELETE FROM NOTES WHERE LID=$row[0];");
-    }
-    if(Settings::isProgressDB()){
-        $db->do('DROP TABLE LOG CASCADE;');
-    }
-    else{
-        $db->do('DROP TABLE LOG;');
-    }
-    $db->do(&Settings::createLOGStmt);
-    $db->do(q(INSERT INTO LOG (ID_CAT, ID_RTF, DATE, LOG, AMOUNT,AFLAG)
-                    SELECT ID_CAT, ID_RTF, DATE, LOG, AMOUNT, AFLAG FROM life_log_temp_table ORDER by DATE;));
-    $db->do('DROP TABLE life_log_temp_table;');
-}
+
 
 1;
\ No newline at end of file
diff --git a/htdocs/cgi-bin/data.cgi b/htdocs/cgi-bin/data.cgi
index 67d740e..f53aa0e 100755
--- a/htdocs/cgi-bin/data.cgi
+++ b/htdocs/cgi-bin/data.cgi
@@ -19,14 +19,13 @@ require Settings;
 my $db        = Settings::fetchDBSettings();
 my $cgi       = Settings::cgi();
 my $dbname    = Settings::dbName();
-my $imgw      = 210;
-my $imgh      = 120;
 my $human     = DateTime::Format::Human::Duration->new();
 my $PRC_WIDTH = Settings::pagePrcWidth();
 my $DEBUG     = Settings::debug();
 my $today     =  Settings::today();
-my $tbl_rc =0;
-
+my $tbl_rc    = 0;
+my $imgw      = 210;
+my $imgh      = 120;
 my $opr = $cgi->param("opr");
 my $confirmed = $cgi->param('confirmed');
 if ($opr == 1){
@@ -182,7 +181,8 @@ try{
 
          my $tbl = '
- '; + + '; while(my @row = $st->fetchrow_array()) { @@ -191,12 +191,13 @@ try{ my $dt = DateTime::Format::SQLite->parse_datetime( $row[3] ); my $log = log2html($row[4]); - $tbl = $tbl . '" . + $tbl .= '" . '" . - '\n". + '\n". ''; if($rs eq "r1"){ - $rs = "r0"; + $rs = "r0"; } else{ $rs = "r1"; @@ -218,7 +219,7 @@ try{
Date Time Log Category
Date Time Log Category
'. $dt->ymd . "
'. $dt->ymd . "' . $dt->hms . "'."$log +
'."$log
' . $ct. '
'; - print "
\n$tbl\n
"; + print "
\n$tbl\n
"; print $cgi->end_html(); } diff --git a/htdocs/cgi-bin/json.cgi b/htdocs/cgi-bin/json.cgi index 8260d22..2c1c2dd 100755 --- a/htdocs/cgi-bin/json.cgi +++ b/htdocs/cgi-bin/json.cgi @@ -23,6 +23,8 @@ use Regexp::Common qw /URI/; use JSON; use IO::Compress::Gzip qw(gzip $GzipError); use Compress::Zlib; +use Crypt::Blowfish; +use Crypt::CBC; use lib "system/modules"; require Settings; @@ -88,57 +90,67 @@ sub defaultJSON { }); } +sub cryptKey { + $passw = $alias.$passw.Settings->CIPHER_KEY; + $passw =~ s/(.)/sprintf '%04x', ord $1/seg; + return substr $passw.Settings->CIPHER_PADDING, 0, 58; +} + sub processSubmit { # my $date = $cgi->param('date'); - my $st; + my ($st, @arr); try { if($action eq 'store'){ - $doc = qq({ - "lid":"$lid", - "bg":"$bg", - "doc":$doc - }); - my $zip = compress($doc, Z_BEST_COMPRESSION); - $st = $db->prepare("SELECT LID FROM NOTES WHERE LID = $lid;"); - $st -> execute(); - if($st->fetchrow_array() eq undef) { - $st = $db->prepare("INSERT INTO NOTES(LID, DOC) VALUES (?, ?);"); - $st->execute($lid, $zip); - $response = "Stored Document (id:$lid)!"; + my $cipher = Crypt::CBC->new(-key => cryptKey(), -cipher => 'Blowfish'); + $doc = qq({ + "lid": "$lid", + "bg": "$bg", + "doc": $doc + }); + + my $zip = compress($cipher->encrypt($doc), Z_BEST_COMPRESSION); + @arr = Settings::selectRecords($db, "SELECT LID FROM NOTES WHERE LID = $lid;")->fetchrow_array(); + if (!@arr) { + $st = $db->prepare("INSERT INTO NOTES(LID, DOC) VALUES (?, ?);"); + $st->execute($lid, $zip); + $response = "Stored Document (id:$lid)!"; } else{ - $st = $db->prepare("UPDATE NOTES SET DOC = ? WHERE LID = $lid;"); - $st->execute($zip); - $response = "Updated Document (id:$lid)!"; + $st = $db->prepare("UPDATE NOTES SET DOC = ? WHERE LID = $lid;"); + $st->execute($zip); + $response = "Updated Document (id:$lid)!"; } } elsif($action eq 'load'){ - $st = $db->prepare("SELECT DOC FROM NOTES WHERE LID = $lid;"); - $st -> execute(); - my @arr = $st->fetchrow_array(); + @arr = Settings::selectRecords($db, "SELECT DOC FROM NOTES WHERE LID = $lid;")->fetchrow_array(); if(@arr eq undef){ - $st = $db->prepare("SELECT DOC FROM NOTES WHERE LID = '0';"); - $st -> execute(); - @arr = $st->fetchrow_array(); + @arr = Settings::selectRecords($db,"SELECT DOC FROM NOTES WHERE LID = '0';")->fetchrow_array(); } + my $cipher = Crypt::CBC->new(-key => cryptKey(), -cipher => 'Blowfish'); $doc = $arr[0]; - $doc = uncompress($doc); - # print $cgi->header( -expires => "+0s", -charset => "UTF-8" ); - # print($doc); - # exit; + my $d = uncompress($doc); + $doc = $cipher->decrypt($d); + # print $cgi->header( -expires => "+0s", -charset => "UTF-8" ); + # print($doc); + # exit; $response = "Loaded Document!"; } - else{ + else{ $error = "Your action ($action) sux's a lot!"; } }catch { - $error = ":LID[$lid]-> ".$_; + if($action eq 'load' && $@ =~ /Ciphertext does not begin with a valid header for 'salt'/){# Maybe an pre v.2.2 old document? + $doc = uncompress($doc); + $response = "Your document LID[$lid] is not secure. Please resave it."; + return; + } + $error = "Error on:LID[$lid]-> ".$@; } } diff --git a/htdocs/cgi-bin/login_ctr.cgi b/htdocs/cgi-bin/login_ctr.cgi index f30b72e..90717ab 100755 --- a/htdocs/cgi-bin/login_ctr.cgi +++ b/htdocs/cgi-bin/login_ctr.cgi @@ -237,6 +237,7 @@ try{ # We live check database for available tables now only once. # If brand new database, this sill returns fine an empty array. my %curr_tables = (); + #my %curr_colums = (); # %("table", @{...columns...}) if(Settings::isProgressDB()){ $changed = checkPreparePGDB(); @@ -292,6 +293,13 @@ try{ } }catch{$@.="\n$sup"; die "Failed[$@]"} } + #Is it pre or around v.2.1, where ID_RTF is instead of RTF in the LOG table? + if($hasLogTbl && !Settings::isProgressDB()){ + $pst = Settings::selectRecords($db, "SELECT * from pragma_table_info('LOG') where name like 'ID_RTF'"); + if($pst->fetchrow_array()){ + $db->do("ALTER TABLE LOG RENAME COLUMN ID_RTF TO RTF"); + } + } # # From v.1.8 Log has changed, to have LOG to NOTES relation. # @@ -345,7 +353,7 @@ try{ foreach my $date (keys %notes_ids){ #next if(ref($notes_ids{$date}) eq 'HASH'); my $nid = $notes_ids{$date}; - my $stmt= "UPDATE LOG SET ID_RTF =". $nid." WHERE DATE == '".$date."';"; + my $stmt= "UPDATE LOG SET RTF =". $nid." WHERE DATE == '".$date."';"; try{ $db->do($stmt); } diff --git a/htdocs/cgi-bin/main.cgi b/htdocs/cgi-bin/main.cgi index cbd6926..e59d6bf 100755 --- a/htdocs/cgi-bin/main.cgi +++ b/htdocs/cgi-bin/main.cgi @@ -63,7 +63,7 @@ if(Settings::anon('^PAGE_EXCLUDES')){ } } -my $sqlView = 'SELECT ID, ID_CAT, ID_RTF, DATE, LOG, AMOUNT, AFLAG, STICKY, PID FROM '.$VW_PAGE;#Only to be found here, the main SQL select statement. +my $sqlView = 'SELECT ID, ID_CAT, DATE, LOG, RTF, AMOUNT, AFLAG, STICKY, PID FROM '.$VW_PAGE;#Only to be found here, the main SQL select statement. my $stmS = $sqlView." WHERE"; my $stmE = ' LIMIT '.&Settings::viewAllLimit.';'; my $stmD = ""; @@ -264,7 +264,7 @@ qq(
); #We use js+perl, trickery to filter by amount type, as well. if($prm_aa >0){my $s = $prm_aa - 1;$prm_aa = " AFLAG=$s AND";}else{$prm_aa=""} - if($prm_rtf){$stmS .= " ID_RTF>0 AND";} + if($prm_rtf){$stmS .= " RTF>0 AND";} if($isPUBViewMode){ $sqlVWL = $stmS." ".Settings::sqlPubors().$stmE; @@ -431,10 +431,10 @@ sub buildLog { my $i = 0; $id = $row[$i++]; #ID must be rowid in LOG. my $cid = $row[$i++]; #CID ID_CAT not used. - my $ct = $hshCats{$cid}; #ID_CAT - my $rtf = $row[$i++]; #ID_RTF since v.1.8 + my $ct = $hshCats{$cid}; #ID_CAT my $dt = DateTime::Format::SQLite->parse_datetime( $row[$i++] ); #LOG.DATE my $log = $row[$i++]; #LOG.LOG + my $rtf = $row[$i++]; #ID_RTF since v.1.8 but just RTF from v.2.1 my $am = $row[$i++]; #LOG.AMOUNT my $af = $row[$i++]; #AFLAG -> Asset as 0, Income as 1, Expense as 2 my $sticky = $row[$i++]; #Sticky to top @@ -1088,10 +1088,9 @@ try { #Apostroph's need to be replaced with doubles and white space to be fixed for the SQL. $log =~ s/'/''/g; - if ( $edit_mode && $edit_mode != "0" ) { - #Update + if ( $edit_mode && $edit_mode != "0" ) { $date = DateTime::Format::SQLite->parse_datetime($date); $date =~ s/T/ /g; - $stm = qq(UPDATE LOG SET ID_CAT='$cat', ID_RTF='$rtf', + $stm = qq(UPDATE LOG SET ID_CAT='$cat', RTF='$rtf', DATE='$date', LOG='$log', AMOUNT=$am, @@ -1099,9 +1098,8 @@ try { STICKY=$sticky WHERE $SQLID=$edit_mode;); # toBuf $stm if $DEBUG; - # - - my $dbUpd = Settings::connectDB($alias, $passw);#@ or LifeLogException->throw("Execute failed [$DBI::errstri]"); + # + traceDBExe($stm); return; } @@ -1159,7 +1157,7 @@ try { } if ($dtCur > $dt){$sticky = 1; toBuf $cgi->p("Insert forced to be sticky, it is in the past!");} $sticky=castToBool($sticky); - $stm = qq(INSERT INTO LOG (ID_CAT, ID_RTF, DATE, LOG, AMOUNT, AFLAG, STICKY) VALUES ($cat,$rtf,'$date','$log',$am,$af,$sticky);); + $stm = qq(INSERT INTO LOG (ID_CAT, DATE, LOG, RTF, AMOUNT, AFLAG, STICKY) VALUES ($cat,'$date','$log',$rtf, $am,$af,$sticky);); $st = traceDBExe($stm); if($sssCDB){ #Allow further new database creation, it is not an login infinite db creation attack. diff --git a/htdocs/cgi-bin/system/modules/Settings.pm b/htdocs/cgi-bin/system/modules/Settings.pm index 1832dd2..43461a2 100644 --- a/htdocs/cgi-bin/system/modules/Settings.pm +++ b/htdocs/cgi-bin/system/modules/Settings.pm @@ -8,7 +8,7 @@ package Settings; use v5.10; use strict; use warnings; -use Exception::Class ('SettingsException','LifeLogException'); +use Exception::Class ('SettingsException','LifeLogException','SettingsLimitSizeException'); use Syntax::Keyword::Try; use CGI; use CGI::Session '-ip_match'; @@ -20,8 +20,9 @@ use DateTime::Duration; use DBI; use experimental qw( switch ); -#This is the default developer release key, replace on istallation. As it is not secure. +# This is the default developer release key, replace on istallation. As it is not secure. use constant CIPHER_KEY => '95d7a85ba891da'; +use constant CIPHER_PADDING => 'fe0a2b6a83e81f13a2d76ab104763773310df6b0a01c7cf9807b4b0ce2a02'; # Default VIEW for all pages. use constant VW_LOG => 'VW_LOG'; # Optional instructional VIEW from config file replacing above default. @@ -191,7 +192,7 @@ sub setTimezone { } } my $try = $tz_map{$TIME_ZONE}; - $try = $tz_map{$to} if(!$try); + $try = $tz_map{$to} if(!$try && $to); if($try){ $TIME_ZONE = $try; #translated to mapped lib. provided zone. $ret -> set_time_zone($try); @@ -249,29 +250,30 @@ return qq( ); CREATE INDEX idx_cat_name ON CAT (NAME); )} -sub createLOGStmt { -if($IS_PG_DB){ +sub createLOGStmt { +#ID_RTF in v.2.0 and lower is not an id, changed to byte from v.2.1. +if($IS_PG_DB){ return qq( CREATE TABLE LOG ( ID INT UNIQUE GENERATED ALWAYS AS IDENTITY, - ID_CAT INT NOT NULL, - ID_RTF INTEGER DEFAULT 0, + ID_CAT INT NOT NULL, DATE TIMESTAMP NOT NULL, LOG VARCHAR ($DBI_LVAR_SZ) NOT NULL, + RTF BOOL DEFAULT 0, AMOUNT money, - AFLAG INT DEFAULT 0, + AFLAG INT DEFAULT 0, STICKY BOOL DEFAULT FALSE, PRIMARY KEY(ID) );)} return qq( CREATE TABLE LOG ( - ID_CAT INT NOT NULL, - ID_RTF INTEGER DEFAULT 0, + ID_CAT INT NOT NULL, DATE DATETIME NOT NULL, LOG VARCHAR ($DBI_LVAR_SZ) NOT NULL, + RTF BYTE DEFAULT 0, AMOUNT DOUBLE, - AFLAG INT DEFAULT 0, + AFLAG INT DEFAULT 0, STICKY BOOL DEFAULT 0 ); )} @@ -461,12 +463,12 @@ sub renumerate { selectRecords($db,'CREATE TABLE life_log_temp_table AS SELECT * FROM LOG;'); my $CI = 'rowid'; $CI = 'ID' if $IS_PG_DB; #update notes table with new log id only for reference sake. - my $st = selectRecords($db, "SELECT $CI, DATE FROM LOG WHERE ID_RTF > 0 ORDER BY DATE;"); + my $st = selectRecords($db, "SELECT $CI, DATE FROM LOG WHERE RTF > 0 ORDER BY DATE;"); while(my @row =$st->fetchrow_array()) { my $sql_date = $row[1]; #$sql_date =~ s/T/ /; $sql_date = DateTime::Format::SQLite->parse_datetime($sql_date); - $sql = "SELECT $CI, DATE FROM life_log_temp_table WHERE ID_RTF > 0 AND DATE = '".$sql_date."';"; + $sql = "SELECT $CI, DATE FROM life_log_temp_table WHERE RTF > 0 AND DATE = '".$sql_date."';"; my @new = selectRecords($db, $sql)->fetchrow_array(); if(scalar @new > 0){ try{#can fail here, for various reasons. @@ -489,23 +491,23 @@ sub renumerate { if($IS_PG_DB){$db->do('DROP TABLE LOG CASCADE;');}else{$db->do('DROP TABLE LOG;');} $db->do(&createLOGStmt); - $db->do('INSERT INTO LOG (ID_CAT, ID_RTF, DATE, LOG, AMOUNT,AFLAG,STICKY) - SELECT ID_CAT, ID_RTF, DATE, LOG, AMOUNT, AFLAG, STICKY FROM life_log_temp_table ORDER by DATE;'); + $db->do('INSERT INTO LOG (ID_CAT, DATE, LOG, RTF ,AMOUNT, AFLAG, STICKY) + SELECT ID_CAT, DATE, LOG, RTF, AMOUNT, AFLAG, STICKY FROM life_log_temp_table ORDER by DATE;'); $db->do('DROP TABLE life_log_temp_table;'); } sub selectRecords { my ($db, $sql) = @_; if(scalar(@_) < 2){ - SettingsException->throw("ERROR Argument number is wrong->db is:$db\n", show_trace=>$DEBUG); + die "Wrong number of arguments, expecting Settings::selectRecords(\$db, \$sql) got Settings::selectRecords('@_').\n"; } try{ - my $pst = $db->prepare($sql); - $pst->execute(); - return 0 if(!$pst); - return $pst; + my $pst = $db->prepare($sql); + return 0 if(!$pst); + $pst->execute(); + return $pst; }catch{ - SettingsException->throw(error=>"Database error encountered!\n ERROR->".$@." SQL-> $sql DSN:".$DSN, show_trace=>$DEBUG); + SettingsException->throw(error=>"Database error encountered!\n ERROR->$@\n SQL-> $sql DSN:".$DSN, show_trace=>$DEBUG); }; } diff --git a/htdocs/cgi-bin/testSettings.pl b/htdocs/cgi-bin/testSettings.pl index 73abe97..39d2f45 100755 --- a/htdocs/cgi-bin/testSettings.pl +++ b/htdocs/cgi-bin/testSettings.pl @@ -15,12 +15,30 @@ use lib $ENV{'PWD'}.'/htdocs/cgi-bin/system/modules'; require CNFParser; +my @set = ('0' ..'9', 'A' .. 'F'); +#@my $str = join '' => map $set[rand @set], 1 .. 56; +#print join '' => map $set[rand @set], 1 .. 56, "\n\n"; + + +print cryptKey('admin','admin'), "\n"; + + +#B6B829430583F8C03EBEFB1677232194EEC8D211609226EC516F7BB4B +sub cryptKey { + my $admin = shift; + my $passw = shift; + $passw = $admin.$passw.Settings->CIPHER_KEY; + $passw =~ s/(.)/sprintf '%04x', ord $1/seg; + return substr $passw.Settings->CIPHER_PADDING, 0, 58; +} + + # my $TIME_ZONE_MAP =""; # open(my $fh, '<',$ENV{'PWD'}.'/dbLifeLog/main.cnf') or LifeLogException->throw("Can't open main.cnf: $!"); # while (my $line = <$fh>) { # chomp $line; # if($line =~ /<) { # chomp $line; # last if($line =~ />$/); @@ -74,76 +92,76 @@ require CNFParser; # $where =~ s/\s+AND$//; # return Settings::createViewLOGStmt(VW_LOG_WITH_EXCLUDES,$where); # } -my $server = 'DBI:Pg:host=elite;name=android'; - -my $v1 = $server =~ qr/:/; -my $v2 = $`; -my $v3 = $' =~ qr/:/; -# $var=1 -# $`=DBI -# $&=: -# $'=Pg:host=elite;name=androi -print $v2.'->'.$`,"\n"; -$v1 =""; - -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="1"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+1m"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+10minutes"; -print "[$v1]->".timeFormatValue($v1),"\n"; -#default -$v1 ="+30m"; -print "[$v1]->".timeFormatValue($v1),"\n"; - -#Let's try sneak in garbage. -$v1 ="+20bitcons"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+20hitcons"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+30hr"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+1hr"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+8.2severe"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+8severe"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+2severe"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+120severe"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+119severe"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+121severe"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+280severe"; -print "[$v1]->".timeFormatValue($v1),"\n"; -$v1 ="+120"; -print "[$v1]->".timeFormatValue($v1),"\n"; - - -sub timeFormatValue { - my $v = shift; - my $ret = "+2m"; - if(!$v){$v=$ret} - if($v !~ /^\+/){$v='+'.$v.'m'}# Must be positive added time - # Find first match in whatever passed. - my @a = $v =~ m/(\+\d+[shm])/gis; - if(scalar(@a)>0){$v=$a[0]} - # Test acceptable setting, which is any number from 2, having any s,m or h. - if($v =~ m/(\+[2-9]\d*[smh])|(\+[1-9]+\d+[smh])/){ - # Next is actually, the dry booger in the nose. Let's pick it out! - # Someone might try to set in seconds value to be under two minutes. - @a = $v =~ m/(\d[2-9]\d+)/gs; - if(scalar(@a)>0 && int($a[0])<120){return $ret}else{return $v} - } - elsif($v =~ m/\+\d+/){# is passedstill without time unit? Minutetise! - $ret=$v."m" - } - return $ret; -} +# my $server = 'DBI:Pg:host=elite;name=android'; + +# my $v1 = $server =~ qr/:/; +# my $v2 = $`; +# my $v3 = $' =~ qr/:/; +# # $var=1 +# # $`=DBI +# # $&=: +# # $'=Pg:host=elite;name=androi +# print $v2.'->'.$`,"\n"; +# $v1 =""; + +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="1"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+1m"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+10minutes"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# #default +# $v1 ="+30m"; +# print "[$v1]->".timeFormatValue($v1),"\n"; + +# #Let's try sneak in garbage. +# $v1 ="+20bitcons"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+20hitcons"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+30hr"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+1hr"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+8.2severe"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+8severe"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+2severe"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+120severe"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+119severe"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+121severe"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+280severe"; +# print "[$v1]->".timeFormatValue($v1),"\n"; +# $v1 ="+120"; +# print "[$v1]->".timeFormatValue($v1),"\n"; + + +# sub timeFormatValue { +# my $v = shift; +# my $ret = "+2m"; +# if(!$v){$v=$ret} +# if($v !~ /^\+/){$v='+'.$v.'m'}# Must be positive added time +# # Find first match in whatever passed. +# my @a = $v =~ m/(\+\d+[shm])/gis; +# if(scalar(@a)>0){$v=$a[0]} +# # Test acceptable setting, which is any number from 2, having any s,m or h. +# if($v =~ m/(\+[2-9]\d*[smh])|(\+[1-9]+\d+[smh])/){ +# # Next is actually, the dry booger in the nose. Let's pick it out! +# # Someone might try to set in seconds value to be under two minutes. +# @a = $v =~ m/(\d[2-9]\d+)/gs; +# if(scalar(@a)>0 && int($a[0])<120){return $ret}else{return $v} +# } +# elsif($v =~ m/\+\d+/){# is passedstill without time unit? Minutetise! +# $ret=$v."m" +# } +# return $ret; +# } # my @AUTOWORDS = ("searchificould","itworks","funeral","gasbottle","electricitybill","ctwodaysinrow","carrego","cartwonewtires","flashlightout","test","check","tmshsample","new"); @@ -155,4 +173,10 @@ sub timeFormatValue { # } # print "\n"; + + + + + + 1; \ No newline at end of file diff --git a/htdocs/cgi-bin/wsrc/main.js b/htdocs/cgi-bin/wsrc/main.js index ef7b1f5..8fa1ae5 100644 --- a/htdocs/cgi-bin/wsrc/main.js +++ b/htdocs/cgi-bin/wsrc/main.js @@ -1126,6 +1126,7 @@ function loadRTFResult(content, result, prms, quill) { var css = $("#q-scroll"+id).prop('style'); if(css){css.backgroundColor = json.content.bg} } + display(json.response, 5); //alert(obj.response); } -- 2.34.1