Exemple #1
0
        public void PutBlob(string filename, byte[] data)
        {
            // Guard
            if (String.IsNullOrEmpty(filename))
            {
                throw new Exception("Can't store in LibraryDB with null filename.");
            }

            // Calculate the MD5 of this blobbiiiieeeeee
            string md5 = StreamMD5.FromBytes(data);

            using (var connection = GetConnection())
            {
                connection.Open();
                using (var transaction = connection.BeginTransaction())
                {
                    using (var command = new SQLiteCommand("DELETE FROM LibraryItem WHERE filename=@filename", connection, transaction))
                    {
                        command.Parameters.AddWithValue("@filename", filename);
                        command.ExecuteNonQuery();
                    }

                    using (var command = new SQLiteCommand("INSERT INTO LibraryItem(filename, last_updated_by, md5, data) VALUES(@filename, @last_updated_by, @md5, @data)", connection, transaction))
                    {
                        command.Parameters.AddWithValue("@filename", filename);
                        command.Parameters.AddWithValue("@last_updated_by", Environment.UserName);
                        command.Parameters.AddWithValue("@md5", md5);
                        command.Parameters.AddWithValue("@data", data);
                        command.ExecuteNonQuery();
                    }

                    transaction.Commit();
                }
            }
        }
        public string ScrapeURLToFile(string url, bool force_download = false, Dictionary <string, string> additional_headers = null)
        {
            string cache_key = StreamMD5.FromText(url);
            string directory = Path.GetFullPath(Path.Combine(base_directory, cache_key.Substring(0, 2)));
            string filename  = Path.GetFullPath(Path.Combine(directory, cache_key));

            if (!File.Exists(filename) || force_download)
            {
                Utilities.Files.DirectoryTools.CreateDirectory(directory);

                // Crude throttle
                if (true)
                {
                    while (DateTime.UtcNow.Subtract(last_scrape_time).TotalMilliseconds < throttle_ms)
                    {
                        Thread.Sleep(50);
                    }
                    last_scrape_time = DateTime.UtcNow;
                }

                Logging.Info("Downloading from {0}", url);
                using (WebClient client = new WebClient())
                {
                    if (null != additional_headers)
                    {
                        foreach (var pair in additional_headers)
                        {
                            client.Headers.Add(pair.Key, pair.Value);
                        }
                    }
                    if (!String.IsNullOrEmpty(userAgent))
                    {
                        client.Headers.Add("User-agent", userAgent);
                    }

                    string temp_filename = filename + ".tmp";
                    try
                    {
                        client.DownloadFile(url, temp_filename);
                    }
                    catch (WebException ex)
                    {
                        File.WriteAllText(temp_filename, ex.ToString());
                    }

                    File.Delete(filename);
                    File.Move(temp_filename, filename);
                }

                string filename_manifest = Path.GetFullPath(Path.Combine(base_directory, @"manifest.txt"));
                string manifest_line     = String.Format("{0}\t{1}", cache_key, url);
                using (StreamWriter sw = File.AppendText(filename_manifest))
                {
                    sw.WriteLine(manifest_line);
                }
            }

            return(filename);
        }
        internal void PutBlob(SQLiteConnection connection, SQLiteTransaction transaction, string fingerprint, string extension, byte[] data)
        {
            // Calculate the MD5 of this blobbiiiieeeeee
            string md5 = StreamMD5.FromBytes(data);

            using (var command = new SQLiteCommand("INSERT INTO LibraryItem(fingerprint, extension, md5, data) VALUES(@fingerprint, @extension, @md5, @data)", connection, transaction))
            {
                command.Parameters.AddWithValue("@fingerprint", fingerprint);
                command.Parameters.AddWithValue("@extension", extension);
                command.Parameters.AddWithValue("@md5", md5);
                command.Parameters.AddWithValue("@data", data);
                command.ExecuteNonQuery();
            }
        }
        public void PutBlob(string fingerprint, string extension, byte[] data)
        {
            // Guard
            if (String.IsNullOrEmpty(fingerprint))
            {
                throw new Exception("Can't store in LibraryDB with null fingerprint.");
            }
            if (String.IsNullOrEmpty(extension))
            {
                throw new Exception("Can't store in LibraryDB with null extension.");
            }

            // Calculate the MD5 of this blobbiiiieeeeee
            string md5 = StreamMD5.FromBytes(data);

            using (var connection = GetConnection())
            {
                connection.Open();

                bool managed_update = false;

                using (var command = new SQLiteCommand("UPDATE LibraryItem SET MD5=@md5, DATA=@data WHERE fingerprint=@fingerprint AND extension=@extension", connection))
                {
                    command.Parameters.AddWithValue("@md5", md5);
                    command.Parameters.AddWithValue("@data", data);
                    command.Parameters.AddWithValue("@fingerprint", fingerprint);
                    command.Parameters.AddWithValue("@extension", extension);
                    int num_rows_updated = command.ExecuteNonQuery();
                    if (1 == num_rows_updated)
                    {
                        managed_update = true;
                    }
                }

                if (!managed_update)
                {
                    using (var command = new SQLiteCommand("INSERT INTO LibraryItem(fingerprint, extension, md5, data) VALUES(@fingerprint, @extension, @md5, @data)", connection))
                    {
                        command.Parameters.AddWithValue("@fingerprint", fingerprint);
                        command.Parameters.AddWithValue("@extension", extension);
                        command.Parameters.AddWithValue("@md5", md5);
                        command.Parameters.AddWithValue("@data", data);
                        command.ExecuteNonQuery();
                    }
                }
            }
        }
        private string MkLegalSizedPath(string basename, string typeIdStr)
        {
            const int PATH_MAX = 240;  // must be less than 255 / 260 - see also https://kb.acronis.com/content/39790

            string root     = Path.GetDirectoryName(basename);
            string name     = Path.GetFileName(basename);
            string dataname = Path.GetFileNameWithoutExtension(DataFile);
            string ext      = SubStr(Path.GetExtension(DataFile), 1).Trim(); // produce the extension without leading dot

            if (ext.StartsWith("bib"))
            {
                ext = SubStr(ext, 3).Trim();
            }
            if (ext.Length > 0)
            {
                ext = "." + ext;
            }

            // UNC long filename/path support by forcing this to be a UNC path:
            string filenamebase = $"{dataname}.{name}{ext}{ExtensionWithDot}";

            // first make the full path without the approved/received, so that that bit doesn't make a difference
            // in the length check and subsequent decision to produce a shorthand filename path or not:

            // It's not always needed, but do the different shorthand conversions anyway and pick the longest fitting one:
            string short_tn = SanitizeFilename(CamelCaseShorthand(name));
            string short_dn = SanitizeFilename(SubStr(dataname, 0, 10) + CamelCaseShorthand(dataname));

            string hash       = StreamMD5.FromText(filenamebase).ToUpper();
            string short_hash = SubStr(hash, 0, Math.Max(6, 11 - short_tn.Length));

            // this variant will fit in the length criterium, guaranteed:
            string alt_filepath0 = Path.GetFullPath(Path.Combine(root, $"{short_dn}.{short_hash}_{short_tn}{ext}{typeIdStr}{ExtensionWithDot}"));
            string filepath      = alt_filepath0;

            // next, we construct the longer variants to check if they fit.
            //
            // DO NOTE that we create a path without typeIdStr part first, because we want both received and approved files to be based
            // on the *same* alt selection decision!

            string picked_alt_filepath = Path.GetFullPath(Path.Combine(root, $"{short_dn}.{short_hash}_{short_tn}{ext}.APPROVEDXYZ{ExtensionWithDot}"));

            name     = SanitizeFilename(name);
            dataname = SanitizeFilename(dataname);

            string alt_filepath1 = Path.GetFullPath(Path.Combine(root, $"{short_dn}_{short_hash}.{name}{ext}.APPROVEDXYZ{ExtensionWithDot}"));

            if (alt_filepath1.Length < PATH_MAX)
            {
                filepath            = Path.GetFullPath(Path.Combine(root, $"{short_dn}_{short_hash}.{name}{ext}{typeIdStr}{ExtensionWithDot}"));
                picked_alt_filepath = alt_filepath1;
            }

            // second alternative: only pick this one if it fits and produces a longer name:
            string alt_filepath2 = Path.GetFullPath(Path.Combine(root, $"{dataname}.{short_hash}_{short_tn}{ext}.APPROVEDXYZ{ExtensionWithDot}"));

            if (alt_filepath2.Length < PATH_MAX && alt_filepath2.Length > picked_alt_filepath.Length)
            {
                filepath            = Path.GetFullPath(Path.Combine(root, $"{dataname}.{short_hash}_{short_tn}{ext}{typeIdStr}{ExtensionWithDot}"));
                picked_alt_filepath = alt_filepath2;
            }
            else
            {
                // third alt: the 'optimally trimmed' test name used as part of the filename:
                int    trim_length   = PATH_MAX - alt_filepath0.Length + 10 - 1;
                string short_dn2     = SanitizeFilename(SubStr(dataname, 0, trim_length) + CamelCaseShorthand(dataname));
                string alt_filepath3 = Path.GetFullPath(Path.Combine(root, $"{short_dn2}.{short_hash}_{short_tn}{ext}{typeIdStr}{ExtensionWithDot}"));
                if (alt_filepath3.Length < PATH_MAX && alt_filepath3.Length > picked_alt_filepath.Length)
                {
                    filepath            = Path.GetFullPath(Path.Combine(root, $"{short_dn2}.{short_hash}_{short_tn}{ext}{typeIdStr}{ExtensionWithDot}"));
                    picked_alt_filepath = alt_filepath3;
                }
            }

            // fourth alt: the full, unadulterated path; if it fits in the length criterium, take it anyway
            string alt_filepath4 = Path.GetFullPath(Path.Combine(root, $"{dataname}.{name}{ext}.APPROVEDXYZ{ExtensionWithDot}"));

            if (alt_filepath4.Length < PATH_MAX)
            {
                // UNC long filename/path support by forcing this to be a UNC path:
                filepath            = Path.GetFullPath(Path.Combine(root, $"{dataname}.{name}{ext}{typeIdStr}{ExtensionWithDot}"));
                picked_alt_filepath = alt_filepath4;
            }

            return(filepath);
        }
        public void PutBlob(string fingerprint, string extension, byte[] data)
        {
            // Guard
            if (String.IsNullOrWhiteSpace(fingerprint))
            {
                throw new Exception("Can't store in LibraryDB with null fingerprint.");
            }
            if (String.IsNullOrWhiteSpace(extension))
            {
                throw new Exception("Can't store in LibraryDB with null extension.");
            }

            // Calculate the MD5 of this blobbiiiieeeeee
            string md5 = StreamMD5.FromBytes(data);

            try
            {
                lock (DBAccessLock.db_access_lock)
                {
                    using (var connection = GetConnection())
                    {
                        connection.Open();
                        using (var transaction = connection.BeginTransaction())
                        {
                            bool managed_update = false;

                            using (var command = new SQLiteCommand("UPDATE LibraryItem SET MD5=@md5, DATA=@data WHERE fingerprint=@fingerprint AND extension=@extension", connection, transaction))
                            {
                                command.Parameters.AddWithValue("@md5", md5);
                                command.Parameters.AddWithValue("@data", data);
                                command.Parameters.AddWithValue("@fingerprint", fingerprint);
                                command.Parameters.AddWithValue("@extension", extension);
                                int num_rows_updated = command.ExecuteNonQuery();
                                if (1 == num_rows_updated)
                                {
                                    managed_update = true;
                                }
                            }

                            if (!managed_update)
                            {
                                using (var command = new SQLiteCommand("INSERT INTO LibraryItem(fingerprint, extension, md5, data) VALUES(@fingerprint, @extension, @md5, @data)", connection, transaction))
                                {
                                    command.Parameters.AddWithValue("@fingerprint", fingerprint);
                                    command.Parameters.AddWithValue("@extension", extension);
                                    command.Parameters.AddWithValue("@md5", md5);
                                    command.Parameters.AddWithValue("@data", data);
                                    command.ExecuteNonQuery();
                                }
                            }

                            transaction.Commit();
                        }
                        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::PutBLOB: Database I/O failure for DB '{0}'.", library_path);
                LibraryDB.FurtherDiagnoseDBProblem(ex, null, library_path);
                throw;
            }
        }
Exemple #7
0
        private static int DoDownloads(Library library, bool restricted_metadata_sync, Dictionary <string, string> historical_sync_file, SynchronisationAction synchronisation_action)
        {
            int download_count = 0;

            StatusManager.Instance.ClearCancelled(StatusCodes.SYNC_META(library));
            foreach (SynchronisationState ss in synchronisation_action.states_to_download)
            {
                StatusManager.Instance.UpdateStatus(StatusCodes.SYNC_META(library), String.Format("Downloading metadata from your Web/Intranet Library ({0} to go)", synchronisation_action.states_to_download.Count - download_count), download_count, synchronisation_action.states_to_download.Count, true);
                ++download_count;

                // Has the user cancelled?
                if (StatusManager.Instance.IsCancelled(StatusCodes.SYNC_META(library)))
                {
                    Logging.Info("User has cancelled their metadata download");
                    break;
                }

                try
                {
                    Logging.Info("+Downloading {0}", ss.filename);

                    StoredUserFile stored_user_file = null;

                    // --- TODO: Replace this with a pretty interface class ------------------------------------------------
                    if (false)
                    {
                    }
                    else if (library.WebLibraryDetail.IsIntranetLibrary)
                    {
                        stored_user_file = SynchronisationExecutor_Intranet.DoDownload(library, ss);
                    }
                    else
                    {
                        throw new Exception(String.Format("Did not understand how to download for library {0}", library.WebLibraryDetail.Title));
                    }
                    // -----------------------------------------------------------------------------------------------------

                    Logging.Info("-Downloading {0}", ss.filename);

                    {
                        // Check that the MD5s match, or we have had some issue in the download
                        Logging.Info("Checking content");
                        string md5_metadata    = StreamMD5.FromBytes(stored_user_file.Content);
                        string header_etag     = stored_user_file.Md5;
                        string header_etag_nik = header_etag;
                        if (null != header_etag && !String.IsNullOrEmpty(header_etag) && 0 != String.Compare(md5_metadata, header_etag, true) && 0 != String.Compare(md5_metadata, header_etag_nik, true))
                        {
                            throw new Exception(String.Format("Local and remote MD5s do not match. local={0} remote={1} remote_nik={2}", md5_metadata, header_etag, header_etag_nik));
                        }

                        Logging.Info("Copying content");
                        library.LibraryDB.PutBlob(ss.fingerprint, ss.extension, stored_user_file.Content);

                        // Remember this MD5 for our sync clash detection
                        Logging.Info("Remembering md5");
                        historical_sync_file[ss.filename] = md5_metadata;
                    }
                }

                catch (Exception ex)
                {
                    Logging.Error(ex, "There was a problem downloading one of your sync files");
                }
            }

            StatusManager.Instance.UpdateStatus(StatusCodes.SYNC_META(library), String.Format("Downloaded {0} metadata from your Web/Intranet Library", download_count));

            return(download_count);
        }
        public void PutBlob(string filename, byte[] data)
        {
            // Guard
            if (String.IsNullOrWhiteSpace(filename))
            {
                throw new Exception("Can't store in LibraryDB with null filename.");
            }

            // Calculate the MD5 of this blobbiiiieeeeee
            string md5 = StreamMD5.FromBytes(data);

            lock (DBAccessLock.db_access_lock)
            {
                using (var connection = GetConnection())
                {
                    connection.Open();
                    using (var transaction = connection.BeginTransaction())
                    {
                        bool managed_update = false;

                        using (var command = new SQLiteCommand("UPDATE LibraryItem SET MD5=@md5, DATA=@data, LAST_UPDATED_BY=@last_updated_by WHERE filename=@filename", connection, transaction))
                        {
                            command.Parameters.AddWithValue("@filename", filename);
                            command.Parameters.AddWithValue("@last_updated_by", Environment.UserName);
                            command.Parameters.AddWithValue("@md5", md5);
                            command.Parameters.AddWithValue("@data", data);
                            int num_rows_updated = command.ExecuteNonQuery();
                            if (1 == num_rows_updated)
                            {
                                managed_update = true;
                            }
                        }

                        if (!managed_update)
                        {
                            using (var command = new SQLiteCommand("INSERT INTO LibraryItem(filename, last_updated_by, md5, data) VALUES(@filename, @last_updated_by, @md5, @data)", connection, transaction))
                            {
                                command.Parameters.AddWithValue("@filename", filename);
                                command.Parameters.AddWithValue("@last_updated_by", Environment.UserName);
                                command.Parameters.AddWithValue("@md5", md5);
                                command.Parameters.AddWithValue("@data", data);
                                command.ExecuteNonQuery();
                            }
                        }

                        transaction.Commit();
                    }
                    connection.Close();
                }

                //
                // see SO link in ../LibraryDB.cs 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 (singeton) for *all* databases,
                // even while *theoretically* this is *wrong* or rather: *unneccessary* 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 another library while lib A I/O is getting cleaned up.
                //
                // In short: Yuck. + Cave canem.
                //
                SQLiteConnection.ClearAllPools();
            }
        }