예제 #1
0
        internal static void AlertUserAboutProblematicImports()
        {
            bool do_view = MessageBoxes.AskErrorQuestion("There were problems with some of the documents you were trying to add to Qiqqa.  Do you want to see the problem details?", true);

            // do NOT spend a long time inside the lock!
            // hence we null the report file reference so that other threads can
            // create another report file and continue work while the user takes
            // a slow look at the old one...
            //
            // In short: take `Process.Start(...)` *outside* the lock!
            string report_filename = null;

            Utilities.LockPerfTimer l1_clk = Utilities.LockPerfChecker.Start();
            lock (problematic_import_documents_lock)
            {
                l1_clk.LockPerfTimerStop();
                if (do_view)
                {
                    report_filename = problematic_import_documents_filename;
                }
                else
                {
                    File.Delete(problematic_import_documents_filename);
                }
                // reset:
                problematic_import_documents_filename      = null;
                problematic_import_documents_alert_showing = 0;
            }

            if (null != report_filename)
            {
                Process.Start(report_filename);
            }
        }
예제 #2
0
        public List <LibraryItem> GetLibraryItems(string extension, List <string> fingerprints = null, int MaxRecordCount = 0)
        {
            List <LibraryItem> results             = new List <LibraryItem>();
            List <Exception>   database_corruption = new List <Exception>();

            try
            {
                lock (DBAccessLock.db_access_lock)
                {
                    using (var connection = GetConnection())
                    {
                        connection.Open();

                        string command_string = "SELECT fingerprint, extension, md5, data FROM LibraryItem WHERE 1=1 ";
                        command_string += turnArgumentSetIntoQueryPart("fingerprint", fingerprints);
                        command_string += turnArgumentIntoQueryPart("extension", extension);
                        if (MaxRecordCount > 0)
                        {
                            // http://www.sqlitetutorial.net/sqlite-limit/
                            command_string += " LIMIT @maxnum";
                        }

                        using (var command = new SQLiteCommand(command_string, connection))
                        {
                            //turnArgumentIntoQueryParameter(command, "fingerprint", fingerprint);
                            turnArgumentIntoQueryParameter(command, "extension", extension);
                            if (MaxRecordCount > 0)
                            {
                                command.Parameters.AddWithValue("@maxnum", MaxRecordCount);
                            }

                            using (SQLiteDataReader reader = command.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    LibraryItem result = new LibraryItem();

                                    int field_count = 0;

                                    // Read the record in 2-3 gangs, as there's some DBs out there which have the BLOB as NOT A BLOB but as a STRING type instead:
                                    // this is probably caused by manual editing (using SQLite CLI or other means) of the BLOB record.

                                    // gang 1: load the field count and header fields: these almost never fail.
                                    try
                                    {
                                        field_count = reader.FieldCount;

                                        result.fingerprint = reader.GetString(0);
                                        result.extension   = reader.GetString(1);
                                        result.md5         = reader.GetString(2);
                                    }
                                    catch (Exception ex)
                                    {
                                        string msg = String.Format("LibraryDB::GetLibraryItems: Database record #{4} gang 1 decode failure for DB '{0}': fingerprint={1}, ext={2}, md5={3}, field_count={5}.",
                                                                   library_path,
                                                                   String.IsNullOrEmpty(result.fingerprint) ? "???" : result.fingerprint,
                                                                   String.IsNullOrEmpty(result.extension) ? "???" : result.extension,
                                                                   String.IsNullOrEmpty(result.md5) ? "???" : result.md5,
                                                                   reader.StepCount, // ~= results.Count + database_corruption.Count
                                                                   field_count
                                                                   );
                                        Logging.Error(ex, "{0}", msg);

                                        Exception ex2 = new Exception(msg, ex);

                                        database_corruption.Add(ex2);

                                        // it's no use to try to decode the rest of the DB record: it is lost to us
                                        continue;
                                    }

                                    if (true)
                                    {
                                        Exception ex2 = null;

                                        long total_bytes = 0;

                                        // gang 2: get the BLOB
                                        try
                                        {
                                            total_bytes = reader.GetBytes(3, 0, null, 0, 0);
                                            result.data = new byte[total_bytes];
                                            long total_bytes2 = reader.GetBytes(3, 0, result.data, 0, (int)total_bytes);
                                            if (total_bytes != total_bytes2)
                                            {
                                                throw new Exception("Error reading blob - blob size different on each occasion.");
                                            }

                                            results.Add(result);
                                            continue;
                                        }
                                        catch (Exception ex)
                                        {
                                            string msg = String.Format("LibraryDB::GetLibraryItems: Database record #{4} BLOB decode failure for DB '{0}': fingerprint={1}, ext={2}, md5={3}, BLOB length={5}.",
                                                                       library_path,
                                                                       String.IsNullOrEmpty(result.fingerprint) ? "???" : result.fingerprint,
                                                                       String.IsNullOrEmpty(result.extension) ? "???" : result.extension,
                                                                       String.IsNullOrEmpty(result.md5) ? "???" : result.md5,
                                                                       reader.StepCount, // ~= results.Count + database_corruption.Count
                                                                       total_bytes
                                                                       );

                                            ex2 = new Exception(msg, ex);

                                            // gang 3: get at the BLOB-née-STRING in an indirect way
                                            object[] fields = new object[5];

                                            try
                                            {
                                                reader.GetValues(fields);
                                                byte[] arr = fields[3] as byte[];
                                                if (arr != null)
                                                {
                                                    string blob = Encoding.UTF8.GetString(arr, 0, arr.Length);

                                                    result.data = new byte[arr.Length];
                                                    Array.Copy(arr, result.data, arr.Length);

                                                    results.Add(result);

                                                    Logging.Warn("LibraryDB::GetLibraryItems: Database record #{0} BLOB field is instead decoded as UTF8 STRING, following this RESOLVED ERROR: {1}\n  Decoded STRING content:\n{2}",
                                                                 reader.StepCount, // ~= results.Count + database_corruption.Count
                                                                 ex2.ToStringAllExceptionDetails(),
                                                                 blob);

                                                    continue;
                                                }
                                                else
                                                {
                                                    throw new Exception("Cannot extract BLOB field.");
                                                }
                                            }
                                            catch (Exception ex3)
                                            {
                                                Logging.Error(ex2);
                                                Logging.Error(ex3);

                                                database_corruption.Add(ex2);
                                            }
                                        }
                                    }
                                }

                                reader.Close();
                            }
                        }

                        connection.Close();
                    }

                    //
                    // see SO link above at the `DBAccessLock.db_access_lock` declaration.
                    //
                    // We keep this *inside* the critical section so that we know we'll be the only active SQLite
                    // action which just transpired.
                    // *This* is also the reason why I went with a *global* lock (singleton) for *all* databases,
                    // even while *theoretically* this is *wrong* or rather: *unnecessary* as the databases
                    // i.e. Qiqqa Libraries shouldn't bite one another. I, however, need to ensure that the
                    // added `System.Data.SQLite.SQLiteConnection.ClearAllPools();` statements don't foul up
                    // matters in library B while lib A I/O is getting cleaned up.
                    //
                    // In short: Yuck. + Cave canem.
                    //
                    SQLiteConnection.ClearAllPools();
                }
            }
            catch (Exception ex)
            {
                Logging.Error(ex, "LibraryDB::GetLibraryItems: Database I/O failure for DB '{0}'.", library_path);
                LibraryDB.FurtherDiagnoseDBProblem(ex, database_corruption, library_path);
                throw;
            }

            if (database_corruption.Count > 0)
            {
                // report database corruption: the user may want to recover from this ASAP!
                if (MessageBoxes.AskErrorQuestion(true, "Library '{0}' has some data corruption. Do you want to abort the application to attempt recovery using external tools, e.g. a data restore from backup?\n\nWhen you answer NO, we will continue with what we could recover so far instead.\n\n\nConsult the Qiqqa logfiles to see the individual corruptions reported.",
                                                  library_path))
                {
                    Logging.Warn("User chose to abort the application on database corruption report");
                    Environment.Exit(3);
                }
            }

            return(results);
        }
예제 #3
0
        public static PDFDocument AddNewPDFDocumentsToLibraryWithMetadata_SYNCHRONOUS(Library library, bool suppress_notifications, bool suppress_signal_that_docs_have_changed, FilenameWithMetadataImport[] filename_with_metadata_imports)
        {
            // Notify if there is just a single doc
            suppress_notifications = suppress_notifications || (filename_with_metadata_imports.Length > 1);

            StatusManager.Instance.ClearCancelled("BulkLibraryDocument");

            PDFDocument last_added_pdf_document = null;
            string      problematic_import_documents_filename = null;

            int successful_additions = 0;

            for (int i = 0; i < filename_with_metadata_imports.Length; ++i)
            {
                if (StatusManager.Instance.IsCancelled("BulkLibraryDocument"))
                {
                    Logging.Warn("User chose to stop bulk adding documents to the library");
                    break;
                }
                StatusManager.Instance.UpdateStatus("BulkLibraryDocument", String.Format("Adding document {0} of {1} to your library", i, filename_with_metadata_imports.Length), i, filename_with_metadata_imports.Length, true);

                FilenameWithMetadataImport filename_with_metadata_import = filename_with_metadata_imports[i];

                try
                {
                    string filename = filename_with_metadata_import.filename;
                    string bibtex   = filename_with_metadata_import.bibtex;

                    // Although the outside world may allow us to be signalling, we will not do it unless we are the n-100th doc or the last doc
                    bool local_suppress_signal_that_docs_have_changed = suppress_signal_that_docs_have_changed;
                    if (!local_suppress_signal_that_docs_have_changed)
                    {
                        if ((i != filename_with_metadata_imports.Length - 1) && (0 != i % 100))
                        {
                            local_suppress_signal_that_docs_have_changed = true;
                        }
                    }

                    PDFDocument pdf_document = library.AddNewDocumentToLibrary_SYNCHRONOUS(filename, filename, bibtex, filename_with_metadata_import.tags, filename_with_metadata_import.notes, suppress_notifications, local_suppress_signal_that_docs_have_changed);
                    if (null != pdf_document)
                    {
                        ++successful_additions;
                    }
                    last_added_pdf_document = pdf_document;
                }

                catch (Exception ex)
                {
                    Logging.Warn(ex, "There was a problem adding a document to the library:\n{0}", filename_with_metadata_import);

                    if (null == problematic_import_documents_filename)
                    {
                        problematic_import_documents_filename = TempFile.GenerateTempFilename("txt");

                        File.AppendAllText(
                            problematic_import_documents_filename,
                            "The following files caused problems while being imported into Qiqqa:\r\n\r\n"
                            );
                    }

                    File.AppendAllText(
                        problematic_import_documents_filename,
                        String.Format(
                            "----------\r\n{0}\r\n{1}\r\n----------\r\n"
                            , ex.Message
                            , filename_with_metadata_import
                            )
                        );
                }
            }

            StatusManager.Instance.UpdateStatus("BulkLibraryDocument", String.Format("Added {0} of {1} document(s) to your library", successful_additions, filename_with_metadata_imports.Length));

            // If there have been some import problems, report them to the user
            if (null != problematic_import_documents_filename)
            {
                if (MessageBoxes.AskErrorQuestion("There were problems with some of the documents you were trying to add to Qiqqa.  Do you want to see the problem details?", true))
                {
                    Process.Start(problematic_import_documents_filename);
                }
                else
                {
                    File.Delete(problematic_import_documents_filename);
                }
            }

            return(last_added_pdf_document);
        }
        public List <IntranetLibraryItem> GetIntranetLibraryItemsSummary()
        {
            List <IntranetLibraryItem> results             = new List <IntranetLibraryItem>();
            List <Exception>           database_corruption = new List <Exception>();

            try
            {
                lock (DBAccessLock.db_access_lock)
                {
                    using (var connection = GetConnection())
                    {
                        connection.Open();

                        string command_string = "SELECT filename, md5 FROM LibraryItem WHERE 1=1 ";

                        using (var command = new SQLiteCommand(command_string, connection))
                        {
                            using (SQLiteDataReader reader = command.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    IntranetLibraryItem result = new IntranetLibraryItem();
                                    results.Add(result);

                                    try
                                    {
                                        result.filename = reader.GetString(0);
                                        result.md5      = reader.GetString(1);
                                    }
                                    catch (Exception ex)
                                    {
                                        string msg = String.Format("IntranetLibraryDB::GetIntranetLibraryItemsSummary: Database record #{3} decode failure for DB '{0}': filename_id={1}, md5={2}.",
                                                                   library_path,
                                                                   String.IsNullOrEmpty(result.filename) ? "???" : result.filename,
                                                                   String.IsNullOrEmpty(result.md5) ? "???" : result.md5,
                                                                   reader.StepCount // ~= results.Count + database_corruption.Count
                                                                   );
                                        Logging.Error(ex, "{0}", msg);

                                        Exception ex2 = new Exception(msg, ex);

                                        database_corruption.Add(ex2);
                                    }
                                }

                                reader.Close();
                            }
                        }

                        connection.Close();
                    }

                    //
                    // see SO link above at the `DBAccessLock.db_access_lock` declaration.
                    //
                    // We keep this *inside* the critical section so that we know we'll be the only active SQLite
                    // action which just transpired.
                    // *This* is also the reason why I went with a *global* lock (singleton) for *all* databases,
                    // even while *theoretically* this is *wrong* or rather: *unnecessary* as the databases
                    // i.e. Qiqqa Libraries shouldn't bite one another. I, however, need to ensure that the
                    // added `System.Data.SQLite.SQLiteConnection.ClearAllPools();` statements don't foul up
                    // matters in library B while lib A I/O is getting cleaned up.
                    //
                    // In short: Yuck. + Cave canem.
                    //
                    SQLiteConnection.ClearAllPools();
                }
            }
            catch (Exception ex)
            {
                Logging.Error(ex, "IntranetLibraryDB::GetLibraryItemsSummary: Database I/O failure for DB '{0}'.", library_path);
                LibraryDB.FurtherDiagnoseDBProblem(ex, database_corruption, library_path);
                throw;
            }

            if (database_corruption.Count > 0)
            {
                // report database corruption: the user may want to recover from this ASAP!
                if (MessageBoxes.AskErrorQuestion(true, "INTRANET Library (Sync Point) '{0}' has some data corruption. Do you want to abort the application to attempt recovery using external tools, e.g. a data restore from backup?\n\nWhen you answer NO, we will continue with what we could recover so far instead.\n\n\nConsult the Qiqqa logfiles to see the individual corruptions reported.",
                                                  library_path))
                {
                    Logging.Warn("User chose to abort the application on database corruption report");
                    Environment.Exit(3);
                }
            }

            return(results);
        }