diff --git a/ChangeLog b/ChangeLog index f371cb45..84401301 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3770 +1,3774 @@ 2020-04-05 Robby Stephenson * Updated z3950fetcher to separate query and response character encoding (Bug 419670). 2020-03-28 Robby Stephenson * Improved audio file data importer to use Album Artist (Bug 419348). * Added notification if user agent is diabled and data source requires it (Bug 419319). 2020-02-28 Robby Stephenson * Updated data source endpoint for TheGamesDB.net. 2020-02-26 Robby Stephenson * Fixed bug with writing invalid XML names (Bug 418067). +2020-02-15 Robby Stephenson + + * Updated Amazon data source to PA-API v5. + 2020-01-25 Robby Stephenson * Improved MobyGames fetcher (Bug 416718). 2019-12-10 Robby Stephenson * Added data source for colnect.net. 2019-11-25 Robby Stephenson * Fixed performance regression when loading data file. * Released Tellico 3.2.3. 2019-11-09 Robby Stephenson * Released Tellico 3.2.2. 2019-10-20 Robby Stephenson * Fixed bug to revert to behavior of selecting newly added entries (Bug 413217). 2019-10-06 Robby Stephenson * Added importer for LibraryThing.com JSON collection files (Bug 411095). 2019-08-04 Robby Stephenson * Fixed bug with calculating relative file links (Bug 410551). 2019-07-09 Robby Stephenson * Released Tellico 3.2.1. 2019-07-06 Robby Stephenson * Fixed selection bug when modifying an entry after selection changed (Bug 391614). 2019-07-01 Robby Stephenson * Fixed bug with not updating status bar when cancelling an entry update (Bug 325591). 2019-06-16 Robby Stephenson * Fixed file preview generation. * Added data source for Kino-Teatr.ua. 2019-05-25 Robby Stephenson * Released Tellico 3.2. 2019-05-24 Robby Stephenson * Updated data source for TheGamesDB.net (Bug 407811). 2019-04-18 Robby Stephenson * Added data source for ComicVine.com. 2019-03-10 Robby Stephenson * Updated VNDB.org data source. 2019-02-24 Robby Stephenson * Corrected server for National Library of Lithuania (Bug 404743). 2019-02-20 Robby Stephenson * Added data source for MobyGames.com. 2019-02-14 Robby Stephenson * Updated IGDB data source to API v3. 2019-02-10 Robby Stephenson * Improved collection merging efficiency (Bug 349410). 2019-01-19 Robby Stephenson * Fixed bug reading title from KinoPoisk (Bug 403184). 2019-01-04 Robby Stephenson * Fixed bug with setting IMDB rating in various locales (Bug 401894). 2019-01-02 Robby Stephenson * Added Amazon sites for BR, AU, IN, MX, and TR. 2018-12-18 Robby Stephenson * Released Tellico 3.1.4. 2018-12-17 Robby Stephenson * Updated Kino.de data source. 2018-12-09 Robby Stephenson * Updated KinoPoisk.ru data source. 2018-10-30 Robby Stephenson * Updated bedetheque.com data source to use https. 2018-10-24 Robby Stephenson * Fixed bug to escape the group name when filtering in the Group View (Bug 399928). 2018-10-02 Robby Stephenson * Fixed bug when using a regular expression for filtering in a number field (Bug 399323). 2018-09-02 Robby Stephenson * Released Tellico 3.1.3. * Updated TheGamesDB data source. 2018-08-24 Robby Stephenson * Updated Amazon source to include multiple content listings in movie plots (Bug 396211). 2018-08-22 Robby Stephenson * Fixed LTR direction for year within Video template (Debian Bug #904259) 2018-07-08 Robby Stephenson * Fixed bug that cleared selection on modification when a filter was applied (Bug 394343). 2018-07-02 Robby Stephenson * Updated SRU fetcher to allow multiple marc records within a SRW result (DNB). 2018-04-18 Robby Stephenson * Updated IMDb data source (Bug 393239). 2018-04-02 Robby Stephenson * Updated BibTeXML importer. 2018-03-28 Robby Stephenson * Fixed compilation for Qt 5.6 (Bug 392457). 2018-03-28 Robby Stephenson * Released Tellico 3.1.2. * Updated Kino.de data source. 2018-03-18 Robby Stephenson * Fixed bug with inconsistent selection (Bug 391614). * Added a workaround for a crash when linking to both Exempi and KFileMetadata. 2018-02-24 Robby Stephenson * Updated IMDb data source. 2018-02-07 Robby Stephenson * Updated ISBNdb.com data source to v3. 2018-02-05 Robby Stephenson * Fixed "Filter by Group" (Bug 389931). 2018-01-30 Robby Stephenson * Updated MusicBrainz data source to Web Service v2. 2018-01-15 Robby Stephenson * Released Tellico 3.1.1. * Removed deprecated Wine.com data source. 2018-01-14 Robby Stephenson * Updated Kino.de data source. * Updated IGDB data source. 2018-01-09 Robby Stephenson * Fixed bug with OMDB settings not being saved (Bug 388703). * Updated IMDb data source. 2017-12-15 Robby Stephenson * Revert change to clear search dialog on new search (Bug 357799). 2017-11-19 Robby Stephenson * Fixed bug with filter selection (Bug 387130). 2017-11-17 Robby Stephenson * Fixed bug with multi-selection and entry editing (Bug 387053). 2017-11-12 Robby Stephenson * Fixed bug with incorrect entry titles in icon view and multiple entry icons (Bug 386548). * Fixed a few ISO-6937 character encodings. 2017-11-06 Robby Stephenson * Fixed bug for "losing" icons after modifying a collection (Bug 386549). 2017-11-02 Robby Stephenson * Fixed bug with duplicated colons in CSV importer (Bug 386483). 2017-11-01 Robby Stephenson * Improved "Filter by Group" to use a regular expression (Bug 386011). 2017-10-14 Robby Stephenson * Updated Goodreads importer to use https endpoint. 2017-10-13 Robby Stephenson * Released Tellico 3.1. 2017-09-20 Robby Stephenson * Fixed crashing bugs with remote image loading (Bug 382572, Bug 379607, Bug 384104). 2017-09-10 Robby Stephenson * Fixed bug with showing stars in column view for float values (Bug 384547). * Fixed bug with comparing float values (Bug 384547). 2017-08-26 Robby Stephenson * Fixed building with CMake 3.9+ (Bug 382680). 2017-07-16 Robby Stephenson * Added data source for kino.de. 2017-07-07 Robby Stephenson * Added message dialog for Amazon Associate warning (Bug 364784). 2017-07-06 Robby Stephenson * Added DBUS option for filtering exported entries (Bug 382035). 2017-07-02 Robby Stephenson * Fixed bug of running out of memory when writing very large XML files (Bug 380832). * Improved performance for filtering large collections by avoiding needless field value formatting. 2017-06-22 Robby Stephenson * Fixed bug with some icons not being shown (Bug 378477). 2017-05-29 Robby Stephenson * Switched from using libdiscid to libcdio, which includes cdtext. 2017-05-06 Robby Stephenson * Removed Freebase data source. 2017-05-01 Robby Stephenson * Fixed track length for CD audio (Bug 379426). 2017-04-30 Robby Stephenson * Added data source for IGDB.com. 2017-04-16 Robby Stephenson * Fixed bug showing icons for custom collection (Bug 378852). 2017-04-09 Robby Stephenson * Added data source for https://opensource.dbc.dk. * Added PEGI rating to GiantBomb results (Bug 375996). 2017-03-27 Robby Stephenson * Released Tellico 3.0.2. 2017-03-26 Robby Stephenson * Added data source for VideoGameGeek.com 2017-03-25 Robby Stephenson * Updated Douban data source to API v2 (from XML to JSON). 2017-03-24 Robby Stephenson * Fixed bug with image location for reports, introduced in v3.0.1 (Bug 377790). 2017-03-16 Robby Stephenson * Updated the IBS.it data source (Bug 373774). 2017-03-11 Robby Stephenson * Added data source for KinoPoisk.ru. 2017-03-04 Robby Stephenson * Added data source for OMDBAPI.com. 2017-02-20 Robby Stephenson * Released Tellico 3.0.1. 2017-02-12 Robby Stephenson * Removed Microsoft Academic Search data source (defunct). * Updated DBLP data source. 2017-02-08 Robby Stephenson * Fixed relative file locations in HTML export (Bug 376134). 2017-02-05 Robby Stephenson * Fixed image open dialog to remember last location (Bug 376002). * Added ESRB rating for "Everyone 10+" (Bug 375995). 2017-01-30 Robby Stephenson * Fixed Bibsys z39.50 settings (Bug 375758). * Fixed "Report Bug" menu item to link to bugs.kde.org (Bug 375760). * Update MARCXML2MODS stylesheet to version 3.6. 2017-01-29 Robby Stephenson * Fixed Google Scholar data source. 2017-01-15 Robby Stephenson * Fixed bug with Rating drawing size in list view (Bug 372560). 2016-12-19 Robby Stephenson * Fixed bug with truncated first two characters of root folder when importing a file listing (Bug 373918). 2016-11-08 Robby Stephenson * Released Tellico 3.0. 2016-06-15 Robby Stephenson * Added native data source for Bedetheque.com 2016-05-25 Robby Stephenson * Improved performance when deleting many entries. 2016-05-03 Robby Stephenson * Use elided text in Choice combobox to avoid wide windows (Bug 362028). 2016-04-26 Robby Stephenson * Added menu items for each Url field in icon view (Bug 250913). 2016-04-25 Robby Stephenson * Fixed crashing bug with empty table rows (Bug 361622). 2016-04-16 Robby Stephenson * Fixed filter comparison with custom dates (Bug 361625). 2016-01-15 Sven Werlen * Fixed "Defaults" button in Collection Fields dialog (Bug 357637). 2015-12-06 Robby Stephenson * Released Tellico 2.3.11. 2015-10-25 Robby Stephenson * Removed the Yahoo fetcher. 2015-10-22 Robby Stephenson * Added user-defined query pairs for SRUFetcher url. 2015-08-22 Robby Stephenson * Improved PDF importer for Science Direct files. 2015-08-16 Robby Stephenson * Fixed key accelerator conflict for 'D' in Collection Fields dialog (Bug 351226). 2015-08-05 Robby Stephenson * Fixed bug with using external fetcher source with MODS output. 2015-07-19 Robby Stephenson * Fixed a crashing bug with Nepomuk and certain file types (Bug 345458). 2015-06-02 Robby Stephenson * Fixed a bug with HTML exporting titles with single-quotes (Bug 348381). 2015-05-27 Robby Stephenson * Removed the Citebase fetcher. 2015-05-24 Robby Stephenson * Fixed a bug showing horizontal scroll bar with many table columns (Bug 348189). 2015-03-15 Robby Stephenson * Removed the PilotDB exporter. 2015-02-15 Robby Stephenson * Released Tellico 2.3.10. 2015-02-14 Robby Stephenson * Updated the Moviemeter data source to use the new JSON API. * Removed the local KXmlRpc copy since it is no longer needed. * Updated cover path in DarkHorse fetcher. 2015-01-23 Robby Stephenson * Updated the Discogs data source to use the new JSON API. 2014-12-22 Robby Stephenson * Added filter rules for numbers less than and greater. 2014-12-12 Robby Stephenson * Converted the BoardGameGeek fetcher from a script to the XML API. 2014-12-06 Robby Stephenson * Added importer for BoardGameGeek collections. 2014-12-05 Robby Stephenson * Fixed bug with importing Goodreads collection. 2014-11-09 Robby Stephenson * Added workaround for incorrect cover thumbnail in BoardGameGeek fetcher. 2014-10-20 Robby Stephenson * Updated CSV importer to recognize LibraryThing files. 2014-10-19 Robby Stephenson * Fixed bug with Allocine API search using punctuation (Bug 337432). 2014-10-11 Robby Stephenson * Added data source for Mathematical Review. * Fixed crashing bug with some ISBNdb results (Bug 339063). * Updated Producer results for IMDb and TheMovieDB fetchers (Bug 336765). 2014-10-11 Pino Toscano * Update internal libcsv copy to 3.0.3. 2014-10-09 Pino Toscano * Switch to using libdiscid for reading audio CD info. 2014-06-22 Robby Stephenson * Released Tellico 2.3.9. 2014-05-26 Robby Stephenson * Fixed bug with writing out link-only images in HTML exporter (Bug 330649). 2014-05-25 Robby Stephenson * Fixed character encoding in Allocine fetcher (Bug 334527). * Removed IMDb country choice since it's now unavailable (Bug 330641). 2014-04-27 Robby Stephenson * Disabled Discogs image fetching since OAuth is now required. 2014-04-17 Robby Stephenson * Fixed CSV importer bug, causing a hang (Bug 329677, Debian Bug 729503). 2014-01-31 Robby Stephenson * Updated TheMovieDB fetcher to API version 3. 2014-01-30 Robby Stephenson * Fixed crashing bug in IMDB fetcher (Bug 330591). 2013-11-14 Robby Stephenson * Fixed crashing bug in command-line importing (Debian bug 729499). 2013-11-08 Robby Stephenson * Fixed type error in Microsoft Academic Search fetcher. 2013-11-07 Robby Stephenson * Fixed error in setting modified date for entry in certain cases (Bug 326911). 2013-11-06 Robby Stephenson * Improved GCstar import and export to handle custom GCstar fields. 2013-10-13 Robby Stephenson * Updated IMDb fetcher (Bug 325673). 2013-07-07 Robby Stephenson * Released Tellico 2.3.8. 2013-07-04 Robby Stephenson * Disabled fetchers for Spanish Ministry of Culture, Beyazperde, Filmstarts, ScreenRush, and Sensacine. 2013-07-01 Robby Stephenson * Updated ISBNdb.com fetcher to use v2 of API. 2013-06-09 Robby Stephenson * Added Bibtex importing for drag/drop text (Bug 319182). 2013-06-08 Robby Stephenson * Fixed Entrex/Pubmed fetcher for summary requests (Bug 319501). 2013-04-05 Robby Stephenson * Fixed bug with retaining allowed values when adding entries from data sources (Bug 317905). 2013-03-27 Robby Stephenson * Fixed bug with not properly escaping text in CSV exporter (Bug 317473). 2013-03-24 Robby Stephenson * Updated GiantBombFetcher for XML changes in responses. 2013-03-11 Robby Stephenson * Fixed regexp in Google Scholar fetcher to set cookie (Bug 316550). 2013-03-10 Robby Stephenson * Update Allocine API to use JSON instead of XML. 2013-03-03 Robby Stephenson * Add coverartarchive.org to MusicBrainz data source. 2013-02-09 Robby Stephenson * Fixed character encoding in IMDB results list (Bug 314113). 2013-02-02 Robby Stephenson * Added fetcher for VNDB.org. 2013-01-27 Robby Stephenson * Added Dewey Decimal and LCC to ISBNdb.org results. 2013-01-26 Robby Stephenson * Fixed crashing bug with OpenLibrary fetcher. 2013-01-23 Robby Stephenson * Released Tellico 2.3.7. 2013-01-18 Robby Stephenson * Fixed a bug with the number field not catching a modified value (Bug 313304). 2013-01-08 Robby Stephenson * Fixed a bug with editing the toolbar config in KDE 4.9.2+. 2012-12-07 Robby Stephenson * Added a config setting for using a Google API key for book searches. * Updated fetcher for IMDb. 2012-11-21 Robby Stephenson * Added VinoXML importer. 2012-11-03 Robby Stephenson * Added fetcher for DBLP.org. 2012-10-14 Robby Stephenson * Fixed a bug that caused loans to fail to get checked-in (Bug 307958). 2012-09-18 Robby Stephenson * Added fetcher for thegamesdb.net. 2012-09-09 Robby Stephenson * Changed Griffith importer to use XML files instead of python script and assuming a sqlite3 database. 2012-09-03 Robby Stephenson * Updated Delicious Library importer. 2012-08-26 Robby Stephenson * Removed API key configuration for Discogs since it's no longer needed. 2012-08-22 Robby Stephenson * Corrected character encoding for DVDfr search. * Corrected title vs. original title in DVDfr results. * Corrected actor and role order in Allocine fetcher. 2012-08-07 Robby Stephenson * Fixed a bug with adding new fields when importing bibtex (Bug 304767). 2012-07-13 Robby Stephenson * Released Tellico 2.3.6. 2012-06-10 Robby Stephenson * Added simple importer for CIW files from ISI. 2012-05-30 Robby Stephenson * Added data sources for IMDB in French, Spanish, German, Italian, and Portuguese. 2012-05-12 Robby Stephenson * Fixed a bug with loading images from Google Book search (Bug 299789). 2012-05-06 Robby Stephenson * Added API data source for HathiTrust. 2012-05-04 Robby Stephenson * Fixed a bug when using a local image directory for a file that has not yet been saved (Bug 299130). 2012-04-20 Robby Stephenson * Added API data sources for Allocine, ScreenRush, FilmStarts, SensaCine, and Beyazperde. 2012-03-23 Robby Stephenson * Fixed bugs in IBS.it data source. 2012-03-14 Robby Stephenson * Added data source for Springer Link. 2012-03-09 Robby Stephenson * Added data source for the Microsoft Academic Search. 2012-02-26 Robby Stephenson * Fixed a crashing bug when entering multiple ISBN values in search. * Improved Amazon search for US UPC values from non-UPC sites. 2012-01-15 Robby Stephenson * Fixed a crashing bug when using the scanner dialog. * Released Tellico 2.3.5. 2011-12-26 Robby Stephenson * Fixed bug with merging entries. Entry IDs will never be different so don't warn the user (Bug 289346). * Fixed bug with Cancel not working in the Entry Merge dialog. 2011-12-17 Robby Stephenson * Refactored fetcher tests to check for network access before running * Updated Discogs fetcher to use API v2 2011-12-14 Robby Stephenson * Fixed importing of multi-line notes from Alexandria (Bug 289022). 2011-11-30 Robby Stephenson * Updated fetcher for Allocine.fr. 2011-10-28 Robby Stephenson * Added filter rules for dates before and after. 2011-10-13 Robby Stephenson * Fixed broken script for searching Dark Horse Comics. 2011-10-05 Robby Stephenson * Fixed bug that always showed checkmark for boolean field values (Bug 283444). 2011-10-04 Robby Stephenson * Updated GCstar exporter to include images. 2011-09-27 Robby Stephenson * Added Amazon data sources for China, Spain, and Italy. 2011-09-24 Robby Stephenson * Released Tellico 2.3.4. 2011-09-18 Robby Stephenson * Fixed bug when editing month in the date widget in KDE 4.7 (Bug 281365). 2011-08-30 Robby Stephenson * Fixed showing a doubled filter count (Bug 281082). 2011-08-21 Robby Stephenson * Added data source for Google Book search. 2011-07-23 Robby Stephenson * Updated filter rules to match against values without diacritics for "Contains" criters (Bug 222400). 2011-07-20 Robby Stephenson * Fixed bug in year from Douban.com data source. * Fixed TMDB search for multiple person results. 2011-07-01 Robby Stephenson * Added importer for MovieMeter.nl. 2011-06-24 Robby Stephenson * Fixed crashing bug with CSV importer and changing delimiters. 2011-05-29 Robby Stephenson * Made Entry View always visible. * Moved icon view to share space with the list view (Bug 250912). 2011-05-20 Robby Stephenson * Added importer for Goodreads collections. 2011-05-13 Pedro Miguel Carvalho * Added shortcut key to switch main window to/from full screen state. * Added shortcut key to show/hide menubar (Bug 251157). 2011-05-07 Pedro Miguel Carvalho * Added UI controls for changing icon size (Bug 250907). 2011-05-07 Robby Stephenson * Fixed bug with image link in Tri-Column report template (Bug 272744). 2011-05-06 Robby Stephenson * Added French, Spanish, and German codes for TheMovieDB. * Added icon cache to improve performance with large images (Patch from Pedro Miguel Carvalho, Bug 272583). 2011-05-01 Robby Stephenson * Changed image loading to use known id instead of recalculating, improves performance. 2011-04-10 Robby Stephenson * Improved performance for loading and sorting large collections. * Released Tellico 2.3.3. 2011-04-04 Robby Stephenson * Fixed bug with loans not being updated for removed entries (Bug 270129). 2011-03-21 Robby Stephenson * Fixed crashing bug for editing some values (Bug 269044). 2011-03-19 Robby Stephenson * Fixed editing existing filters (Bug 268829). 2011-03-07 Robby Stephenson * Added Manga search for AnimeNfo. 2011-02-27 Robby Stephenson * Fixed parsing bug with AnimeNfo results. 2011-02-23 Regis Boudin * Updated videodev include for compatibility with Linux kernel 2.6.38+. 2011-02-21 Robby Stephenson * Added data source for douban.com. 2011-02-19 Robby Stephenson * Added data source fetcher for filmaster.com. 2011-02-18 Regis Boudin * Fixed build with GCC 4.6. 2011-02-13 Robby Stephenson * Added check for duplicate bibtex keys (Bug 245225). 2011-02-02 Robby Stephenson * Added capability to import ADS format for z39.50 sources. 2011-01-11 Robby Stephenson * Fixed bug with parsing empty table values (Bug 261108). 2011-01-03 Robby Stephenson * Fixed parsing bug with IMDb results (Bug 262036). 2010-12-12 Robby Stephenson * Released Tellico 2.3.2. 2010-12-11 Robby Stephenson * Updated Bibtex handler to translate ~ to non-breaking space. 2010-12-08 Robby Stephenson * Updated Allocine script to version 0.7.3 (Bug 258281). 2010-12-03 Robby Stephenson * Fixed sorting multiple numeric values in column view. * Fixed sorting for numeric values in group view. 2010-12-01 Robby Stephenson * Fixed bug with FreeDB results not using track artists (Bug 258541). * Fixed bug with importing Bibtex file with keyword and keywords fields (Bug 258269); 2010-11-23 Robby Stephenson * Fixed Google Scholar fetcher to properly set bibtex cookie. 2010-11-21 Robby Stephenson * Added cover art to MusicBrainz fetcher. 2010-11-20 Robby Stephenson * Switched Nepomuk requirement to optional for file metadata. 2010-11-14 Robby Stephenson * Fixed bug with updating groups for derived values (Bug 256374). 2010-11-10 Robby Stephenson * Fixed bug with list view settings not being saved between sessions (Bug 256373). 2010-11-06 Robby Stephenson * Released Tellico 2.3.1. 2010-11-02 Robby Stephenson * Fixed edit dialog behavior when closing to be consistent with discarding data (Bug: 255938). 2010-10-21 Robby Stephenson * Fixed error in Italian translation that caused HTML error and add workaround for future problems (Bug: 254863). 2010-10-17 Robby Stephenson * Updated Freebase source for adapted /music/release schema. 2010-10-14 Robby Stephenson * Updated IMDb data source for new layout (Bug: 253549). 2010-10-13 Robby Stephenson * Added options in CSV importer and exporter for table delimiters. 2010-09-20 Robby Stephenson * Changed URL field output to truncate link text to 30 letters (Bug: 250880). 2010-09-12 Robby Stephenson * Increased max icon size to 256. 2010-09-11 Robby Stephenson * Changed updating match algorithm to try to improve results when having multiple good matches (Bug: 250886). 2010-09-10 Robby Stephenson * Updated GCstar plugin fetcher to use a separate thread. * Fixed crash with z39.50 fetcher (Bug: 250795). 2010-09-07 Robby Stephenson * Updated GCstar plugin fetcher to work with comic books. 2010-09-03 Robby Stephenson * Fixed parsing for director and writer in IMDB fetcher (Bug: 249096). 2010-09-02 Robby Stephenson * Updated DTD and added unit test for validation. 2010-08-29 Robby Stephenson * Added GCstar import/export for comic book collections. 2010-08-28 Robby Stephenson * Fixed bug with adding new fields during CSV import. 2010-08-25 Robby Stephenson * Added GCstar export for wine collections. * Improved GCstar import for wine collections. 2010-08-21 Robby Stephenson * Fixed filter view to apply filter when item is selected (Bug: 248657). 2010-08-16 Robby Stephenson * Improved exporter to add option for limiting exported fields (Bug 246390). 2010-08-13 Robby Stephenson * Added data source fetcher for dvdfr.com. 2010-08-07 Robby Stephenson * Released Tellico 2.3. 2010-07-29 Robby Stephenson * Fixed sorting by rating when rating has double digits (Bug: 246202). 2010-07-25 Robby Stephenson * Fixed IMDb matching on partial titles (Bug: 245665). * Fixed IMDb fetching of alternative titles. 2010-07-11 Robby Stephenson * Fixed IMDb fetching of audio track and color (Bug: 244159). 2010-07-10 Robby Stephenson * Fixed metadata import for file listing. 2010-07-09 Robby Stephenson * Added Dewey Decimal and LoC Classification to z3950 MODS import. * Fix subtitle value in MODS import. 2010-06-19 Robby Stephenson * Added filter for data source list. 2010-05-04 Robby Stephenson * Updated BoardGameGeek fetcher for new API. 2010-05-02 Robby Stephenson * Added data source for Freebase. 2010-04-29 Robby Stephenson * Fixed crashing bug for loading images whose size exceeded the cache size. 2010-04-28 Robby Stephenson * Changed Amazon Japan video search to include DVDs. * Added new fetcher for combining results from multiple sources. 2010-04-27 Robby Stephenson * Updated allocine.fr script to version 0.7.1, patch from Romain Henriet. 2010-03-29 Robby Stephenson * Fixed bug with cursor position in date field entry. 2010-03-27 Robby Stephenson * Added fetcher for openlibrary.org (requires QJSON lib). 2010-03-24 Robby Stephenson * Added clear button for rating widget (BUG: 227982). * Control characters are now stripped from all text files on import (BUG: 231302). * Removed local FindKcddb cmake module in favor of system FindKdeMultimedia. 2010-03-23 Robby Stephenson * Fixed selection bug when duplicating an entry (BUG: 231125). 2010-03-22 Robby Stephenson * Fixed crash in exporting HTML for collections with no grouping field (BUG: 231302). * Fixed bug that showed info dialog multiple times when changing image location. 2010-02-24 Robby Stephenson * Fixed crash in CSV importer. 2010-02-21 Robby Stephenson * Tweaked entry ID values to start at 1 instead of 0. 2010-02-13 Robby Stephenson * Released Tellico 2.2. 2010-02-06 Robby Stephenson * Enabled Korganizer integration for tracking loans. 2010-01-27 Robby Stephenson * Fixed some hyphenation problems when typing 978 ISBN values. * Enabled KAddressBook integration for borrowers. 2010-01-15 Robby Stephenson * Fixed bug in en_GB translation that affected file selection. 2010-01-13 Robby Stephenson * Enabled filter dialog to apply empty filter. 2010-01-03 Robby Stephenson * Improved performance for modifying many entries at once. 2010-01-01 Robby Stephenson * Added Giant Bomb data source. 2009-12-31 Robby Stephenson * Fixed adding "link-only" files (BUG #220645). * Fixed Discogs track download and title search. 2009-12-22 Robby Stephenson * Updated CrossRef data source to allow authentication via email address. * Updated Crossref data source for new schema. 2009-12-19 Robby Stephenson * Added MusicBrainz data source. 2009-12-18 Robby Stephenson * Fixed setting correct permissions of backup file (BUG #219259). * Fixed formatting of multiple people with auto-formatting (BUG #219268). 2009-12-16 Robby Stephenson * Added themoviedb.org data source. 2009-12-12 Robby Stephenson * Improve updating from Amazon to include book title or album title in search. 2009-12-04 Robby Stephenson * Added option for turning webcam off. 2009-11-30 Robby Stephenson * Enabled JavaScript for report dialog, incase a template wants to use it. * Fixed crashing bug for sorting during HTML export. * Fixed multiple selection in entry view (KDE #216122). 2009-11-20 Robby Stephenson * Fixed IMDB search to find director and writers. * Fixed Polish ISBN formatting. * Released Tellico 2.1.1. 2009-11-17 Robby Stephenson * Fixed sorting in icon view. 2009-11-16 Robby Stephenson * Fixed bug that kept filters from being saved. BUG#214672. 2009-11-12 Robby Stephenson * Updated script for searching allocine.fr. 2009-11-11 Robby Stephenson * Changed webcam image preview to only appear when searching by ISBN or UPC/EAN. 2009-11-10 Robby Stephenson * Fixed sorting when auto-formatting is on. 2009-11-06 Robby Stephenson * Fixed crashing bug related to some table values. * FIxed crashing bug related to column sorting. BUG#214661. 2009-11-01 Robby Stephenson * Released Tellico 2.1. 2009-10-31 Robby Stephenson * Fixed crashing bug for loading ISBN list from file. 2009-10-22 Robby Stephenson * Fixed bug with the sort order not being saved. 2009-10-21 Robby Stephenson * Fixed bug that auto-formatting was not always applied. 2009-10-11 Robby Stephenson * Fixed grouping to always show group for empty value. * Fixed crashing bug when filtering entry with empty title. * Fixed compilation for KDE < 4.2. * Fixed filtering bug when modifying entry value. 2009-10-10 Robby Stephenson * Added PAM/PRISM translator to SRU fetcher. 2009-10-07 Robby Stephenson * Updated Amazon and Crossref search to no longer store key in the KWallet. 2009-10-03 Robby Stephenson * Fixed bug to allow multiple values in table columns. 2009-09-30 Robby Stephenson * Added Wine.com data source. 2009-09-29 Robby Stephenson * Updated GCstar import support for video games and board games. 2009-09-24 Robby Stephenson * Updated ISBNdb.com fetcher and Discogs fetcher to allow user access keys. 2009-09-23 Robby Stephenson * Updated the SRU fetcher to leave the response format empty. 2009-09-22 Robby Stephenson * Fixed crash when exporting to HTML under certain conditions. * Removed the GCcfilms exporter and added the GCstar exporter. 2009-09-21 Robby Stephenson * Fixed typo in DTD. 2009-09-20 Robby Stephenson * Released Tellico 2.0. 2009-09-07 Robby Stephenson * Enabled multiple ISBN and LCCN search for SRU fetcher. 2009-09-06 Robby Stephenson * Updated the "Multiple ISBN" dialog box to validate ISBN and CueCat input. 2009-09-04 Robby Stephenson * Updated webcam support to use libv4l library. 2009-09-01 Robby Stephenson * Released Tellico 2.0pre2. 2009-08-23 Robby Stephenson * Added default fields for entry creation date and last-modified date. * Removed dependency on QCA2 library. 2009-08-09 Robby Stephenson * Updated Tellico user mailing list address. * Released Tellico 2.0pre1. 2009-08-01 Robby Stephenson * Updated HTML export to use country and language code both for sorting. * Updated Tellico home page URL. * Moved to document syntax 11 for removing Dependent and ReadOnly fields. * Added ID field to each new collection by default. 2009-03-13 Petri Damstén * Added scanning support to image widget. * Added 'Open With...' -button to image widget 2009-03-10 Robby Stephenson * Added reading album artist from TPE2 tags in mp3 files. 2009-02-14 Robby Stephenson * Released Tellico 1.3.5. 2009-02-04 Robby Stephenson * Reverted change from 2007 that merged entries by combining all values in multiple-value fields. 2009-02-02 Robby Stephenson * Fixed the CueCat decoder to work for ISBN searches, as well as UPC. 2009-01-30 Robby Stephenson * Updated Deliciour Library 1 importer to handle movies and games. * Fixed Ubuntu bug#317822, don't mark collection modified when image is found in local data directory. * Fixed query bug with z39.50 ISBN search. 2008-12-07 Robby Stephenson * Updated it.po, thanks to Valerio Ricci. 2008-10-23 Robby Stephenson * Added date, time, and username as available params for the XSLT export. 2008-09-13 Robby Stephenson * Released Tellico 1.3.4. 2008-09-04 Robby Stephenson * Fixed error with consecutive tabs in CSV import. 2008-08-26 Robby Stephenson * Fixed bug with image links in reports not being linked correctly. 2008-08-20 Robby Stephenson * Added minimal searching for board games from Amazon. 2008-08-07 Robby Stephenson * Changed Choice fields to sort by position in list. 2008-07-25 Robby Stephenson * Added ESC key for clearing quick filter. * Improved drag/drop to match on file extension after checking mimetype. 2008-07-22 Robby Stephenson * Updated IMDb plot regexp. 2008-07-09 Robby Stephenson * Fixed EntryView to honor copy() command properly, for clipboard. * Released Tellico 1.3.3. 2008-06-24 Robby Stephenson * Updated Porbase URL in z39.50 server list. 2008-06-13 Robby Stephenson * Updated drag-and-drop to allow HTTP urls. 2008-06-12 Robby Stephenson * Changed Arxiv fetcher to remove ID version number for results. 2008-06-07 Robby Stephenson * Fixed bug with merging file catalogs, to properly match on URL. 2008-05-24 Robby Stephenson * Released Tellico 1.3.2.1. * Fixed en_GB translation to remove context strings. 2008-05-23 Robby Stephenson * Released Tellico 1.3.2. * Include Canadian LCC values, patch from Peter Stevenson. 2008-05-19 Robby Stephenson * Updated entry creation to always add default values. Fixes Debian bug 481639. 2008-04-29 Robby Stephenson * Improved IMDb search results. 2008-04-15 Robby Stephenson * When adding a book to a bibliography, set the type to 'book'. 2008-04-11 Robby Stephenson * Updated LCCN search to validate format. * Updated entry match to compare validated LCCN. * Updated z39.50 search to allow multiple LCCN values. 2008-04-09 Robby Stephenson * Changed loader to eliminate possible duplicates for fields allowing multiple values (like keywords). * Updated PubMed search to add all keywords. * Fixed PubMed search to use utf8 encoding. 2008-04-08 Robby Stephenson * Changed XML loader to strip whitespace from non-paragraph values. 2008-04-06 Robby Stephenson * Added languages and editors to Amazon book import. 2008-03-15 Robby Stephenson * Updated mods2tellico.xsl to try to capture more of mods info converted from refbase. 2008-03-13 Robby Stephenson * Updated mods2tellico.xsl to try to capture more of bibtex info converted from wikindx. 2008-03-10 Robby Stephenson * Released Tellico 1.3.1. * Added Discogs.com fetcher. 2008-03-06 Robby Stephenson * Added Google Scholar search. 2008-03-05 Robby Stephenson * Fixed bug that prevented using bibtex format for external scripts. 2008-03-03 Robby Stephenson * Changed "ISBN not found" dialog to only appear if multiple ISBN values are being searched. 2008-03-01 Robby Stephenson * Fixed bug with SRU format not getting remembered in config dialog. * Fixed bug with entries with multiple titles not getting linked in the HTML export. 2008-02-29 Robby Stephenson * Added LCCN lookup to z39.50 source. * Added LCCN lookup to SRU source. * Updated CrossRef fetcher to use new unixref format with more data. 2008-02-26 Robby Stephenson * Added DOI search to Entrez/Pubmed interface. * Fixed bug with some free-form date fields not getting formatted correctly, resulting in a blank string in column view and reports. 2008-02-12 Robby Stephenson * Improved loader to delay loading linked images as long as possible. 2008-02-11 Robby Stephenson * Fixed Bibtex import for keywords field. 2008-02-07 Robby Stephenson * Updated Delicious Library importer to look for cover images. * Updated BoardGameGeek script to grab cover image, patch from Sven Werlen. 2008-01-29 Robby Stephenson * Released Tellico 1.3. * Fixed bug with stepping through selected entries after editing one. 2008-01-20 Robby Stephenson * Fixed bug with matching ISBN values for updating entries. 2008-01-11 Robby Stephenson * Released Tellico 1.3pre1. 2008-01-09 Robby Stephenson * Updated Spanish Ministry of Culture script. 2008-01-05 Robby Stephenson * Updated IMDB search to fix director and TV cast. 2007-12-11 Robby Stephenson * Added DCOP calls to set the view filter and to show entries. * Improved filter to allow specifying field name with '='. 2007-12-10 Robby Stephenson * Added importer for Delicious Library files. 2007-12-06 Robby Stephenson * Updated Dependant field parsing to allow %{id} to grab entry id. * Added context menu option in icon view to change sorting. * Extended the DCOP interface to allow adding and removing entries, reading field values, and setting field values. 2007-11-30 Robby Stephenson * Added capability to save a link to an image only, instead of saving it to the users image directory. Setting an image field property link=true will make that the default. 2007-11-26 Robby Stephenson * Updated to allow a "required" property to be included to indicate that a on-empty value should always be present when saving. * Updated to use libcsv from Robert Gamble, allowing newlines 2007-11-25 Robby Stephenson * Fixed bug with creating filter from group item. * Added Ukrainian translation from Serhij Dubyk Сергій Дубик. 2007-11-15 Robby Stephenson * Added a sharedmimeinfo file. 2007-11-13 Robby Stephenson * Added Bibsonomy fetcher. * Added citebase.org fetcher. 2007-11-06 Robby Stephenson * Added option for importing bibtex in Unicode (UTF-8). * Added importing from Referencer. 2007-11-05 Robby Stephenson * Added arxiv.org API fetcher. 2007-11-04 Robby Stephenson * Fixed bugs related to compilation with gcc 4.3. 2007-10-27 Robby Stephenson * Added option for saving images in local relative directory to data file. 2007-10-26 Robby Stephenson * Added menu command for merging entries, based on patch from Cyril Dangerville. 2007-10-24 Robby Stephenson * Added CrossRef search for DOI. 2007-10-21 Robby Stephenson * Fixed list view sorting to take into account automatic formatting. * Fixed date sorting to put empty dates first. 2007-10-19 Robby Stephenson * Fixed sorting bug for images. * Fixed sorting bug for empty dates in HTML export, patch from Jake Maciejewski. 2007-10-15 Robby Stephenson * Fixed crashing bug in bibtex importer. 2007-10-14 Robby Stephenson * Added PDF importer, using exempi XMP library. * Added drag & drop support for PDF importer. 2007-10-13 Robby Stephenson * Added DOI and URL to default bibtex collection fields. 2007-09-23 Robby Stephenson * Added data source for using GCstar plugins. * Fixed bug with escaping the image id for file names. 2007-09-22 Robby Stephenson * Released Tellico 1.2.14. 2007-09-20 Robby Stephenson * Added importer for Griffith database. 2007-09-10 Robby Stephenson * Updated Spanish Ministry of Culture script to search out of prints book and add notes field. * Updated tellico2html.js file for searching in HTML export. * Updated GCstar import to parse new XML format in GCstar version 1.2. 2007-08-27 Robby Stephenson * Updated allocine.fr source script to version 0.4, thanks to Mathias Monnerville. * Updated the Amazon.com search to allow searching for comic books. * Updated the isbndb.com search to allow searching for comic books. * Fixed ISBNdb search. 2007-08-25 Robby Stephenson * Changed default SRU port from 7090 to 80. 2007-08-23 Robby Stephenson * Update CSS in HTML export. 2007-08-10 Robby Stephenson * Fixed bug with yaz_iconv not flushing complete string for z39.50 search. 2007-08-09 Robby Stephenson * Added MARCXML to possible SRU format. * Fixed bug with MARC stylesheets to work with better with embedded records. 2007-08-03 Robby Stephenson * Updated entry updating to work with all collection types. 2007-07-28 Robby Stephenson * Released Tellico 1.2.13. 2007-07-24 Robby Stephenson * Added spell-check for text and paragraph fields. 2007-07-22 Robby Stephenson * Added compatibility with yaz3. 2007-07-19 Robby Stephenson * Added importer for GCstar files. * Fixed bug with namespace handling in Tellico XML loading. 2007-07-14 Robby Stephenson * Added Copac and National Library of Lithuania to z39.50 list. 2007-07-08 Robby Stephenson * Fixed crashing bug when adding items to a collection with loans. 2007-07-04 Robby Stephenson * Released Tellico 1.2.12. * Changed z39.60 search event loop in an attemp to fix some intermittent freezes. 2007-06-26 Robby Stephenson * Changed Quick Filter to split text on white space and do an AND search. * Bumped automake requirement to version 1.8 or later for macro to work. * Fixed HTML export to not rewrite file location for files referenced in the XSL file which don't exist. * Fixed Column View report to sort numerically when needed. * Fixed Date fields to suppress empty date values. * Fixed Date comparisons to work for single digits, patch from Jake Maciejewski. * Fixed Fields Dialog to show warnings when clicking Ok. 2007-06-22 Robby Stephenson * Added '\%' to bibtex translation table for comment escaping. 2007-05-30 Robby Stephenson * Fixed potential recursion bug for dependent fields. 2007-05-26 Robby Stephenson * Fixed bug that didn't write image size options when printing. 2007-05-09 Robby Stephenson * Added Turkish translation, thanks to Ali Isingor. 2007-05-08 Robby Stephenson * Released Tellico 1.2.11. 2007-05-06 Robby Stephenson * Improved file saving performance by caching image info on load. 2007-05-04 Robby Stephenson * Fixed bug that lost some images when loading directly from XML. 2007-04-13 Robby Stephenson * Fixed sorting for Dependent fields to match on subfields. 2007-04-12 Robby Stephenson * Fixed BibteXML export to include author. * Fixed book collection conversion to bibliography to set entry type. 2007-04-10 Robby Stephenson * Updated IMDb search for director and writer. * Fixed CDDB lookup on OpenBSD, patch from Marc Espie. 2007-04-07 Robby Stephenson * Released Tellico 1.2.10. 2007-04-03 Robby Stephenson * Improved accuracy and comprehensiveness of merging collections. * Changed collection merge to concatenate paragraph fields. * Fixed bug that hid some field groups after undoing append collection. 2007-04-01 Robby Stephenson * Added Blu-ray and HD DVD to video formats. 2007-03-31 Robby Stephenson * Updated ibs.it searching. * Fixed searching to replace all HTML entities. * Fixed character encoding for Alexandria import. 2007-03-29 Robby Stephenson * Updated Amazon API to 2007-02-22. * Fixed error handling for ISBN-13 searches. * Fixed bug that showed "ISBN Not Found" dialog in error. 2007-03-27 Robby Stephenson * Cleaned-up up ONIX export a bit. * Fixed Alexandria export. * Fixed importing covers from Alexandria. 2007-03-24 Robby Stephenson * Fixed bug that caused some amazon results to be hidden when repeating a search. 2007-03-20 Robby Stephenson * Fixed hanging bug when stopping a z39.50 search. 2007-03-14 Robby Stephenson * Fixed crashing bug when undoing a collection import or replacement. 2007-03-01 Robby Stephenson * Released Tellico 1.2.9. 2007-02-25 Robby Stephenson * Relaxed MODS processing to allow any result with typeOfResource="text" * Fixed bug with adding space after commas in edit widget. * Fixed bug with formatting of dependent fields always being capitalized. 2007-02-20 Robby Stephenson * Updated IMDb parsing for alternative titles and certifications. 2007-02-06 Robby Stephenson * Fixed sorting for rating groups with rating = 10. 2007-02-03 Robby Stephenson * Released Tellico 1.2.8. 2007-01-22 Robby Stephenson * Updated CDDB import to grab extd data as comments, and category as keyword. 2007-01-21 Robby Stephenson * Updated audio file importer to take disc number into account for mp3, ogg, and flac files. 2007-01-13 Robby Stephenson * Changed IMDB rating to allow float values. * Fixed compile error for z3950 connection, patch from Markus Brueffer. 2007-01-04 Robby Stephenson * Fixed cleanup for deleting scripts installed from newstuff. 2007-01-03 Robby Stephenson * Fixed busy cursor hanging in newstuff download dialog. 2006-12-21 Robby Stephenson * Fixed "ISBN Not Found" dialog to have selectable text. 2006-12-19 Robby Stephenson * Fixed bug with comparing relative URLs for merging file catalogs. 2006-12-17 Robby Stephenson * Fixed bug with some results in z39.50 search not showing up, due to search events getting processed out of order. 2006-12-07 Robby Stephenson * Fixed bug with secondary and tertiary sorting. 2006-12-06 Robby Stephenson * Bumped the Amazon ECS version to 2006-11-30. * Updated Amazon search to allow ISBN-13 values. * Fixed bug with saving URLs without the protocol. * Changed entry comparison for files to match only on URL value. * Released Tellico 1.2.7. 2006-12-04 Robby Stephenson * Improved performance for copying and deleting multiple entries. 2006-11-30 Robby Stephenson * Updated sorting to take title articles into account. 2006-11-29 Robby Stephenson * Added ISBN hyphenation rules for German, Czech, and Dutch. * Fixed bug that caused image loss when loading from data file. 2006-11-10 Robby Stephenson * Released Tellico 1.2.6. 2006-11-09 Robby Stephenson * Improved TV show matching for IMDB. 2006-11-05 Robby Stephenson * Fixed crashing bug in ProgressItem, reported by Izaak Branderhorst. 2006-11-03 Robby Stephenson * Released Tellico 1.2.5. 2006-11-01 Robby Stephenson * Fixed another bug with over-writing images on entry update. 2006-10-24 Robby Stephenson * Updated yahoo audio search webservice url. 2006-10-18 Robby Stephenson * Added warning when importing CSV without assigning any fields. * Updated allocine script (v 0.3), from Mathias Monnerville. 2006-10-16 Robby Stephenson * Added board game collection, patch from Steve Beattie. * Added script for searching boardgamegeek.com, patch from Steve Beattie. 2006-10-15 Robby Stephenson * Branched 1.2.x series. * Fixed encoding bug with allocine.fr script. 2006-10-14 Robby Stephenson * Released Tellico 1.2.4. 2006-10-12 Robby Stephenson * Fixed bug with overwriting images when updating entries. 2006-10-11 Robby Stephenson * Improved performance on loading and unloading. * Changed Nintendo 'Revolution' to 'Wii'. 2006-10-05 Robby Stephenson * Fixed off-by-one error in file listing volume name reader. * Update allocine fetcher. 2006-09-25 Robby Stephenson * Fixed bug with fetch dialog showing all sources, instead of just relevant ones. 2006-09-24 Robby Stephenson * Released Tellico 1.2.3. 2006-09-20 Robby Stephenson * Added button to search dialog to get additional results. 2006-09-14 Robby Stephenson * Updated to work with KDE 3.3.1 or later. 2006-09-09 Robby Stephenson * Fixed bug with HTML export not including tellico2html.js. 2006-09-07 Robby Stephenson * Fixed bug that could cause loss of images. * Fixed URL output in Image List report. * Released Tellico 1.2.2. 2006-09-06 Robby Stephenson * Released Tellico 1.2.1. 2006-09-05 Robby Stephenson * Fixed CDDB cache importer to always assume utf-8, as in libkcddb. * Changed bibtex exporter to add braces or quotes around url fields. 2006-09-04 Robby Stephenson * Fixed infinite loop in file listing importer. * Increased default image cache size to 10 meg. * Reworked image loading to avoid reloading large images as much as possible. 2006-09-02 Robby Stephenson * Fixed bug with CDDB not working (always using debug for Pink Floyd!) 2006-09-01 Robby Stephenson * Fixed bug with completion not working in filter dialog. * Fixed bug with template image gradients not getting updated properly. 2006-08-31 Robby Stephenson * Released Tellico 1.2. 2006-08-30 Robby Stephenson * Fixed crashing bug when using right-click in list view. 2006-08-16 Robby Stephenson * Added Abstract and Keywords to default fields for bibliographies. 2006-08-15 Robby Stephenson * Released Tellico 1.2-pre3. 2006-08-09 Robby Stephenson * Released Tellico 1.2-pre2. 2006-08-07 Robby Stephenson * Fixed bug with merging fields in CSV importer. 2006-08-06 Robby Stephenson * Released Tellico 1.2-pre1. 2006-08-06 Robby Stephenson * Added "next" and "prev" buttons to entry editor. 2006-08-04 Robby Stephenson * Changed HTML export to use libxml2 instead of KHTML for parsing. That should fix the "all text on one line" problem. * Bumped minimum libxml2 verstion to 2.6.0 or greater. 2006-07-30 Robby Stephenson * Added bundled scripts for allocine.fr, Spanish Minstry of Culture, and Dark Horse Comics, all from Mathias Monnerville. 2005-11-13 Robby Stephenson * Bumped minimum KDE version to 3.3. 2006-07-18 Robby Stephenson * Added illustrator field to MODS translator. 2006-07-13 Robby Stephenson * Changed document saving to remember what format (Zip or XML) was opened, and save the file in the same format. 2006-07-04 Robby Stephenson * Updated sorting in HTML export to be locale-aware. * Added auto-completion to filter dialog. 2006-06-30 Robby Stephenson * Improved ISBN-13 compliance. 2006-06-24 Robby Stephenson * Fixed bug with relative url links in template. 2006-06-22 Robby Stephenson * Fixed memory leak in image handling. 2006-06-16 Robby Stephenson * Changed IMDb and Amazon to always use iso-8859-1 encoding for URL. 2006-06-12 Robby Stephenson * Fixed bug with HTML export and MSIE. 2006-06-11 Robby Stephenson * Added date stamp to all report templates. 2006-06-07 Robby Stephenson * Fixed IMDb bug that hid partial name matches for duplicate names. * Implemented workaround for NetAccess::download() that doesn't show download progress info. 2006-06-03 Robby Stephenson * Added validator for unmodified CueCat bar code reader. 2006-05-07 Robby Stephenson * Released Tellico 1.1.6. 2006-05-04 Regis Boudin * Added French documentation. 2006-05-04 Robby Stephenson * Fixed bug that prevented some images from being written to data directory. 2006-04-22 Robby Stephenson * Add font and color config to templates. 2006-04-19 Robby Stephenson * Fixed bug with multiple ISBN search for z29.50. * Fixed "disappearing paragraph" bug in Fancy template. * Fixed name order for IBS searches. * Released Tellico 1.1.5. 2006-04-12 Robby Stephenson * Added Xbox 360, Revolution and Playstation3 to game collection. 2006-04-11 Robby Stephenson * Added view config saving for custom collections based on URL. * Fixed crashing bug when deleting and re-adding fields by same name. * Fixed bug with HTML decoding in IMDb plot summary. 2006-04-10 Robby Stephenson * Fixed bug that re-added default fields when adding search results. 2006-04-07 Robby Stephenson * Added OS check (linux only) for enabling cd-text support. 2006-04-06 Robby Stephenson * Updated Spanish, thanks to Alejandro Hamann and Quique. * Fixed bug with writing temporary files, even when using app data dir. 2006-04-03 Robby Stephenson * Improved PubMed fetcher to grab URL links. 2006-04-02 Robby Stephenson * Changed grouping list to be sorted alphabetically. * Fixed OpenOffice.org plugin build for OOo SDK 2.0.2. * Released Tellico 1.1.4. 2006-03-29 Robby Stephenson * Added referrer to IBS fetcher so images get downloaded. 2006-03-27 Robby Stephenson * Changed XSL templates to output valid HTML 4.01 Strict, patch from Karl Ove Hufthammer. 2006-03-25 Robby Stephenson * Added button for installing new templates. 2006-03-24 Robby Stephenson * Added context menu item in table widget for clearing table. 2006-03-23 Robby Stephenson * Added template preview button in configuration dialog. * Added field for relative folder path to file catalog listing. * Fixed bug with incorrectly setting document modified when loading images. * Fixed bug with loading loans for entries with non-consecutive ids. * Fixed bug that prevented XSLT files with spaces or other non-encoded characters from being opened. * Fixed bug that caused crashes when changing grouping options for some fields. 2006-03-15 Robby Stephenson * Fixed bug that prevented files saved with versions < 0.8 from being opened. 2006-03-12 Robby Stephenson * Released Tellico 1.1.3. 2006-03-10 Robby Stephenson * Fixed bug with comic book template not showing up in a couple of places in the GUI. * Updated MARC2MODS3 stylesheet to MODS 3.1 from LoC. * Added preset z39.50 server list. 2006-03-09 Robby Stephenson * Added isbndb.com fetcher. * Fixed z39.50 search to use proper encoding for search terms. 2006-03-07 Robby Stephenson * Changed ISO 5426 converter to grab diaeresis instead of umlaut. 2006-03-05 Robby Stephenson * Added patch from Karl Ove Hufthammer, for lang-specific sorting. * Released Tellico 1.1.2. 2006-03-04 Robby Stephenson * Fixed file save permissions to remember group. * Fixed backup file permissions. 2006-03-02 Robby Stephenson * Fixed mess with shared pointers holding shared pointers holding shared pointers... * Fixed button size in HTML export. 2006-02-28 Robby Stephenson * Updated fr.po, thanks to Mathias Monnerville. * Fixed GCfilms importer to read running time correctly. * Use IMDb url for updating if available. 2006-02-27 Robby Stephenson * Added pt_BR, thanks to Claudio Felix. 2006-02-23 Robby Stephenson * Added dialog for importing new data sources. 2006-02-22 Robby Stephenson * Fixed HTML export to always use relative links. * Fixed Fancy template column widths, patch from Karl Ove Hufthammer. 2006-02-20 Robby Stephenson * Add Ant Movie Catalog file importer. 2006-02-19 Robby Stephenson * Allow data sources to overwrite current data when updating. 2006-02-18 Robby Stephenson * Added dialog to choose from multiple CDDB lookup responses. * Fields can have default values. * Changed default IMDB server to akas.imdb.com. 2006-02-18 Robby Stephenson * Fixed cleanup when oowriter is closed. * Fixed character encoding for IMDB searches. * Released Tellico 1.1.1. * Branched Tellico 1.1.x. 2006-02-17 Robby Stephenson * Fixed image linking in HTML export. * Updated admin/ and Makefile.am for recent KDE updates. * Changed installation directory of .desktop file to use xdg_appsdir. 2006-02-14 Robby Stephenson * Fixed templates to scale images down to max of 150x200. 2006-02-08 Robby Stephenson * Fixed bug with Video template not showing nationality. * Added ISO 5426 and ISO 6937 character set converters, adapted from MARC4J; 2006-02-07 Robby Stephenson * Dual-licensed the documentation under the GFDL and the FreeBSD Documentation License. * Released Tellico 1.1. 2006-02-04 Robby Stephenson * Added track length to CDDB lookup result. 2006-02-01 Robby Stephenson * Fixed bug with not deleting some temporary files. 2006-01-29 Robby Stephenson * Updated bibtex-translation.xml, thanks to Norbert Nemec. * Updated es.po, thanks to Alejandro Hamann. 2006-01-25 Robby Stephenson * Relaxed MODS importer to look for publisher, isbn, or lccn to determine the entry is a book. Previously, a typeOfResource="text" was required. * Allowed z39.50 source to ask for MODS directly. * Added message box for stderr output from scripts. * Added simple GRS-1 importer for z39.50 fetcher. 2006-01-24 Robby Stephenson * Fixed bug with potential data los for saving image files after changing config option. * Released Tellico 1.1pre3. 2006-01-22 Robby Stephenson * Released Tellico 1.1pre2. * Improved entry views with some graphics, a la amaroK. 2006-01-21 Robby Stephenson * Added drag-n-drop from Mozilla Firefox to image widget. * Changed default behavior to include images in data file, and added a config option. * Added a message box warning about performance when the user first opens a file with more than 100 images. 2006-01-20 Robby Stephenson * Fixed bug with parsing arguments to external data source scripts. 2006-01-19 Robby Stephenson * AnimneNfo.com source added by default, and IBS.it added if Italian is in the user's language list. * Alexandria exporter includes publishing year now. 2006-01-17 Robby Stephenson * Alexandria importer includes publishing year now. 2006-01-15 Robby Stephenson * Changed file saving to be smart about which images need written to cache. Should improve save-time. * Fixed bug with undo/redo not correctly reverting to old values. 2006-01-14 Robby Stephenson * Added Russian translation, from Artur Kalimullin. 2006-01-12 Robby Stephenson * Released Tellico 1.1pre1. * Added/updated SRU fetcher. 2006-01-11 Robby Stephenson * Added Internet Bookshop (ibs.it) book source. 2006-01-10 Robby Stephenson * Added AnimeNfo.com search source. 2006-01-09 Robby Stephenson * Fixed Debian bug #346414 in hu.po. 2006-01-08 Robby Stephenson * Changed image loader to read and write images inside the Tellico shared directory to speed up loading, etc. 2005-12-06 Robby Stephenson * Merged OpenOffice.org interop. Requires SDK for compilation. 2006-01-05 Robby Stephenson * Added Yahoo! Album Search source. * Added Tri-Column video report template. 2005-12-15 Robby Stephenson * Expand external app fetcher to include any of the fetch keys. 2005-12-08 Robby Stephenson * Increased max table columns to 10. 2005-12-07 Robby Stephenson * Updated Amazon fetcher to allow selecting keywords or not. 2005-12-05 Robby Stephenson * Changed bibtex behavior to not add braces around capital letters. 2005-11-29 Robby Stephenson * Moved no-capitalization list to config dialog. 2005-11-28 Robby Stephenson * Updated Hungarian, from Csaba Zakarias. 2005-11-17 Robby Stephenson * Added separate UPC searching for Amazon. 2005-11-16 Robby Stephenson * Added CD-Text reading to FreeDBImporter, plus option to disable compilation in case of platform issues. * Move to Amazon ECS version 4. 2005-11-15 Robby Stephenson * Added IMDB rating field to fetcher. 2005-11-13 Robby Stephenson * Bumped minimum KDE version to 3.2 and minimum QT version to 3.2. 2005-11-10 Robby Stephenson * Custom data source fields are now configurable. 2005-11-07 Robby Stephenson * Made size of IMDb cast list configurable. * Broke out cite actions into submenu, and added copying bibtex to clipboard. 2005-11-03 Robby Stephenson * Bumped XML syntax version to 9 to include file catalog and changing default music collection track field to 3 columns. 2005-11-01 Robby Stephenson * Fixed compilation on GNU/kFreeBSD, Debian bug #336949, patch from Aurelien Jarno. 2005-10-29 Robby Stephenson * Improved table fields to show column titles. * Changed buttons to a right-click popup. 2005-10-27 Robby Stephenson * Improved audio file metadata importer to include track length as an additional column in the track field. 2005-10-26 Robby Stephenson * The audio file importer now checks for track numbers in the file name, patch from Andras Mantia. 2005-10-19 Robby Stephenson * Added a string store to reduce memory consumption. 2005-10-17 Robby Stephenson * Released Tellico 1.0.3. 2005-10-15 Robby Stephenson * Added buttons for inserting and removing rows from a table. 2005-10-13 Robby Stephenson * Enabled auto-updating for certain entries and data sources. 2005-10-12 Robby Stephenson * Updated it.po, from Lorenzo Novaro. 2005-10-11 Robby Stephenson * New and modified entries now show labels indicating so. 2005-10-08 Robby Stephenson * Improved status bar. 2005-10-07 Robby Stephenson * Updated CalendarHandler to compile with KDEPIM 3.5. 2005-10-06 Robby Stephenson * Fixed bug with bibtex macro names always being imported in lowercase. 2005-10-03 Robby Stephenson * Fixed bug with initialization, patch from Marco Clemencic. 2005-10-02 Robby Stephenson * Released Tellico 1.0.2. * Updated IMDB fetcher. 2005-09-25 Robby Stephenson * Fixed bug with auto-detecting MARC encoding for z39.50 import. 2005-09-22 Robby Stephenson * Fixed bug with CSV importer not properly parsing some non-CSV files. * Fixed bug with entry selection in icon view. * Added GCfilms exporter; 2005-09-21 Robby Stephenson * Fixed bug with showing invalid loan due date. * Added GCfilms importer. * Added IMDB link when fetching info from IMDB. * limited number of cast results form IMDB to 20; 2005-09-20 Robby Stephenson * Released Tellico 1.0.1. 2005-09-19 Robby Stephenson * Fixed bug with loading utf-8 encoded bibtex files. 2005-09-18 Robby Stephenson * Fixed bug with variable icon sizes in config dialog. 2005-09-16 Robby Stephenson * Fixed bug for merging field properties, which mostly showed up as double-colons in music collection tracks. * Fixed crashing bug with modifying filters and cancelling. 2005-09-11 Robby Stephenson * Added Norwegian Nynorsk translation from Karl Ove Hufthammer. 2005-09-10 Robby Stephenson * Fixed bug in audio file metadata import that was overwriting track listings incorrectly. * Fixed crashing bug for selecting groups and entries in group view. 2005-09-09 Robby Stephenson * Released Tellico 1.0. 2005-09-05 Robby Stephenson * Added Polish translaion from Marek Janukowicz. 2005-08-22 Robby Stephenson * Fixed bug with external application fetcher not reading path entries correctly. * Updated Spanish translation, thanks to Brian Hughes. 2005-08-21 Robby Stephenson * Updated Swedish, thanks to Peter Landgren. * Added patch from Nix to dynamically resize table columns. 2005-08-20 Robby Stephenson * Released Tellico 1.0pre2. 2005-08-14 Robby Stephenson * Changed audio file metadata to ignore case when doing album comparisons. * Changed collection loader to better compensate for old rating fields. * Changed AddEntry command to act on multiple entries. 2005-08-11 Robby Stephenson * Updated Dutch translation, thanks to Fred Marchee. * Updated Fininsh translation, thanks to Teuvo Eloranta. 2005-08-06 Robby Stephenson * Released Tellico 1.0pre1. 2005-08-04 Robby Stephenson * When exporting HTML, copy any relative images, etc. 2005-08-01 Robby Stephenson * Strip HTML tags from paragraph export to PilotDB. 2005-07-30 Robby Stephenson * Make the amazon importer default to medium images instead of none. 2005-07-28 Robby Stephenson * Make the 'Fancy' template default. 2005-07-01 Robby Stephenson * Released Tellico 0.13.8. 2005-06-30 Robby Stephenson * Fixed bug with CSV import parsing. 2005-06-28 Robby Stephenson * Added license exception for linking to OpenSSL. 2005-06-25 Robby Stephenson * Added a status message timeout for main window and fetch dialog. 2005-06-23 Robby Stephenson * Fixed crashing bug for fields with empty categories. 2005-06-22 Robby Stephenson * Added initial Catalan translation from David Majà Martínez. 2005-06-20 Robby Stephenson * Added fetcher for reading output from an external application. * Fixed bug with wrong icon size for entry action. 2005-06-17 Robby Stephenson * Added video game collection type. 2005-06-12 Robby Stephenson * Added capability for recursively reading all CDDB cache files, based on patch from Marco Hofmann. 2005-06-08 Robby Stephenson * Fixed bug with file extension defaulting to .bc. 2005-06-02 Robby Stephenson * Changed Table field to allow up to 5 columns. Deprecated Table2 type. 2005-06-01 Robby Stephenson * Added Rating field type. 2005-05-31 Robby Stephenson * Fixed bug with grouping by checkbox fields. 2005-05-30 Robby Stephenson * Updated Norwegian translation. * Added Unimarc support to z39.50 importer. 2005-05-25 Robby Stephenson * Released Tellico 0.13.7. 2005-05-25 Robby Stephenson * Changed bibtex exporter to put crossref'd entries at the end. Only one level of crossref is allowed. 2005-05-19 Robby Stephenson * Fixed bug with changing between rating and normal choice field. 2005-05-18 Robby Stephenson * Updated Dutch translation, thanks to Fred Marchee. 2005-05-13 Robby Stephenson * Fixed bug with clearing table fields. * Added ONIX export, borrowing format from Alexandria. 2005-05-10 Robby Stephenson * Changed contact email to tellico-users mailing list. 2005-04-27 Robby Stephenson * Fixed corner case xml element naming for new fields. * Fixed typo on allowed and description in fields editor. 2005-04-10 Robby Stephenson * Fixed bug with having fields named after xml elements, like entry or field. 2005-04-09 Robby Stephenson * Added a PubMed search source, thanks to an XSL stylesheet from Michaël Zugaro. 2005-04-08 Robby Stephenson * Added some accelerators, thanks to patch from Felix Berger. 2005-04-07 Robby Stephenson * Added Undo/Redo framework. 2005-04-03 Robby Stephenson * Released Tellico 0.13.6. 2005-03-29 Robby Stephenson * Fixed bug with adding multiple entries from fetch dialog not added additional images. * Added Portuguese translation, thanks to Ligia Moreira. 2005-03-23 Robby Stephenson * Changed internet search dialog to clear results when running a new search. * Changed selection so that hidden items are not selected. 2005-03-16 Robby Stephenson * Added shortcuts for entry grouping and filter. 2005-03-08 Robby Stephenson * Released Tellico 0.13.5. 2005-03-02 Robby Stephenson * Updated German translation, thanks to Gerrit Albrecht. 2005-03-01 Robby Stephenson * Fixed crash for some amazon searches. * Fixed character encoding for Japanese amazon searches. 2005-02-27 Robby Stephenson * Released Tellico 0.13.4 2005-02-25 Robby Stephenson * Added a configure param to disable cddb even if kcddb headers found. 2005-02-24 Robby Stephenson * Updated bibtex translation map, fixing some bugs. * Fixed bug with Amazon search character encoding. * Changed Bibtex handler to convert {MIT} to MIT, importing and exporting. 2005-02-23 Robby Stephenson * Fixed bug with grouping being enabled for paragraphs. 2005-02-17 Robby Stephenson * Fixed bug with exporting empty groups in HTML. * Added capability for adding loan due notice to KOrganizer calendar. 2005-02-14 Robby Stephenson * Fixed bug with RIS importer messing up entry types. 2005-02-10 Robby Stephenson * Released Tellico 0.13.3. 2005-02-08 Robby Stephenson * Fixed bug in configure script for disabling amazon, thanks to Markus Brueffer. * Fixed bug with spin boxes not enabling Apply button in Config Dialog. * Fixed bug with audio file metadata import for compilations. 2005-02-05 Robby Stephenson * Update some bibtex character translations. * Released Tellico 0.13.2. 2005-02-03 Robby Stephenson * Updated IMDb fetcher. * Updated bibtex import and export to recognize {X} constructions. * Fixed bug with locale encoding for HTML export. * Changed statusbar to show tooltips for menu items. 2005-02-02 Robby Stephenson * Added France and Canada locales to Amazon.com sources. 2005-02-01 Robby Stephenson * Fixed bug with importing RIS with no final space. * Fixed Debian bug #290467 for gcc 4.0 compilation, thanks to Regis Boudin. 2005-01-28 Robby Stephenson * Removed find dialog since the filter works so much better. 2005-01-27 Robby Stephenson * Removed option for toggling entry count show in group view. * Added check-out for lending items with view. 2005-01-25 Robby Stephenson * Fixed Debian bug #292165 with bibtex export of umlauts. 2005-01-21 Robby Stephenson * Updated Swedish translation, thanks to Peter Landgren. 2005-01-20 Robby Stephenson * Added saved filters with view. * Changed IMDb cast page to decode HTML entities, thanks to Rafa Kortes. 2005-01-12 Robby Stephenson * Changed double click in the Internet Search dialog to add the entry. 2005-01-11 Robby Stephenson * Added collection report dialog. 2005-01-06 Robby Stephenson * Improved file loading efficiency somewhat. 2005-01-05 Robby Stephenson * Changed image loading to be on-demand for local zip files. 2004-12-19 Robby Stephenson * Added DCOP interface for exporting files. * Added vertical line between columns in list view. * Translated xsl entry template names. 2004-12-18 Robby Stephenson * Added DCOP interface for importing files. 2004-12-17 Robby Stephenson * Branched to 0.13.x. * Updated audio file importer to use "(Various)" for multi-artist albums. 2004-12-11 Robby Stephenson * Fixed a bug in PilotDB exporting, I think. * Added PilotDB export support for Date fields. * Released Tellico 0.13.1. 2004-12-10 Robby Stephenson * Fixed some date formatting. 2004-12-09 Robby Stephenson * Fixed auto bibtex key generation for empty keys. 2004-12-08 Robby Stephenson * Fixed Entry View to react to changes in the color palette. 2004-12-06 Robby Stephenson * Increased maz z39.50 port to 999999. * Fixed some KMAX, KMIN template issues with casts. * Added column shading for detailed view, per KDE bug 59791. 2004-12-03 Robby Stephenson * Updated Finnish translation, thanks to Tuevo Eloranta. * Updated Norwegian translation, thanks to Leif Mathis Gaup. * Fixed *BSD compile, with patch from Markus Brüffer. 2004-12-01 Robby Stephenson * Enabled relative URLs in URL fields. * Disabled running executables in URL fields. * Released Tellico 0.13. 2004-11-30 Robby Stephenson * Removed all the KStaticDeleter references. Don't get fancy. * Updated title sorts to work for articles ending with an apostrophe. * Updated audio file metadata importer to recognize albums with multiple artists and change track list accordingly. * Fixed CSV importer to properly find pre-existing, non-default fields. 2004-11-25 Robby Stephenson * Added stars for up to a 10 rating. * Added checkmark in entry templates for bool fields. 2004-11-20 Robby Stephenson * Added Spanish tips translation from ventolera. 2004-11-19 Robby Stephenson * Released Tellico 0.13pre3. 2004-11-18 Robby Stephenson * Added command-line options for loading bibtex, MODS, and RIS files. 2004-11-17 Robby Stephenson * Updated Amazon fetcher to work with more than 10 ISBN. * Updated z39.50 fetcher to work with more than 10 ISBN. * Added language lookup table to mods2tellico.xsl. * Updated lyxpipe cite to auto-generate bibtex key if necessary. * Added "load ISBN list from file" to search dialog. 2004-11-14 Robby Stephenson * Released Tellico 0.13pre2. 2004-11-12 Robby Stephenson * Improved printing speed. * Added user and password config for z39.50. * Added address field to default bibliography. 2004-11-10 Robby Stephenson * Updated French translation, thanks to Regis Boudin. * Fixed layout bug in config dialog. 2004-11-09 Robby Stephenson * Added fix similar to kde bug 86188 for including linux/cdrom.h. * Changed latex cite to separate multiple cites with ", " instead of "," to better match pybliographer. 2004-11-08 Robby Stephenson * Added host config to IMDB. * Added image download option to Amazon and IMDB. * Updated kde-common/admin/cvs.sh to allow other automake versions. * Released Tellico 0.13pre1. 2004-11-07 Robby Stephenson * Added character set config to z39.50. * Changed main document to tellico.xml. 2004-10-31 Robby Stephenson * Updated initial minimum width to 600 pixels. * Added group selection entry to collection menu. * Updated Find Dialog to remove FromBeginning (didn't make sense) 2004-10-23 Robby Stephenson * Updated HTML export and printing stylesheets a bit. 2004-10-18 Robby Stephenson * Added patch to compile on g++ 2.95 from Markus Brueffer. 2004-10-11 Robby Stephenson * Added patch from wwp to fix saving file on quit. 2004-10-02 Robby Stephenson * Added confirm message box when deleting an entry. 2004-09-23 Robby Stephenson * Changed default file textension to .tc. 2004-09-15 Robby Stephenson * Added Unrated certification to default video collection. 2004-09-14 Robby Stephenson * Renamed Bookcase to Tellico * Added IMDB fetcher. * Incremented DTD version to 7, changed root element to tellico. * Changed zip file to use maindoc.xml as do the KOffice files. 2004-09-10 Robby Stephenson * Added new icons from Virginie Quesnay. 2004-09-09 Robby Stephenson * Updated French translation, thanks to Regis Boudin. * Removed libcdda (cdparanoia) dependency, using cd-discid code instead. * Added rating widget in entry editor, using stars. 2004-09-07 Robby Stephenson * Really fixed gcc 2.95 compilation. * Added RIS importer. 2004-09-02 Robby Stephenson * Released Bookcase 0.11. 2004-09-01 Robby Stephenson * Added FreeDB importer. 2004-08-27 Robby Stephenson * Fixed compile on gcc 2.95. 2004-08-26 Robby Stephenson * Added Alexandria importer. * Added Alexandria exporter. 2004-08-25 Robby Stephenson * Updated Norwegian translation, thanks to Leif Mathis Gaup. * Read album covers from .directory files in audio file importer. 2004-08-24 Robby Stephenson * Added support for reading audio file meta-data. 2004-08-22 Robby Stephenson * Updated paragraph fields to remember line-breaks. * Released Bookcase 0.10. 2004-08-20 Robby Stephenson * Added HTML formatting to paragraphs in entry templates. * Updated German translation, thanks to Gerrit M. Albrecht. 2004-08-18 Robby Stephenson * Added Latin1Literal class to speed up string comparisons. * Added MODS import menu item. 2004-08-10 Robby Stephenson * Added "export selected entries only" option. 2004-08-09 Robby Stephenson * Fixed UI file location. * Released Bookcase 0.10pre2. 2004-08-08 Robby Stephenson * Released Bookcase 0.10pre1. * Added entry icon to fetch dialog button. 2004-08-03 Robby Stephenson * Fixed bug for exporting HTML for entries with multiple titles. * Changed HTML export to put entry files in separate directory. * Added link to parent collection file for entry templates. 2004-08-01 Robby Stephenson * Updated French translation, thanks to Rui Nibau. * Changed to show a spinbox for single number values; 2004-07-30 Robby Stephenson * Fixed bug with capitalization of words split by dash or comma, and ending with apostrophes. 2004-07-13 Robby Stephenson * Fixed bugs with enabling apply/ok buttons in fields dialog for extended properties, and disappearing groups when renaming fields. 2004-07-02 Robby Stephenson * Added collection and group view in stack. 2004-06-26 Robby Stephenson * Fixed bug with copying selected entries. 2004-06-21 Robby Stephenson * Fixed gcc 3.4 compilation bug in src/translators/btparse/err.h 2004-06-07 Robby Stephenson * Update Norwegian translation, thanks to Leif Mathis Gaup. 2004-05-27 Robby Stephenson * Released Bookcase 0.9.3. * Updated italian translation, thanks to FaUsT. 2004-05-25 Robby Stephenson * Fixed crashing bug for exporting when no fields can be grouped. 2004-05-24 Robby Stephenson * Restructured ISBN code to get rid of some old cruft. * Added patch from Regis Boudin to add French ISBN formatting and remove old copyright restricted ISBN code. 2004-05-22 Robby Stephenson * Added entry group templates. 2004-05-19 Robby Stephenson * Changed focus to move to next entry when one is deleted. * Changed document loader to allow i18n attribute indicate translation. * Fixed bug with empty group names. 2004-05-17 Robby Stephenson * Released Bookcase 0.9.2. 2004-05-15 Robby Stephenson * Fixed bug with xslt import with libxml < 2.6. * Fixed bug with losing extended properties when cloning fields/ 2004-05-10 Robby Stephenson * Added Amazon fetcher. * Added SRU Fetcher. 2004-05-07 Robby Stephenson * Fixed bug with XSLT importer. * Added MODS XSLT importer file. 2004-05-06 Robby Stephenson * Branched 0.9.x 2004-05-02 Robby Stephenson * Changed Fields Dialog to close when creating a new collection or opening one. * Removed GCJ/java checks from configure. * Released Bookcase 0.9.1 2004-04-30 Robby Stephenson * Fixed i18n extraction for UI and gettext extensions. * Removed Japanese translation because of encoding errors. * Updated CSV import to know about current fields when appending or merging 2004-04-29 Robby Stephenson * Updated German translation from Gerrit Albrecht. 2004-04-27 Robby Stephenson * Fixed bug with lyxpipe using LYXSRV insted of LYXCMD. * Changed quick filter to wait 200 ms before updating the filter. 2004-04-21 Robby Stephenson * Added wait cursor for file saving. * Fixed bug with created a double collection when failing to load the initial file. * Make sure column view updates when an image is cleared. 2004-04-18 Robby Stephenson * Released Bookcase 0.9. 2004-04-16 Robby Stephenson * Improved visibilty of image size in Fancy.xsl. * Updated Italian translation, thanks to FaUsT. * Updated Finnish translation, thanks to Teuvo. * Added hidden pref for pixmap size in detailed view. * Changed HTML exporter to use titles as filenames. 2004-04-13 Robby Stephenson * Added hidden preference for pilotdb export charset encoding. 2004-04-09 Robby Stephenson * Improved bibtex export to detect duplicates keys and add 'a'b, 'b', etc. * Released Bookcase 0.9pre1 2004-03-26 Robby Stephenson * Updated printing and HTML export to include images. * Updated Detailed View to show images, too. 2004-03-25 Robby Stephenson * Updated Italian translation, thanks to FaUsT. * Changed toolbar icon for New Entry to collection icon on top of empty mime type. 2004-03-24 Robby Stephenson * Added menu item in group view for filtering on groups. * Added config options for image sizes in printout. 2004-03-22 Robby Stephenson * Fixed another bug with HTML export character encoding. * Added config panel for bibtex quotation style and lyxpipe location 2004-03-20 Robby Stephenson * Fixed Paragraph fields to properly signal modified in the Entry Editor. * Fixed crashing bug for fields with nothing but whitespace in the datafile. * Updated printing stylesheet so that images can be included. 2004-03-19 Robby Stephenson * Changed document opening so that if the first field has the name of "_default", all the default fields will be added. 2004-03-17 Robby Stephenson * Changed Dependent fields to allow grouping. * Changed Fields Dialog to allow changing the field types, in some limited cases. 2004-03-15 Robby Stephenson * Fixed crashing bug in PilotDB export with images. * Fixed bug with "other" delimiter freezing the app. 2004-03-13 Robby Stephenson * Fixed bug with trying to reopen Untitled after converting to a bibliography. * Added support for lyxpipe and citing entries in bibliography. 2004-03-12 Robby Stephenson * Changed to be more aggressive in creating URL fields on Bibtex import. 2004-03-10 Robby Stephenson * Added Compact and Fancy entry templates. * Released Bookcase 0.8.5. 2004-03-07 Robby Stephenson * Fixed bug in Video template for when there's no Cast template. * Fixed bug with detecting old versions of gcc and vector::at. 2004-03-05 Robby Stephenson * Fixed bug with filter dialog layout and KDE 3.2. 2004-03-04 Robby Stephenson * Fixed bug with corrupt zip files when more than one entry references the same image. 2004-02-28 Robby Stephenson * Fixed the gcc 2.95 compile bug for sure. 2004-02-25 Robby Stephenson * Fixed crashing bug in Data::Entry::field(). * Fixed XSLTHandler to work for URLs, so XSLT files can reference other files. 2004-02-22 Robby Stephenson * Fixed bug with HTML entities in XSLT stylesheet params. 2004-02-20 Robby Stephenson * Fixed bug with duplicate menu items in KDE 3.2.x. * Fixed bug to properly reorder categories when reordering fields. * Released Bookcase 0.8.4. 2004-02-19 Robby Stephenson * Fixed auto-capitalize and auto-format to take affect immediately. 2004-02-18 Robby Stephenson * Added Czech translation, thanks to Robert Kratky. 2004-02-17 Robby Stephenson * Fixed compilation issues for gcc 2.95 and FreeBSD. * Changed the Config Dialog to use a semi-colon to separated the articles, prefixes, and suffixes instead of a comma, for consistency. They're still saved in the config file with a comma, though, so old options should be fine. 2004-02-14 Robby Stephenson * Fixed problem when trying to reopen last file and getting error. * Added drag & drop support for the image widget. 2004-02-13 Robby Stephenson * Updated Estonian translation, from Toomas Nigola. * Changed to remember sorting config in group view. 2004-02-12 Robby Stephenson * Fixed crashing bug when renaming a single-category field. 2004-02-11 Robby Stephenson * Changed Entry Editor to redo page layout when deleting fields. * Added merge functionality when importing collections. * Fixed bug with exporting HTML in non-utf8. 2004-02-09 Robby Stephenson * Updated Spanish translation, thanks to Quique. * Added sorting by group count. 2004-02-07 Robby Stephenson * Added workaround for layout bug in Keramik style. 2004-02-04 Robby Stephenson * Fixed saving visible columns yet again. * Changed XSLT parser to prevent all network calls. * Fixed crashing bug when deleting a field. * Fixed compilation bug when using libxml > 2.6.0 but libxslt < 1.1. * Released Bookcase 0.8.3. 2004-02-04 Robby Stephenson * Fixed file truncation for file with some Unicode characters. * Released Bookcase 0.8.2. 2004-02-03 Robby Stephenson * Released Bookcase 0.8.1. 2004-02-02 Robby Stephenson * Added Album entry template. * Added Finnish translation, thanks to Teuvo Eloranta. * Added --with-xml-catalog configure option. 2004-01-30 Robby Stephenson * Fixed compile bug with src/Makefile.am and libxslt. * Fixed bug with $datadir param in Default entry template. * Added Video entry template. * Added EAN to ISBN conversion for 978 and 979 codes. Patch from Martijn Pieters. 2004-01-27 Robby Stephenson * Fixed bug with Select All not working. * Fixed some compilation issues. * Updated French translation from Rui Nibau. 2004-01-25 Robby Stephenson * Fixed DTD for new collections. * Updated some accel keys. * Changed Requires to KDE 3.1 or higher since KZip is used. * Released Bookcase 0.8. 2004-01-23 Robby Stephenson * Released Bookcase 0.8pre1 2004-01-22 Robby Stephenson * Added Finnish translation from Teuvo Eloranta. * Added "Export to XML" option since file format is Zip now. * Switched to new XML parser in libxml2 > 2.6.0 2004-01-13 Robby Stephenson * Added new Collection menu. 2004-01-09 Robby Stephenson * Changed entry method for bibtex string macros. Triple-clicking on list view items is stupid. 2004-01-08 Robby Stephenson * Added zip file format for collections with images. 2004-01-01 Robby Stephenson * Added cut, copy, and paste functions. * Added new entry and copy entry functions. * Added find previous, select all, and deselection functions. 2003-12-31 Robby Stephenson * Changed double clicking an entry to show the editor. * Moved entry edit widget to dialog box. 2003-12-13 Robby Stephenson * Updated French translation, thanks to RNB. 2003-12-11 Robby Stephenson * Added image fields. * Changed auto-completion to account for fields with multiple values. 2003-12-07 Robby Stephenson * Updated Norwegian translation, thanks to Leif Mathis Gaup. 2003-11-25 Robby Stephenson * Fixed bug in remembering visible columns for custom fields. * Added some speed-ups in loading time. * Released Bookcase 0.7.2 2003-11-24 Robby Stephenson * Fixed another ISBN format bug for non-english publishers. 2003-11-23 Robby Stephenson * Fixed bug with auto-capitalization when global setting was turned off. * Fixed bug with not saving options for empty collections. * Fixed bug with reordering visible fields. 2003-11-21 Robby Stephenson * Added Pilot-DB export option. * Fixed bug to update status line after deleting entries. 2003-11-12 Robby Stephenson * Added Derived field type for concatenating values from other fields. * Added default collections for trading cards, coins, stamps, comic books, and wines. 2003-11-11 Robby Stephenson * Updated German tranlation, thanks to Gerrit Albrecht. * Added Swedish translation, thanks to Karolina Lindqvist. * Fixed bug with adding 2-column table. * Cut the 0.7.x branch. * Added Derived field type. 2003-11-09 Robby Stephenson * Fixed charset encoding bug. * Fixed compile for GCC 2.96. * Changed bibtex and CSV export to default to locale encoding. * Fixed bug with field editor not updating title. * Released Bookcase 0.7.1. 2003-11-08 Robby Stephenson * Released Bookcase 0.7. 2003-11-05 Robby Stephenson * Fixed the tips file translation issue. * Released Bookcase 0.7pre2. 2003-11-03 Robby Stephenson * Fixed ISBN formatting bug for non-english language publishers. * Released Bookcase 0.7pre1. 2003-10-25 Robby Stephenson * Fixed documentation compile error for KDE 3.2. * Added CSV importer. * Changed Bibtexml importer and exporter to use internal classes rather than XSL stylesheets. 2003-10-24 Robby Stephenson * Added string macro editor for Bibtex collections. * Printing now only prints visible entries by default. 2003-10-21 Robby Stephenson * Added new group for all attributes formatted as names, so Editors and Authors can be grouped together. 2003-10-10 Robby Stephenson * Changed printing to print columns as shown in the view, and group by the current grouping. * Removed old options for printing fields selection. 2003-10-04 Robby Stephenson * Added capability to reorder fields in field editor. 2003-10-03 Robby Stephenson * Imported btparse library code to use for importing Bibtex files. It has a lot of warnings, but none should be fatal. 2003-09-23 Robby Stephenson * Added "Bibtex Field" to default Bibtex collections fields to aid in exporting to Bibtex and Bibtexml. * Bumped document syntax to 4. * Changed "attribute" elements in document file to "field". * Changed all unit elements to "entry" rather than the unit name. * Changed Boolean values to save as "true" instead of an implicit value. * Added CSV exporter. 2003-09-12 Robby Stephenson * Added 2-column Table field type. 2003-09-09 Robby Stephenson * Changed boolean values to show title in group view. 2003-09-03 Robby Stephenson * Fixed bug when deleting multiple books from detailed listview. 2003-08-31 Robby Stephenson * Changed field completion to be case-insensitive. * Fixed bug with saving listview columns. 2003-08-16 Robby Stephenson * Fixed bug with reopening last saved file. 2003-08-12 Robby Stephenson * Added French translation from RNB. 2003-08-08 Robby Stephenson * Added menu item for configuring toolbars. * Added menu item for configuring shortcut keys. 2003-07-31 Robby Stephenson * Added Table field type. 2003-07-30 Robby Stephenson * Fixed bug in saving visible columns. * Improved loading time. 2003-07-29 Robby Stephenson * Implemented adding file extension (.bc) when using File->Save As and an extension filter is shown. * Added status bar text showing number of selected books when more than 1 is selected. 2003-07-24 Robby Stephenson * Added menu comands for showing/hiding the group view and editor. 2003-07-23 Robby Stephenson * Added "--nofile" command line option for bypassing the "Open last file" config setting. * Added "Tip of the Day" dialog. * Changed default for showing unit count to true. * Changed Year field type to generic Number. * Improved sorting to sort numerically for Number fields. * Added Default button to Field Editor dialog. * Added additional format option to never capitalize or format. * Fixed last open file option to no longer be over-written when Bookcase is exited with an empty document. * Cut the 0.6.x branch. 2003-07-22 Robby Stephenson * Fixed crashing bug in "Edit Fields" dialog. 2003-07-21 Robby Stephenson * Released Bookcase 0.6.5 2003-07-09 Robby Stephenson * Added Estonian translation from Toomas Nigola. 2003-07-07 Robby Stephenson * Updated German translation from Ulf-Diether Ehlert. 2003-07-01 Robby Stephenson * Fixed translation bug with category names. * Added Bulgarian translation from Boyan Ivanov. 2003-06-25 Robby Stephenson * Fixed sorting bug for empty fields. * Released Bookcase 0.6.4 2003-06-19 Robby Stephenson * Changed focus to first tab when adding new book. * Disabled multiple value option for title field in field editor. * Fixed bug with completion object not being updated when a field is changed to all auto-completion. * Fixed drawing bug with toolbar label background. 2003-06-17 Robby Stephenson * Fixed bug with surname matching too much with periods in regexp. * Added Japanese translation from Linux Magazine. * Added Dutch translation from Liese De Vos. 2003-05-30 Robby Stephenson * Fix crashing bug when opening the filter dialog on KDE 3.0. 2003-05-27 Robby Stephenson * Released Bookcase 0.6.3 2003-05-26 Robby Stephenson * Added feature where the column view remembers the previous sorted column, so that items with the same sort key are subsequently sorted by previous column. 2003-05-25 Robby Stephenson * Add Spanish translation, thanks to Quique. * Updated Norwegian translation, thanks to Leif Mathis. * Fix bug with collapse/expand all not working in group view. * Fix bug with "Find" not properly traversing the collection. * Fix bug with sorted column not being saved between document loads. * Fix bug with changed category not being reflected in edit widget. * Fix bug with name prefixes not being limited to word boundaries. 2003-05-11 Robby Stephenson * Released Bookcase 0.6.2 2003-05-10 Robby Stephenson * Fixed bug with custom fields not showing up in column popup menu. * Fixed crashing when selecting a different book after modifying the current one and not saving it. * Fixed icon bug for column view. * Prettied-up some of layout code. 2003-05-09 Robby Stephenson * Added Norwegian translation from Leif Mathis Gaup. * Fixed printing bug for books with multiple groups. 2003-05-06 Robby Stephenson * Changed initial file opening so that if a new empty file was the last viewed, then no recent file is opened at next startup. * Fixed bug with initial collection not updating edit widgets. 2003-05-05 Robby Stephenson * Released Bookcase version 0.6.1 * Fixed automake 1.7 error with configure.in.in * Fixed UIC variable usage, primarily for *BSD platforms * Fixed double encoding bug on bibtexml inport * Fixed wrong namespace on bibtexml export * Changed to use KAcceleratorManager for keyboard accels on tabs * Fixed bug with the group view icon not getting changed back to the folder 2003-05-03 Robby Stephenson * Fixed bug with editing multiple books and getting asked every time if I'm sure. * Changed modified field names in the "Edit Fields" dialog to be colored, an din italic font instead of having an asterisk in front. 2003-05-02 Robby Stephenson * Released Bookcase version 0.6 * Changed Bibtex export to use the Locale character encoding instead of UTF-8 2003-04-22 Robby Stephenson * Added a URL attribute type * Changed articles, surname prefixes, and suffixes to be case-insensitive so the config value no longer has to include both capitalized and lower-case values * Broke out the attribute format flag for cleaner code 2003-04-13 Robby Stephenson * Added multiple selection, batch editing 2003-04-12 Robby Stephenson * Added RegExp Editor button to find dialog * Added Quick and Advanced filtering in the detailed list view 2003-04-10 Robby Stephenson * Added new "person" icon for grouping when the group is a name 2003-04-09 Robby Stephenson * Convert from KTabCtrl to QTabWidget, which has more documentation and is actively developed 2003-04-08 Robby Stephenson * Fixed bug with column order not retained on save 2003-04-05 Robby Stephenson * Fixed bug with validating multiple copyright years 2003-03-25 Robby Stephenson * Added surname prefixes like 'de' and 'von' 2003-03-23 Robby Stephenson * Fixed debug mistake. * Released Bookcase version 0.5.2a 2003-03-22 Robby Stephenson * Released Bookcase version 0.5.2 2003-03-21 Robby Stephenson * Added German translation from Gerrit M. Albrecht * Fixed bugs with empty suffix and article list * Fixed some i18n issues 2003-03-18 Robby Stephenson * Added Hungarian translation from Marcel Hilzinger 2003-03-15 Robby Stephenson * Fixed types and compile problems. Thanks, Dre! * Released Bookcase version 0.5.1 2003-03-14 Robby Stephenson * Put the book count in color. * Took out general export function for now. * Released Bookcase version 0.5 2003-03-07 Robby Stephenson * Added a new printing page in the configuration dialog * Added a checkmark pixmap for the boolean fields * Added a header menu to be able to select which fields to show in the detailed list view 2003-03-02 Robby Stephenson * Fixed character encoding bug for latin2 2003-01-25 Robby Stephenson * Fixed focus bug 2003-01-19 Robby Stephenson * Changed FORMAT_VERSION to 2 * Changed document format so that attributes which allow multiple entries are saved in multiple XML elements * Changed "keywords" attribute to "keyword" * Added check for document format version so that incompatible versions are not loaded. Older versions will be properly loaded, but once saved, previous versions of Bookcase will not load the new files properly. * Changed "Language" and "Copyright Year" to allow multiple entries. 2002-12-12 Robby Stephenson * Added namespaces to document file 2002-12-11 Robby Stephenson * Added import from Bibtexml using XSLT 2002-12-08 Robby Stephenson * Added export to Bibtex using XSLT * Added export to Bibtexml using XSLT 2002-12-03 Robby Stephenson * Released Bookcase version 0.4.1 * Added a Romanian translation from Iulian Ursache 2002-11-29 Robby Stephenson * Fixed compile problems with gcc 2.95 2002-11-24 Robby Stephenson * Released Bookcase version 0.4 * Printing improvements in XSLT layout 2002-11-10 Robby Stephenson * Added an Italian translation from FaUsT 2002-11-05 Robby Stephenson * Added a new toolbar for changing the grouping of the units * Changed the bookcase.spec file to add proper files 2002-10-20 Robby Stephenson * Released Bookcase version 0.3 2002-08-27 Robby Stephenson * Added two new XSLT files * Added printing support using KHTML * Added RPM spec file to automake 2002-02-21 Robby Stephenson * Released Bookcase version 0.2 2002-02-20 Robby Stephenson * Added BCColumnView::slotShowUnit() so to ensure that the proper listview in the stack is shown. * Added BCAttribute::capitalize for auto capitalization of names and titles. 2002-02-08 Robby Stephenson * Changed selection behavior so the two listviews do not sync now. Clicking in one clears the other. 2002-02-03 Robby Stephenson * Added article and suffix list to options in config dialog. 2002-01-11 Robby Stephenson * BCListView is now BCColumnView and can flip between different collections. Woo-hoo! Now multiple collections behave properly. Also started adding configuration options. 2002-01-10 Robby Stephenson * bctabcontrol.{h,cpp} Need KTabCtl::showTab to be public, so subclassed it. 2002-01-10 Robby Stephenson * configdialog.{h,cpp} Added beginnings of configuration dialog. 2001-12-06 Robby Stephenson * bcgroupview.cpp, bcattribute.cpp Fixed the bug for formatting names with suffixes like Jr. 2001-11-05 Robby Stephenson * bookcase.h Removed the File->Close() action item. 2001-11-05 Robby Stephenson * bcgroupview.cpp Fixed the bug where clicking the collapse or expand menu item on a BCUnitItem caused the pixmap to change to a folder. 2001-11-04 Robby Stephenson * Released Bookcase version 0.1 diff --git a/src/fetch/CMakeLists.txt b/src/fetch/CMakeLists.txt index 17ad8343..bf04e0bf 100644 --- a/src/fetch/CMakeLists.txt +++ b/src/fetch/CMakeLists.txt @@ -1,92 +1,91 @@ ADD_SUBDIRECTORY( scripts ) ########### next target ############### SET(fetch_STAT_SRCS allocinefetcher.cpp amazonfetcher.cpp amazonrequest.cpp animenfofetcher.cpp arxivfetcher.cpp bedethequefetcher.cpp bibliosharefetcher.cpp bibsonomyfetcher.cpp boardgamegeekfetcher.cpp colnectfetcher.cpp comicvinefetcher.cpp configwidget.cpp crossreffetcher.cpp dbcfetcher.cpp dblpfetcher.cpp discogsfetcher.cpp doubanfetcher.cpp dvdfrfetcher.cpp entrezfetcher.cpp execexternalfetcher.cpp fetcher.cpp fetcherinfolistitem.cpp fetcherinitializer.cpp fetcherjob.cpp fetchmanager.cpp fetchresult.cpp filmasterfetcher.cpp gcstarpluginfetcher.cpp gcstarthread.cpp giantbombfetcher.cpp googlebookfetcher.cpp googlescholarfetcher.cpp hathitrustfetcher.cpp - hmac_sha2.c ibsfetcher.cpp igdbfetcher.cpp imdbfetcher.cpp isbndbfetcher.cpp kinofetcher.cpp kinopoiskfetcher.cpp kinoteatrfetcher.cpp messagehandler.cpp + messagelogger.cpp mobygamesfetcher.cpp moviemeterfetcher.cpp mrlookupfetcher.cpp multifetcher.cpp musicbrainzfetcher.cpp omdbfetcher.cpp openlibraryfetcher.cpp - sha2.c springerfetcher.cpp srufetcher.cpp thegamesdbfetcher.cpp themoviedbfetcher.cpp videogamegeekfetcher.cpp vndbfetcher.cpp xmlfetcher.cpp z3950connection.cpp z3950fetcher.cpp ) add_library(fetch STATIC ${fetch_STAT_SRCS}) TARGET_LINK_LIBRARIES(fetch newstuff utils ${TELLICO_BTPARSE_LIBS} Qt5::Network KF5::Archive KF5::IconThemes KF5::JobWidgets KF5::KIOWidgets ) IF(${KF5KIO_VERSION} VERSION_GREATER "5.18.0") TARGET_LINK_LIBRARIES(fetch KF5::KIOGui ) ENDIF(${KF5KIO_VERSION} VERSION_GREATER "5.18.0") IF( Yaz_FOUND ) TARGET_LINK_LIBRARIES(fetch ${Yaz_LIBRARIES}) ENDIF( Yaz_FOUND ) ########### install files ############### INSTALL(FILES z3950-servers.cfg DESTINATION ${TELLICO_DATA_INSTALL_DIR} ) diff --git a/src/fetch/README.hmac b/src/fetch/README.hmac deleted file mode 100644 index 95a47467..00000000 --- a/src/fetch/README.hmac +++ /dev/null @@ -1,6 +0,0 @@ -The following files are part of a BSD-licensed SHA-2 implementation, -downloaded from http://www.ouah.org/ogay/hmac/ on 21 August 2009 - sha2.h - sha2.c - hmac_sha2.h - hmac_sha2.c \ No newline at end of file diff --git a/src/fetch/amazonfetcher.cpp b/src/fetch/amazonfetcher.cpp index 4995933c..fd580137 100644 --- a/src/fetch/amazonfetcher.cpp +++ b/src/fetch/amazonfetcher.cpp @@ -1,1089 +1,1205 @@ /*************************************************************************** - Copyright (C) 2004-2009 Robby Stephenson + Copyright (C) 2004-2020 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include #include "amazonfetcher.h" #include "amazonrequest.h" -#include "../translators/xslthandler.h" -#include "../translators/tellicoimporter.h" +#include "../collectionfactory.h" #include "../images/imagefactory.h" #include "../utils/guiproxy.h" #include "../collection.h" #include "../entry.h" #include "../field.h" #include "../fieldformat.h" #include "../utils/string_utils.h" #include "../utils/isbnvalidator.h" -#include "../utils/datafileregistry.h" #include "../gui/combobox.h" #include "../tellico_debug.h" #include #include #include #include #include #include #include #include #include -#include #include #include #include +#include #include #include #include #include +#include +#include +#include +#include namespace { static const int AMAZON_RETURNS_PER_REQUEST = 10; static const int AMAZON_MAX_RETURNS_TOTAL = 20; static const char* AMAZON_ASSOC_TOKEN = "tellico-20"; - // need to have these in the translation file - static const char* linkText = I18N_NOOP("Amazon Link"); } using namespace Tellico; using Tellico::Fetch::AmazonFetcher; // static +// see https://webservices.amazon.com/paapi5/documentation/common-request-parameters.html#host-and-region const AmazonFetcher::SiteData& AmazonFetcher::siteData(int site_) { Q_ASSERT(site_ >= 0); - Q_ASSERT(site_ < 15); - static SiteData dataVector[14] = { + Q_ASSERT(site_ < XX); + static SiteData dataVector[16] = { { i18n("Amazon (US)"), - QUrl(QLatin1String("http://webservices.amazon.com/onca/xml")), + "www.amazon.com", + "us-east-1", QLatin1String("us"), i18n("United States") }, { i18n("Amazon (UK)"), - QUrl(QLatin1String("http://webservices.amazon.co.uk/onca/xml")), + "www.amazon.co.uk", + "eu-west-1", QLatin1String("gb"), i18n("United Kingdom") }, { i18n("Amazon (Germany)"), - QUrl(QLatin1String("http://webservices.amazon.de/onca/xml")), + "www.amazon.de", + "eu-west-1", QLatin1String("de"), i18n("Germany") }, { i18n("Amazon (Japan)"), - QUrl(QLatin1String("http://webservices.amazon.co.jp/onca/xml")), + "www.amazon.co.jp", + "us-west-2", QLatin1String("jp"), i18n("Japan") }, { i18n("Amazon (France)"), - QUrl(QLatin1String("http://webservices.amazon.fr/onca/xml")), + "www.amazon.fr", + "eu-west-1", QLatin1String("fr"), i18n("France") }, { i18n("Amazon (Canada)"), - QUrl(QLatin1String("http://webservices.amazon.ca/onca/xml")), + "www.amazon.ca", + "us-east-1", QLatin1String("ca"), i18n("Canada") }, { + // TODO: no chinese in PAAPI-5 yet? i18n("Amazon (China)"), - QUrl(QLatin1String("http://webservices.amazon.cn/onca/xml")), + "www.amazon.cn", + "us-west-2", QLatin1String("ch"), i18n("China") }, { i18n("Amazon (Spain)"), - QUrl(QLatin1String("http://webservices.amazon.es/onca/xml")), + "www.amazon.es", + "eu-west-1", QLatin1String("es"), i18n("Spain") }, { i18n("Amazon (Italy)"), - QUrl(QLatin1String("http://webservices.amazon.it/onca/xml")), + "www.amazon.it", + "eu-west-1", QLatin1String("it"), i18n("Italy") }, { i18n("Amazon (Brazil)"), - QUrl(QLatin1String("http://webservices.amazon.com.br/onca/xml")), + "www.amazon.com.br", + "us-east-1", QLatin1String("br"), i18n("Brazil") }, { i18n("Amazon (Australia)"), - QUrl(QLatin1String("http://webservices.amazon.com.au/onca/xml")), + "www.amazon.com.au", + "us-west-2", QLatin1String("au"), i18n("Australia") }, { i18n("Amazon (India)"), - QUrl(QLatin1String("http://webservices.amazon.in/onca/xml")), + "www.amazon.in", + "eu-west-1", QLatin1String("in"), i18n("India") }, { i18n("Amazon (Mexico)"), - QUrl(QLatin1String("http://webservices.amazon.com.mx/onca/xml")), + "www.amazon.com.mx", + "us-east-1", QLatin1String("mx"), i18n("Mexico") }, { i18n("Amazon (Turkey)"), - QUrl(QLatin1String("http://webservices.amazon.com.tr/onca/xml")), + "www.amazon.com.tr", + "eu-west-1", QLatin1String("tr"), i18n("Turkey") + }, { + i18n("Amazon (Singapore)"), + "www.amazon.sg", + "us-west-2", + QLatin1String("sg"), + i18n("Singapore") + }, { + i18n("Amazon (UAE)"), + "www.amazon.ae", + "eu-west-1", + QLatin1String("ae"), + i18n("United Arab Emirates") } }; return dataVector[qBound(0, site_, static_cast(sizeof(dataVector)/sizeof(SiteData)))]; } AmazonFetcher::AmazonFetcher(QObject* parent_) - : Fetcher(parent_), m_xsltHandler(nullptr), m_site(Unknown), m_imageSize(MediumImage), - m_assoc(QLatin1String(AMAZON_ASSOC_TOKEN)), m_addLinkField(true), m_limit(AMAZON_MAX_RETURNS_TOTAL), + : Fetcher(parent_), m_site(Unknown), m_imageSize(MediumImage), + m_assoc(QLatin1String(AMAZON_ASSOC_TOKEN)), m_limit(AMAZON_MAX_RETURNS_TOTAL), m_countOffset(0), m_page(1), m_total(-1), m_numResults(0), m_job(nullptr), m_started(false) { - (void)linkText; // just to shut up the compiler + // to facilitate transition to Amazon PAAPI5, allow users to enable logging the Amazon + // results so they can be shared for debugging + const QByteArray enableLog = qgetenv("TELLICO_ENABLE_AMAZON_LOG").trimmed().toLower(); + m_enableLog = (enableLog == "true" || enableLog == "1"); } AmazonFetcher::~AmazonFetcher() { - delete m_xsltHandler; - m_xsltHandler = nullptr; } QString AmazonFetcher::source() const { return m_name.isEmpty() ? defaultName() : m_name; } QString AmazonFetcher::attribution() const { return i18n("This data is licensed under specific terms.", QLatin1String("https://affiliate-program.amazon.com/gp/advertising/api/detail/agreement.html")); } bool AmazonFetcher::canFetch(int type) const { return type == Data::Collection::Book || type == Data::Collection::ComicBook || type == Data::Collection::Bibtex || type == Data::Collection::Album || type == Data::Collection::Video || type == Data::Collection::Game || type == Data::Collection::BoardGame; } bool AmazonFetcher::canSearch(FetchKey k) const { // no UPC in Canada return k == Title - || k == Person - || k == ISBN - || (k == UPC && m_site != CA) - || k == Keyword; + || k == Person + || k == ISBN + || k == UPC + || k == Keyword; } void AmazonFetcher::readConfigHook(const KConfigGroup& config_) { const int site = config_.readEntry("Site", int(Unknown)); Q_ASSERT(site != Unknown); m_site = static_cast(site); if(m_name.isEmpty()) { m_name = siteData(m_site).title; } QString s = config_.readEntry("AccessKey"); if(!s.isEmpty()) { - m_access = s; + m_accessKey = s; } else { myWarning() << "No Amazon access key"; } s = config_.readEntry("AssocToken"); if(!s.isEmpty()) { m_assoc = s; } s = config_.readEntry("SecretKey"); if(!s.isEmpty()) { - m_amazonKey = s.toUtf8(); + m_secretKey = s; } else { myWarning() << "No Amazon secret key"; } int imageSize = config_.readEntry("Image Size", -1); if(imageSize > -1) { m_imageSize = static_cast(imageSize); } } void AmazonFetcher::search() { m_started = true; m_page = 1; m_total = -1; m_countOffset = 0; m_numResults = 0; doSearch(); } void AmazonFetcher::continueSearch() { m_started = true; m_limit += AMAZON_MAX_RETURNS_TOTAL; doSearch(); } void AmazonFetcher::doSearch() { - // calling secretKey() ensures that we try to read it first - if(secretKey().isEmpty() || m_access.isEmpty()) { - if(m_access.isEmpty()) { - myWarning() << "No Amazon access key"; - } + if(m_secretKey.isEmpty() || m_accessKey.isEmpty()) { // this message is split in two since the first half is reused later message(i18n("Access to data from Amazon.com requires an AWS Access Key ID and a Secret Key.") + QLatin1Char(' ') + i18n("Those values must be entered in the data source settings."), MessageHandler::Error); stop(); return; } -// myDebug() << "value = " << request().value; -// myDebug() << "getting page " << m_page; - - QMap params; - params.insert(QStringLiteral("Service"), QStringLiteral("AWSECommerceService")); - params.insert(QStringLiteral("AssociateTag"), m_assoc); - params.insert(QStringLiteral("AWSAccessKeyId"), m_access); - params.insert(QStringLiteral("Operation"), QStringLiteral("ItemSearch")); - params.insert(QStringLiteral("ResponseGroup"), QStringLiteral("Large")); - params.insert(QStringLiteral("ItemPage"), QString::number(m_page)); - // this should match the namespace in amazon2tellico.xsl - params.insert(QStringLiteral("Version"), QStringLiteral("2011-08-01")); - - const int type = collectionType(); - switch(type) { - case Data::Collection::Book: - case Data::Collection::ComicBook: - case Data::Collection::Bibtex: - params.insert(QStringLiteral("SearchIndex"), QStringLiteral("Books")); - params.insert(QStringLiteral("SortIndex"), QStringLiteral("relevancerank")); - break; - - case Data::Collection::Album: - params.insert(QStringLiteral("SearchIndex"), QStringLiteral("Music")); - break; - - case Data::Collection::Video: - // CA and JP appear to have a bug where Video only returns VHS or Music results - // DVD will return DVD, Blu-ray, etc. so just ignore VHS for those users - if(m_site == CA || m_site == JP || m_site == IT || m_site == ES) { - params.insert(QStringLiteral("SearchIndex"), QStringLiteral("DVD")); - } else { - params.insert(QStringLiteral("SearchIndex"), QStringLiteral("Video")); - } - params.insert(QStringLiteral("SortIndex"), QStringLiteral("relevancerank")); - break; - - case Data::Collection::Game: - params.insert(QStringLiteral("SearchIndex"), QStringLiteral("VideoGames")); - break; - - case Data::Collection::BoardGame: - params.insert(QStringLiteral("SearchIndex"), QStringLiteral("Toys")); - params.insert(QStringLiteral("SortIndex"), QStringLiteral("relevancerank")); - break; - - case Data::Collection::Coin: - case Data::Collection::Stamp: - case Data::Collection::Wine: - case Data::Collection::Base: - case Data::Collection::Card: - myDebug() << "can't fetch this type:" << collectionType(); - stop(); - return; + const QByteArray payload = requestPayload(request()); + if(payload.isEmpty()) { + stop(); + return; } - QString value = request().value; - - switch(request().key) { - case Title: - params.insert(QStringLiteral("Title"), value); - break; - - case Person: - if(type == Data::Collection::Video) { - params.insert(QStringLiteral("Actor"), value); - params.insert(QStringLiteral("Director"), value); - } else if(type == Data::Collection::Album) { - params.insert(QStringLiteral("Artist"), value); - } else if(type == Data::Collection::Game) { - params.insert(QStringLiteral("Manufacturer"), value); - } else { // books and bibtex - QString s = QStringLiteral("author:%1 or publisher:%2").arg(value, value); -// params.insert(QLatin1String("Author"), value, mib); -// params.insert(QLatin1String("Publisher"), value, mib); - params.insert(QStringLiteral("Power"), s); - } - break; - - case ISBN: - { - params.insert(QStringLiteral("Operation"), QStringLiteral("ItemLookup")); - - QString cleanValue = value; - cleanValue.remove(QLatin1Char('-')); - // ISBN only get digits or 'X' - QStringList isbns = FieldFormat::splitValue(cleanValue); - // Amazon isbn13 search is still very flaky, so if possible, we're going to convert - // all of them to isbn10. If we run into a 979 isbn13, then we're forced to do an - // isbn13 search - bool isbn13 = false; - for(QStringList::Iterator it = isbns.begin(); it != isbns.end(); ) { - if((*it).startsWith(QLatin1String("979"))) { - if(m_site == JP) { // never works for JP - myWarning() << "ISBN-13 searching not implemented for Japan"; - it = isbns.erase(it); - continue; - } - isbn13 = true; - break; - } - ++it; - } - // if we want isbn10, then convert all - if(!isbn13) { - for(QStringList::Iterator it = isbns.begin(); it != isbns.end(); ++it) { - if((*it).length() > 12) { - (*it) = ISBNValidator::isbn10(*it); - (*it).remove(QLatin1Char('-')); - } - } - // the default search is by ASIN, which prohibits SearchIndex - params.remove(QStringLiteral("SearchIndex")); - } - // limit to first 10 - while(isbns.size() > 10) { - isbns.pop_back(); - } - params.insert(QStringLiteral("ItemId"), isbns.join(QLatin1String(","))); - if(isbn13) { - params.insert(QStringLiteral("IdType"), QStringLiteral("EAN")); - } - } - break; - - case UPC: - { - QString cleanValue = value; - cleanValue.remove(QLatin1Char('-')); - // for EAN values, add 0 to beginning if not 13 characters - // in order to assume US country code from UPC value - QStringList values; - foreach(const QString& splitValue, cleanValue.split(FieldFormat::delimiterString())) { - QString tmpValue = splitValue; - if(m_site != US && tmpValue.length() == 12) { - tmpValue.prepend(QLatin1Char('0')); - } - values << tmpValue; - // limit to first 10 values - if(values.length() >= 10) { - break; - } - } - - params.insert(QStringLiteral("Operation"), QStringLiteral("ItemLookup")); - // US allows UPC, all others are EAN - if(m_site == US) { - params.insert(QStringLiteral("IdType"), QStringLiteral("UPC")); - } else { - params.insert(QStringLiteral("IdType"), QStringLiteral("EAN")); - } - params.insert(QStringLiteral("ItemId"), values.join(QLatin1String(","))); - } - break; - - case Keyword: - params.insert(QStringLiteral("Keywords"), value); - break; - - case Raw: - { - QString key = value.section(QLatin1Char('='), 0, 0).trimmed(); - QString str = value.section(QLatin1Char('='), 1).trimmed(); - params.insert(key, str); - } - break; - - default: - myWarning() << "key not recognized: " << request().key; - stop(); - return; + AmazonRequest request(m_accessKey, m_secretKey); + request.setHost(siteData(m_site).host); + request.setRegion(siteData(m_site).region); + + // debugging check + if(m_testResultsFile.isEmpty()) { + QUrl u; + u.setHost(QString::fromUtf8(siteData(m_site).host)); + m_job = KIO::storedHttpPost(payload, u, KIO::HideProgressInfo); + QMapIterator i(request.headers(payload)); + while(i.hasNext()) { + i.next(); + m_job->addMetaData(QStringLiteral("customHTTPHeader"), QString::fromUtf8(i.key() + ": " + i.value())); + } + } else { + myDebug() << "Reading" << m_testResultsFile; + m_job = KIO::storedGet(QUrl::fromLocalFile(m_testResultsFile), KIO::NoReload, KIO::HideProgressInfo); } - - AmazonRequest request(siteData(m_site).url, m_amazonKey); - QUrl newUrl = request.signedRequest(params); -// myDebug() << newUrl; - - m_job = KIO::storedGet(newUrl, KIO::NoReload, KIO::HideProgressInfo); KJobWidgets::setWindow(m_job, GUI::Proxy::widget()); connect(m_job.data(), &KJob::result, this, &AmazonFetcher::slotComplete); } void AmazonFetcher::stop() { if(!m_started) { return; } // myDebug(); if(m_job) { m_job->kill(); m_job = nullptr; } m_started = false; emit signalDone(this); } void AmazonFetcher::slotComplete(KJob*) { -// myDebug(); - if(m_job->error()) { m_job->uiDelegate()->showErrorMessage(); stop(); return; } - QByteArray data = m_job->data(); + const QByteArray data = m_job->data(); if(data.isEmpty()) { myDebug() << "no data"; stop(); return; } // since the fetch is done, don't worry about holding the job pointer m_job = nullptr; + if(m_enableLog) { + QTemporaryFile logFile(QDir::tempPath() + QStringLiteral("/amazon-search-items-XXXXXX.json")); + logFile.setAutoRemove(false); + if(logFile.open()) { + QTextStream t(&logFile); + t.setCodec("UTF-8"); + t << data; + myLog() << "Writing Amazon data output to" << logFile.fileName(); + } + } #if 0 myWarning() << "Remove debug from amazonfetcher.cpp"; - QFile f(QString::fromLatin1("/tmp/test%1.xml").arg(m_page)); + QFile f(QString::fromLatin1("/tmp/test%1.json").arg(m_page)); if(f.open(QIODevice::WriteOnly)) { QTextStream t(&f); t.setCodec("UTF-8"); t << data; } f.close(); #endif - QStringList errors; - if(m_total == -1) { - QDomDocument dom; - if(!dom.setContent(data, false)) { - myWarning() << "server did not return valid XML."; - stop(); - return; - } - // check for ItemSearchErrorResponse - if(dom.documentElement().tagName() == QLatin1String("ItemSearchErrorResponse")) { - QDomNode n = dom.documentElement().namedItem(QStringLiteral("Error")).namedItem(QStringLiteral("Message")); - if(!n.isNull()) { - message(n.toElement().text(), MessageHandler::Error); - stop(); - return; - } - } - // find TotalResults element - // it's in the first level under the root element - //ItemSearchResponse/Items/TotalResults - QDomNode n = dom.documentElement().namedItem(QStringLiteral("Items")) - .namedItem(QStringLiteral("TotalResults")); - QDomElement e = n.toElement(); - if(!e.isNull()) { - m_total = e.text().toInt(); - } - n = dom.documentElement().namedItem(QStringLiteral("Items")) - .namedItem(QStringLiteral("Request")) - .namedItem(QStringLiteral("Errors")); - e = n.toElement(); - if(!e.isNull()) { - QDomNodeList nodes = e.elementsByTagName(QStringLiteral("Error")); - for(int i = 0; i < nodes.count(); ++i) { - e = nodes.item(i).toElement().namedItem(QStringLiteral("Code")).toElement(); - if(!e.isNull() && e.text() == QLatin1String("AWS.ECommerceService.NoExactMatches")) { - // no exact match, not a real error, so skip - continue; - } - // for some reason, Amazon will return an error simply when a valid ISBN is not found - // I really want to ignore that, so check the IsValid element in the Request element - QDomNode isValidNode = n.parentNode().namedItem(QStringLiteral("IsValid")); - if(request().key == ISBN && isValidNode.toElement().text().toLower() == QLatin1String("true")) { - continue; - } - e = nodes.item(i).toElement().namedItem(QStringLiteral("Message")).toElement(); - if(!e.isNull()) { - errors << e.text(); - } - } - } - } - - if(!m_xsltHandler) { - initXSLTHandler(); - if(!m_xsltHandler) { // probably an error somewhere in the stylesheet loading - stop(); - return; - } - } - -// QRegExp stripHTML(QLatin1String("<.*>"), true); -// stripHTML.setMinimal(true); - - // assume amazon is always utf-8 - QString str = m_xsltHandler->applyStylesheet(QString::fromUtf8(data.constData(), data.size())); - Import::TellicoImporter imp(str); - // be quiet when loading images - imp.setOptions(imp.options() ^ Import::ImportShowImageErrors); - Data::CollPtr coll = imp.collection(); - if(!coll) { - myDebug() << "no collection pointer"; + QJsonParseError jsonError; + QJsonObject databject = QJsonDocument::fromJson(data, &jsonError).object(); + if(jsonError.error != QJsonParseError::NoError) { + myDebug() << "AmazonFetcher: JSON error -" << jsonError.errorString(); + message(jsonError.errorString(), MessageHandler::Error); stop(); return; } + QJsonObject resultObject = databject.value(QStringLiteral("SearchResult")).toObject(); + if(resultObject.isEmpty()) { + resultObject = databject.value(QStringLiteral("ItemsResult")).toObject(); + } - if(!m_addLinkField) { - // remove amazon field if it's not to be added - coll->removeField(QStringLiteral("amazon")); + if(m_total == -1) { + int totalResults = resultObject.value(QStringLiteral("TotalResultCount")).toInt(); + if(totalResults > 0) { + m_total = totalResults; +// myDebug() << "Total results is" << totalResults; + } } - Data::EntryList entries = coll->entries(); - if(entries.isEmpty() && !errors.isEmpty()) { + QStringList errors; + QJsonValue errorValue = databject.value(QLatin1String("Errors")); + if(!errorValue.isNull()) { + foreach(const QJsonValue& error, errorValue.toArray()) { + errors += error.toObject().value(QLatin1String("Message")).toString(); + } + } + if(!errors.isEmpty()) { for(QStringList::ConstIterator it = errors.constBegin(); it != errors.constEnd(); ++it) { myDebug() << "AmazonFetcher::" << *it; } message(errors[0], MessageHandler::Error); stop(); return; } + Data::CollPtr coll = createCollection(); + if(!coll) { + myDebug() << "no collection pointer"; + stop(); + return; + } + int count = -1; - foreach(Data::EntryPtr entry, entries) { + foreach(const QJsonValue& item, resultObject.value(QLatin1String("Items")).toArray()) { ++count; if(m_numResults >= m_limit) { break; } - if(count < m_countOffset) { - continue; - } if(!m_started) { // might get aborted break; } + Data::EntryPtr entry(new Data::Entry(coll)); + populateEntry(entry, item.toObject()); // special case book author // amazon is really bad about not putting spaces after periods if(coll->type() == Data::Collection::Book) { QRegExp rx(QLatin1String("\\.([^\\s])")); QStringList values = FieldFormat::splitValue(entry->field(QStringLiteral("author"))); for(QStringList::Iterator it = values.begin(); it != values.end(); ++it) { (*it).replace(rx, QStringLiteral(". \\1")); } entry->setField(QStringLiteral("author"), values.join(FieldFormat::delimiterString())); } // UK puts the year in the title for some reason if(m_site == UK && coll->type() == Data::Collection::Video) { QRegExp rx(QLatin1String("\\[(\\d{4})\\]")); QString t = entry->title(); if(rx.indexIn(t) > -1) { QString y = rx.cap(1); t = t.remove(rx).simplified(); entry->setField(QStringLiteral("title"), t); if(entry->field(QStringLiteral("year")).isEmpty()) { entry->setField(QStringLiteral("year"), y); } } } - // strip HTML from comments, or plot in movies - // tentatively don't do this, looks like ECS 4 cleaned everything up -/* - if(coll->type() == Data::Collection::Video) { - QString plot = entry->field(QLatin1String("plot")); - plot.remove(stripHTML); - entry->setField(QLatin1String("plot"), plot); - } else if(coll->type() == Data::Collection::Game) { - QString desc = entry->field(QLatin1String("description")); - desc.remove(stripHTML); - entry->setField(QLatin1String("description"), desc); - } else { - QString comments = entry->field(QLatin1String("comments")); - comments.remove(stripHTML); - entry->setField(QLatin1String("comments"), comments); - } -*/ // myDebug() << entry->title(); FetchResult* r = new FetchResult(Fetcher::Ptr(this), entry); m_entries.insert(r->uid, entry); emit signalResultFound(r); ++m_numResults; } // we might have gotten aborted if(!m_started) { return; } // are there any additional results to get? - m_hasMoreResults = m_page * AMAZON_RETURNS_PER_REQUEST < m_total; + m_hasMoreResults = m_testResultsFile.isEmpty() && (m_page * AMAZON_RETURNS_PER_REQUEST < m_total); const int currentTotal = qMin(m_total, m_limit); - if(m_page * AMAZON_RETURNS_PER_REQUEST < currentTotal) { + if(m_testResultsFile.isEmpty() && (m_page * AMAZON_RETURNS_PER_REQUEST < currentTotal)) { int foundCount = (m_page-1) * AMAZON_RETURNS_PER_REQUEST + coll->entryCount(); message(i18n("Results from %1: %2/%3", source(), foundCount, m_total), MessageHandler::Status); ++m_page; m_countOffset = 0; doSearch(); } else if(request().value.count(QLatin1Char(';')) > 9) { // start new request after cutting off first 10 isbn values FetchRequest newRequest = request(); newRequest.value = request().value.section(QLatin1Char(';'), 10); startSearch(newRequest); } else { m_countOffset = m_entries.count() % AMAZON_RETURNS_PER_REQUEST; if(m_countOffset == 0) { ++m_page; // need to go to next page } stop(); } } Tellico::Data::EntryPtr AmazonFetcher::fetchEntryHook(uint uid_) { Data::EntryPtr entry = m_entries[uid_]; if(!entry) { myWarning() << "no entry in dict"; return entry; } // do what we can to remove useless keywords const int type = collectionType(); switch(type) { case Data::Collection::Book: case Data::Collection::ComicBook: case Data::Collection::Bibtex: if(optionalFields().contains(QStringLiteral("keyword"))) { StringSet newWords; const QStringList keywords = FieldFormat::splitValue(entry->field(QStringLiteral("keyword"))); foreach(const QString& keyword, keywords) { - // the amazon2tellico stylesheet separates keywords with '/' - const QStringList words = keyword.split(QLatin1Char('/')); - foreach(const QString& word, words) { - if(word == QLatin1String("General") || - word == QLatin1String("Subjects") || - word == QLatin1String("Par prix") || // french stuff - word == QLatin1String("Divers") || // french stuff - word.startsWith(QLatin1Char('(')) || - word.startsWith(QLatin1String("Authors"))) { - continue; - } - newWords.add(word); + if(keyword == QLatin1String("General") || + keyword == QLatin1String("Subjects") || + keyword == QLatin1String("Par prix") || // french stuff + keyword == QLatin1String("Divers") || // french stuff + keyword.startsWith(QLatin1Char('(')) || + keyword.startsWith(QLatin1String("Authors"))) { + continue; } + newWords.add(keyword); } entry->setField(QStringLiteral("keyword"), newWords.toList().join(FieldFormat::delimiterString())); } entry->setField(QStringLiteral("comments"), Tellico::decodeHTML(entry->field(QStringLiteral("comments")))); break; case Data::Collection::Video: { const QString genres = QStringLiteral("genre"); QStringList oldWords = FieldFormat::splitValue(entry->field(genres)); StringSet words; // only care about genres that have "Genres" in the amazon response // and take the first word after that for(QStringList::Iterator it = oldWords.begin(); it != oldWords.end(); ++it) { if((*it).indexOf(QLatin1String("Genres")) == -1) { continue; } // the amazon2tellico stylesheet separates words with '/' QStringList nodes = (*it).split(QLatin1Char('/')); for(QStringList::Iterator it2 = nodes.begin(); it2 != nodes.end(); ++it2) { if(*it2 != QLatin1String("Genres")) { continue; } ++it2; if(it2 != nodes.end() && *it2 != QLatin1String("General")) { words.add(*it2); } break; // we're done } } entry->setField(genres, words.toList().join(FieldFormat::delimiterString())); // language tracks get duplicated, too words.clear(); words.add(FieldFormat::splitValue(entry->field(QStringLiteral("language")))); entry->setField(QStringLiteral("language"), words.toList().join(FieldFormat::delimiterString())); } entry->setField(QStringLiteral("plot"), Tellico::decodeHTML(entry->field(QStringLiteral("plot")))); break; case Data::Collection::Album: { const QString genres = QStringLiteral("genre"); QStringList oldWords = FieldFormat::splitValue(entry->field(genres)); StringSet words; // only care about genres that have "Styles" in the amazon response // and take the first word after that for(QStringList::Iterator it = oldWords.begin(); it != oldWords.end(); ++it) { if((*it).indexOf(QLatin1String("Styles")) == -1) { continue; } // the amazon2tellico stylesheet separates words with '/' QStringList nodes = (*it).split(QLatin1Char('/')); bool isStyle = false; for(QStringList::Iterator it2 = nodes.begin(); it2 != nodes.end(); ++it2) { if(!isStyle) { if(*it2 == QLatin1String("Styles")) { isStyle = true; } continue; } if(*it2 != QLatin1String("General")) { words.add(*it2); } } } entry->setField(genres, words.toList().join(FieldFormat::delimiterString())); } entry->setField(QStringLiteral("comments"), Tellico::decodeHTML(entry->field(QStringLiteral("comments")))); break; case Data::Collection::Game: entry->setField(QStringLiteral("description"), Tellico::decodeHTML(entry->field(QStringLiteral("description")))); break; } // clean up the title parseTitle(entry); // also sometimes table fields have rows but no values Data::FieldList fields = entry->collection()->fields(); QRegExp blank(QLatin1String("[\\s") + FieldFormat::columnDelimiterString() + FieldFormat::delimiterString() + QLatin1String("]+")); // only white space, column separators and value separators foreach(Data::FieldPtr fIt, fields) { if(fIt->type() != Data::Field::Table) { continue; } if(blank.exactMatch(entry->field(fIt))) { entry->setField(fIt, QString()); } } + // don't want to show image urls in the fetch dialog + // so clear them after reading the URL QString imageURL; switch(m_imageSize) { case SmallImage: imageURL = entry->field(QStringLiteral("small-image")); + entry->setField(QStringLiteral("small-image"), QString()); break; case MediumImage: imageURL = entry->field(QStringLiteral("medium-image")); + entry->setField(QStringLiteral("medium-image"), QString()); break; case LargeImage: imageURL = entry->field(QStringLiteral("large-image")); + entry->setField(QStringLiteral("large-image"), QString()); break; case NoImage: default: break; } -// myDebug() << "grabbing " << imageURL.toDisplayString(); + if(!imageURL.isEmpty()) { +// myDebug() << "grabbing " << imageURL; QString id = ImageFactory::addImage(QUrl::fromUserInput(imageURL), true); if(id.isEmpty()) { message(i18n("The cover image could not be loaded."), MessageHandler::Warning); } else { // amazon serves up 1x1 gifs occasionally, but that's caught in the image constructor // all relevant collection types have cover fields entry->setField(QStringLiteral("cover"), id); } } - // don't want to show image urls in the fetch dialog - entry->setField(QStringLiteral("small-image"), QString()); - entry->setField(QStringLiteral("medium-image"), QString()); - entry->setField(QStringLiteral("large-image"), QString()); return entry; } -void AmazonFetcher::initXSLTHandler() { - QString xsltfile = DataFileRegistry::self()->locate(QStringLiteral("amazon2tellico.xsl")); - if(xsltfile.isEmpty()) { - myWarning() << "can not locate amazon2tellico.xsl."; - return; - } - - QUrl u = QUrl::fromLocalFile(xsltfile); - - delete m_xsltHandler; - m_xsltHandler = new XSLTHandler(u); - if(!m_xsltHandler->isValid()) { - myWarning() << "error in amazon2tellico.xsl."; - delete m_xsltHandler; - m_xsltHandler = nullptr; - return; - } -} - Tellico::Fetch::FetchRequest AmazonFetcher::updateRequest(Data::EntryPtr entry_) { const int type = entry_->collection()->type(); const QString t = entry_->field(QStringLiteral("title")); if(type == Data::Collection::Book || type == Data::Collection::ComicBook || type == Data::Collection::Bibtex) { const QString isbn = entry_->field(QStringLiteral("isbn")); if(!isbn.isEmpty()) { return FetchRequest(Fetch::ISBN, isbn); } const QString a = entry_->field(QStringLiteral("author")); if(!a.isEmpty()) { return t.isEmpty() ? FetchRequest(Fetch::Person, a) : FetchRequest(Fetch::Keyword, t + QLatin1Char('-') + a); } } else if(type == Data::Collection::Album) { const QString a = entry_->field(QStringLiteral("artist")); if(!a.isEmpty()) { return t.isEmpty() ? FetchRequest(Fetch::Person, a) : FetchRequest(Fetch::Keyword, t + QLatin1Char('-') + a); } } // optimistically try searching for title and rely on Collection::sameEntry() to figure things out if(!t.isEmpty()) { return FetchRequest(Fetch::Title, t); } return FetchRequest(); } +QByteArray AmazonFetcher::requestPayload(FetchRequest request_) { + QJsonObject payload; + payload.insert(QLatin1String("PartnerTag"), m_assoc); + payload.insert(QLatin1String("PartnerType"), QLatin1String("Associates")); + payload.insert(QLatin1String("Operation"), QLatin1String("SearchItems")); + payload.insert(QLatin1String("SortBy"), QLatin1String("Relevance")); + // not mandatory +// payload.insert(QLatin1String("Marketplace"), QLatin1String(siteData(m_site).host)); + if(m_page > 1) { + payload.insert(QLatin1String("ItemPage"), m_page); + } + + QJsonArray resources; + resources.append(QLatin1String("ItemInfo.Title")); + resources.append(QLatin1String("ItemInfo.ContentInfo")); + resources.append(QLatin1String("ItemInfo.ByLineInfo")); + resources.append(QLatin1String("ItemInfo.TechnicalInfo")); + + const int type = request_.collectionType; + switch(type) { + case Data::Collection::Book: + case Data::Collection::ComicBook: + case Data::Collection::Bibtex: + payload.insert(QLatin1String("SearchIndex"), QLatin1String("Books")); + resources.append(QLatin1String("ItemInfo.ExternalIds")); + resources.append(QLatin1String("ItemInfo.ManufactureInfo")); + break; + + case Data::Collection::Album: + payload.insert(QLatin1String("SearchIndex"), QLatin1String("Music")); + break; + + case Data::Collection::Video: + // CA and JP appear to have a bug where Video only returns VHS or Music results + // DVD will return DVD, Blu-ray, etc. so just ignore VHS for those users + payload.insert(QLatin1String("SearchIndex"), QLatin1String("MoviesAndTV")); + if(m_site == CA || m_site == JP || m_site == IT || m_site == ES) { + payload.insert(QStringLiteral("SearchIndex"), QStringLiteral("DVD")); + } else { + payload.insert(QStringLiteral("SearchIndex"), QStringLiteral("Video")); + } +// params.insert(QStringLiteral("SortIndex"), QStringLiteral("relevancerank")); + resources.append(QLatin1String("ItemInfo.ContentRating")); + break; + + case Data::Collection::Game: + payload.insert(QLatin1String("SearchIndex"), QLatin1String("VideoGames")); + break; + + case Data::Collection::BoardGame: + payload.insert(QLatin1String("SearchIndex"), QLatin1String("ToysAndGames")); +// params.insert(QStringLiteral("SortIndex"), QStringLiteral("relevancerank")); + break; + + case Data::Collection::Coin: + case Data::Collection::Stamp: + case Data::Collection::Wine: + case Data::Collection::Base: + case Data::Collection::Card: + myDebug() << "can't fetch this type:" << collectionType(); + return QByteArray(); + } + + switch(request_.key) { + case Title: + payload.insert(QLatin1String("Title"), request_.value); + break; + + case Person: + if(type == Data::Collection::Video) { + payload.insert(QStringLiteral("Actor"), request_.value); +// payload.insert(QStringLiteral("Director"), request_.value); + } else if(type == Data::Collection::Album) { + payload.insert(QStringLiteral("Artist"), request_.value); + } else if(type == Data::Collection::Book) { + payload.insert(QLatin1String("Author"), request_.value); + } else { + payload.insert(QLatin1String("Keywords"), request_.value); + } + break; + + case ISBN: + { + QString cleanValue = request_.value; + cleanValue.remove(QLatin1Char('-')); + // ISBN only get digits or 'X' + QStringList isbns = FieldFormat::splitValue(cleanValue); + // Amazon isbn13 search is still very flaky, so if possible, we're going to convert + // all of them to isbn10. If we run into a 979 isbn13, then we're forced to do an + // isbn13 search + bool isbn13 = false; + for(QStringList::Iterator it = isbns.begin(); it != isbns.end(); ) { + if((*it).startsWith(QLatin1String("979"))) { + isbn13 = true; + break; + } + ++it; + } + // if we want isbn10, then convert all + if(!isbn13) { + for(QStringList::Iterator it = isbns.begin(); it != isbns.end(); ++it) { + if((*it).length() > 12) { + (*it) = ISBNValidator::isbn10(*it); + (*it).remove(QLatin1Char('-')); + } + } + } + // limit to first 10 + while(isbns.size() > 10) { + isbns.pop_back(); + } + payload.insert(QLatin1String("Keywords"), isbns.join(QLatin1String("|"))); + if(isbn13) { +// params.insert(QStringLiteral("IdType"), QStringLiteral("EAN")); + } + } + break; + + case UPC: + { + QString cleanValue = request_.value; + cleanValue.remove(QLatin1Char('-')); + // for EAN values, add 0 to beginning if not 13 characters + // in order to assume US country code from UPC value + QStringList values; + foreach(const QString& splitValue, cleanValue.split(FieldFormat::delimiterString())) { + QString tmpValue = splitValue; + if(m_site != US && tmpValue.length() == 12) { + tmpValue.prepend(QLatin1Char('0')); + } + values << tmpValue; + // limit to first 10 values + if(values.length() >= 10) { + break; + } + } + + payload.insert(QLatin1String("Keywords"), values.join(QLatin1String("|"))); + } + break; + + case Keyword: + payload.insert(QLatin1String("Keywords"), request_.value); + break; + + case Raw: + { + QString key = request_.value.section(QLatin1Char('='), 0, 0).trimmed(); + QString str = request_.value.section(QLatin1Char('='), 1).trimmed(); + payload.insert(key, str); + } + break; + + default: + myWarning() << "key not recognized: " << request().key; + return QByteArray(); + } + + switch(m_imageSize) { + case SmallImage: resources.append(QLatin1String("Images.Primary.Small")); break; + case MediumImage: resources.append(QLatin1String("Images.Primary.Medium")); break; + case LargeImage: resources.append(QLatin1String("Images.Primary.Large")); break; + case NoImage: break; + } + + payload.insert(QLatin1String("Resources"), resources); + return QJsonDocument(payload).toJson(QJsonDocument::Compact); +} + +Tellico::Data::CollPtr AmazonFetcher::createCollection() { + Data::CollPtr coll = CollectionFactory::collection(collectionType(), true); + if(!coll) { + return coll; + } + + QString imageFieldName; + switch(m_imageSize) { + case SmallImage: imageFieldName = QStringLiteral("small-image"); break; + case MediumImage: imageFieldName = QStringLiteral("medium-image"); break; + case LargeImage: imageFieldName = QStringLiteral("large-image"); break; + case NoImage: break; + } + + if(!imageFieldName.isEmpty()) { + Data::FieldPtr field(new Data::Field(imageFieldName, QString(), Data::Field::URL)); + coll->addField(field); + } + + if(optionalFields().contains(QStringLiteral("amazon"))) { + Data::FieldPtr field(new Data::Field(QStringLiteral("amazon"), i18n("Amazon Link"), Data::Field::URL)); + field->setCategory(i18n("General")); + coll->addField(field); + } + + return coll; +} + +void AmazonFetcher::populateEntry(Data::EntryPtr entry_, const QJsonObject& info_) { + QVariantMap itemMap = info_.value(QLatin1String("ItemInfo")).toObject().toVariantMap(); + entry_->setField(QStringLiteral("title"), mapValue(itemMap, "Title", "DisplayValue")); + const QString isbn = mapValue(itemMap, "ExternalIds", "ISBNs", "DisplayValues"); + if(!isbn.isEmpty()) { + // could be duplicate isbn10 and isbn13 values + QStringList isbns = FieldFormat::splitValue(isbn, FieldFormat::StringSplit); + for(QStringList::Iterator it = isbns.begin(); it != isbns.end(); ++it) { + if((*it).length() > 12) { + (*it) = ISBNValidator::isbn10(*it); + (*it).remove(QLatin1Char('-')); + } + } + isbns.removeDuplicates(); + entry_->setField(QStringLiteral("isbn"), isbns.join(FieldFormat::delimiterString())); + } + + QStringList actors, artists, authors, illustrators, publishers; + QVariantMap byLineMap = itemMap.value(QLatin1String("ByLineInfo")).toMap(); + QVariantList contribArray = byLineMap.value(QLatin1String("Contributors")).toList(); + foreach(const QVariant& v, contribArray) { + const QVariantMap contribMap = v.toMap(); + const QString role = contribMap.value(QLatin1String("Role")).toString(); + const QString name = contribMap.value(QLatin1String("Name")).toString(); + if(role == QLatin1String("Actor")) { + actors += name; + } else if(role == QLatin1String("Artist")) { + artists += name; + } else if(role == QLatin1String("Author")) { + authors += name; + } else if(role == QLatin1String("Illustrator")) { + illustrators += name; + } else if(role == QLatin1String("Publisher")) { + publishers += name; + } + } + // assume for books that the manufacturer is the publishers + if(collectionType() == Data::Collection::Book || + collectionType() == Data::Collection::Bibtex || + collectionType() == Data::Collection::ComicBook) { + const QString manufacturer = byLineMap.value(QLatin1String("Manufacturer")).toMap() + .value(QLatin1String("DisplayValue")).toString(); + publishers += manufacturer; + } + + actors.removeDuplicates(); + artists.removeDuplicates(); + authors.removeDuplicates(); + illustrators.removeDuplicates(); + publishers.removeDuplicates(); + + if(!actors.isEmpty()) { + entry_->setField(QStringLiteral("cast"), actors.join(FieldFormat::delimiterString())); + } + if(!artists.isEmpty()) { + entry_->setField(QStringLiteral("artist"), artists.join(FieldFormat::delimiterString())); + } + if(!authors.isEmpty()) { + entry_->setField(QStringLiteral("author"), authors.join(FieldFormat::delimiterString())); + } + if(!illustrators.isEmpty()) { + entry_->setField(QStringLiteral("illustrator"), illustrators.join(FieldFormat::delimiterString())); + } + if(!publishers.isEmpty()) { + entry_->setField(QStringLiteral("publisher"), publishers.join(FieldFormat::delimiterString())); + } + + QVariantMap contentMap = itemMap.value(QLatin1String("ContentInfo")).toMap(); + entry_->setField(QStringLiteral("edition"), mapValue(contentMap, "Edition", "DisplayValue")); + entry_->setField(QStringLiteral("pages"), mapValue(contentMap, "PagesCount", "DisplayValue")); + const QString pubDate = mapValue(contentMap, "PublicationDate", "DisplayValue"); + if(!pubDate.isEmpty()) { + entry_->setField(QStringLiteral("pub_year"), pubDate.left(4)); + } + QVariantList langArray = itemMap.value(QLatin1String("ContentInfo")).toMap() + .value(QStringLiteral("Languages")).toMap() + .value(QStringLiteral("DisplayValues")).toList(); + QStringList langs; + foreach(const QVariant& v, langArray) { + langs += mapValue(v.toMap(), "DisplayValue"); + } + langs.removeDuplicates(); + langs.removeAll(QString()); + entry_->setField(QStringLiteral("language"), langs.join(FieldFormat::delimiterString())); + + if(collectionType() == Data::Collection::Book || + collectionType() == Data::Collection::Bibtex || + collectionType() == Data::Collection::ComicBook) { + QVariantMap classificationsMap = itemMap.value(QLatin1String("Classifications")).toMap(); + QVariantMap technicalMap = itemMap.value(QLatin1String("TechnicalInfo")).toMap(); + QString binding = mapValue(classificationsMap, "Binding", "DisplayValue"); + if(binding.isEmpty()) { + binding = mapValue(technicalMap, "Formats", "DisplayValues"); + } + if(binding.contains(QStringLiteral("Paperback")) && binding != QStringLiteral("Trade Paperback")) { + binding = i18n("Paperback"); + } else if(binding.contains(QStringLiteral("Hard"))) { // could be Hardcover or Hardback + binding = i18n("Hardback"); + } + entry_->setField(QStringLiteral("binding"), binding); + } + + QVariantMap imagesMap = info_.value(QLatin1String("Images")).toObject().toVariantMap(); + switch(m_imageSize) { + case SmallImage: + entry_->setField(QStringLiteral("small-image"), mapValue(imagesMap, "Primary", "Small", "URL")); + break; + case MediumImage: + entry_->setField(QStringLiteral("medium-image"), mapValue(imagesMap, "Primary", "Medium", "URL")); + break; + case LargeImage: + entry_->setField(QStringLiteral("large-image"), mapValue(imagesMap, "Primary", "Large", "URL")); + break; + case NoImage: + break; + } + + if(optionalFields().contains(QStringLiteral("amazon"))) { + entry_->setField(QStringLiteral("amazon"), mapValue(info_.toVariantMap(), "DetailPageURL")); + } +} + void AmazonFetcher::parseTitle(Tellico::Data::EntryPtr entry_) { // assume that everything in brackets or parentheses is extra - QRegExp rx(QLatin1String("[\\(\\[](.*)[\\)\\]]")); - rx.setMinimal(true); + static const QRegularExpression rx(QLatin1String("[\\(\\[](.*?)[\\)\\]]")); QString title = entry_->field(QStringLiteral("title")); - int pos = rx.indexIn(title); - while(pos > -1) { - if(parseTitleToken(entry_, rx.cap(1))) { - title.remove(pos, rx.matchedLength()); + int pos = 0; + QRegularExpressionMatch match = rx.match(title, pos); + while(match.hasMatch()) { + pos = match.capturedStart(); + if(parseTitleToken(entry_, match.captured(1))) { + title.remove(match.capturedStart(), match.capturedLength()); --pos; // search again there } - pos = rx.indexIn(title, pos+1); + match = rx.match(title, pos+1); } - entry_->setField(QStringLiteral("title"), title.trimmed()); + entry_->setField(QStringLiteral("title"), title.simplified()); } bool AmazonFetcher::parseTitleToken(Tellico::Data::EntryPtr entry_, const QString& token_) { // myDebug() << "title token:" << token_; // if res = true, then the token gets removed from the title bool res = false; if(token_.indexOf(QLatin1String("widescreen"), 0, Qt::CaseInsensitive) > -1 || token_.indexOf(i18n("Widescreen"), 0, Qt::CaseInsensitive) > -1) { entry_->setField(QStringLiteral("widescreen"), QStringLiteral("true")); // res = true; leave it in the title } else if(token_.indexOf(QLatin1String("full screen"), 0, Qt::CaseInsensitive) > -1) { // skip, but go ahead and remove from title res = true; } else if(token_.indexOf(QLatin1String("import"), 0, Qt::CaseInsensitive) > -1) { // skip, but go ahead and remove from title res = true; } if(token_.indexOf(QLatin1String("blu-ray"), 0, Qt::CaseInsensitive) > -1) { entry_->setField(QStringLiteral("medium"), i18n("Blu-ray")); res = true; } else if(token_.indexOf(QLatin1String("hd dvd"), 0, Qt::CaseInsensitive) > -1) { entry_->setField(QStringLiteral("medium"), i18n("HD DVD")); res = true; } else if(token_.indexOf(QLatin1String("vhs"), 0, Qt::CaseInsensitive) > -1) { entry_->setField(QStringLiteral("medium"), i18n("VHS")); res = true; } if(token_.indexOf(QLatin1String("director's cut"), 0, Qt::CaseInsensitive) > -1 || token_.indexOf(i18n("Director's Cut"), 0, Qt::CaseInsensitive) > -1) { entry_->setField(QStringLiteral("directors-cut"), QStringLiteral("true")); // res = true; leave it in the title } if(token_.toLower() == QLatin1String("ntsc")) { entry_->setField(QStringLiteral("format"), i18n("NTSC")); res = true; } if(token_.toLower() == QLatin1String("dvd")) { entry_->setField(QStringLiteral("medium"), i18n("DVD")); res = true; } - static QRegExp regionRx(QLatin1String("Region [1-9]")); - if(regionRx.indexIn(token_) > -1) { - entry_->setField(QStringLiteral("region"), i18n(regionRx.cap(0).toUtf8().constData())); + if(token_.indexOf(QLatin1String("series"), 0, Qt::CaseInsensitive) > -1) { + entry_->setField(QStringLiteral("series"), token_); + res = true; + } + static const QRegularExpression regionRx(QLatin1String("Region [1-9]")); + QRegularExpressionMatch match = regionRx.match(token_); + if(match.hasMatch()) { + entry_->setField(QStringLiteral("region"), i18n(match.captured().toUtf8().constData())); res = true; } if(entry_->collection()->type() == Data::Collection::Game) { Data::FieldPtr f = entry_->collection()->fieldByName(QStringLiteral("platform")); if(f && f->allowed().contains(token_)) { res = true; } } return res; } -QString AmazonFetcher::secretKey() const { - return QString::fromUtf8(m_amazonKey); -} - //static QString AmazonFetcher::defaultName() { return i18n("Amazon.com Web Services"); } QString AmazonFetcher::defaultIcon() { return favIcon("http://www.amazon.com"); } Tellico::StringHash AmazonFetcher::allOptionalFields() { StringHash hash; hash[QStringLiteral("keyword")] = i18n("Keywords"); + hash[QStringLiteral("amazon")] = i18n("Amazon Link"); return hash; } Tellico::Fetch::ConfigWidget* AmazonFetcher::configWidget(QWidget* parent_) const { return new AmazonFetcher::ConfigWidget(parent_, this); } AmazonFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const AmazonFetcher* fetcher_/*=0*/) : Fetch::ConfigWidget(parent_) { QGridLayout* l = new QGridLayout(optionsWidget()); l->setSpacing(4); l->setColumnStretch(1, 10); int row = -1; QLabel* al = new QLabel(i18n("Registration is required for accessing the %1 data source. " "If you agree to the terms and conditions, sign " "up for an account, and enter your information below.", AmazonFetcher::defaultName(), QLatin1String("https://affiliate-program.amazon.com/gp/flex/advertising/api/sign-in.html")), optionsWidget()); al->setOpenExternalLinks(true); al->setWordWrap(true); ++row; l->addWidget(al, row, 0, 1, 2); // richtext gets weird with size al->setMinimumWidth(al->sizeHint().width()); QLabel* label = new QLabel(i18n("Access key: "), optionsWidget()); l->addWidget(label, ++row, 0); m_accessEdit = new QLineEdit(optionsWidget()); connect(m_accessEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotSetModified); l->addWidget(m_accessEdit, row, 1); QString w = i18n("Access to data from Amazon.com requires an AWS Access Key ID and a Secret Key."); label->setWhatsThis(w); m_accessEdit->setWhatsThis(w); label->setBuddy(m_accessEdit); label = new QLabel(i18n("Secret key: "), optionsWidget()); l->addWidget(label, ++row, 0); m_secretKeyEdit = new QLineEdit(optionsWidget()); // m_secretKeyEdit->setEchoMode(QLineEdit::PasswordEchoOnEdit); connect(m_secretKeyEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotSetModified); l->addWidget(m_secretKeyEdit, row, 1); label->setWhatsThis(w); m_secretKeyEdit->setWhatsThis(w); label->setBuddy(m_secretKeyEdit); label = new QLabel(i18n("Country: "), optionsWidget()); l->addWidget(label, ++row, 0); m_siteCombo = new GUI::ComboBox(optionsWidget()); for(int i = 0; i < XX; ++i) { const AmazonFetcher::SiteData& siteData = AmazonFetcher::siteData(i); QIcon icon(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/locale/countries/%1/flag.png").arg(siteData.country))); m_siteCombo->addItem(icon, siteData.countryName, i); m_siteCombo->model()->sort(0); } void (GUI::ComboBox::* activatedInt)(int) = &GUI::ComboBox::activated; connect(m_siteCombo, activatedInt, this, &ConfigWidget::slotSetModified); connect(m_siteCombo, activatedInt, this, &ConfigWidget::slotSiteChanged); l->addWidget(m_siteCombo, row, 1); w = i18n("Amazon.com provides data from several different localized sites. Choose the one " "you wish to use for this data source."); label->setWhatsThis(w); m_siteCombo->setWhatsThis(w); label->setBuddy(m_siteCombo); label = new QLabel(i18n("&Image size: "), optionsWidget()); l->addWidget(label, ++row, 0); m_imageCombo = new GUI::ComboBox(optionsWidget()); m_imageCombo->addItem(i18n("Small Image"), SmallImage); m_imageCombo->addItem(i18n("Medium Image"), MediumImage); m_imageCombo->addItem(i18n("Large Image"), LargeImage); m_imageCombo->addItem(i18n("No Image"), NoImage); connect(m_imageCombo, activatedInt, this, &ConfigWidget::slotSetModified); l->addWidget(m_imageCombo, row, 1); w = i18n("The cover image may be downloaded as well. However, too many large images in the " "collection may degrade performance."); label->setWhatsThis(w); m_imageCombo->setWhatsThis(w); label->setBuddy(m_imageCombo); label = new QLabel(i18n("&Associate's ID: "), optionsWidget()); l->addWidget(label, ++row, 0); m_assocEdit = new QLineEdit(optionsWidget()); void (QLineEdit::* textChanged)(const QString&) = &QLineEdit::textChanged; connect(m_assocEdit, textChanged, this, &ConfigWidget::slotSetModified); l->addWidget(m_assocEdit, row, 1); w = i18n("The associate's id identifies the person accessing the Amazon.com Web Services, and is included " "in any links to the Amazon.com site."); label->setWhatsThis(w); m_assocEdit->setWhatsThis(w); label->setBuddy(m_assocEdit); l->setRowStretch(++row, 10); if(fetcher_) { m_siteCombo->setCurrentData(fetcher_->m_site); - m_accessEdit->setText(fetcher_->m_access); - m_secretKeyEdit->setText(fetcher_->secretKey()); + m_accessEdit->setText(fetcher_->m_accessKey); + m_secretKeyEdit->setText(fetcher_->m_secretKey); m_assocEdit->setText(fetcher_->m_assoc); m_imageCombo->setCurrentData(fetcher_->m_imageSize); } else { // defaults m_assocEdit->setText(QLatin1String(AMAZON_ASSOC_TOKEN)); m_imageCombo->setCurrentData(MediumImage); } addFieldsWidget(AmazonFetcher::allOptionalFields(), fetcher_ ? fetcher_->optionalFields() : QStringList()); KAcceleratorManager::manage(optionsWidget()); } void AmazonFetcher::ConfigWidget::saveConfigHook(KConfigGroup& config_) { int n = m_siteCombo->currentData().toInt(); config_.writeEntry("Site", n); QString s = m_accessEdit->text().trimmed(); if(!s.isEmpty()) { config_.writeEntry("AccessKey", s); } - QByteArray b = m_secretKeyEdit->text().trimmed().toUtf8(); - if(!b.isEmpty()) { - config_.writeEntry("SecretKey", b); + s = m_secretKeyEdit->text().trimmed(); + if(!s.isEmpty()) { + config_.writeEntry("SecretKey", s); } s = m_assocEdit->text().trimmed(); if(!s.isEmpty()) { config_.writeEntry("AssocToken", s); } n = m_imageCombo->currentData().toInt(); config_.writeEntry("Image Size", n); } QString AmazonFetcher::ConfigWidget::preferredName() const { return AmazonFetcher::siteData(m_siteCombo->currentData().toInt()).title; } void AmazonFetcher::ConfigWidget::slotSiteChanged() { emit signalName(preferredName()); } diff --git a/src/fetch/amazonfetcher.h b/src/fetch/amazonfetcher.h index b228cdda..ba8f831e 100644 --- a/src/fetch/amazonfetcher.h +++ b/src/fetch/amazonfetcher.h @@ -1,180 +1,185 @@ /*************************************************************************** - Copyright (C) 2004-2009 Robby Stephenson + Copyright (C) 2004-2020 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef AMAZONFETCHER_H #define AMAZONFETCHER_H #include "fetcher.h" #include "configwidget.h" #include "../datavectors.h" #include #include #include class QLineEdit; class QCheckBox; class QLabel; class KJob; namespace KIO { class StoredTransferJob; } +class AmazonFetcherTest; namespace Tellico { - class XSLTHandler; namespace GUI { class ComboBox; } namespace Fetch { /** * A fetcher for Amazon.com. * * @author Robby Stephenson */ class AmazonFetcher : public Fetcher { Q_OBJECT -public: - enum Site { - Unknown = -1, - US = 0, - UK = 1, - DE = 2, - JP = 3, - FR = 4, - CA = 5, - CN = 6, - ES = 7, - IT = 8, - BR = 9, - AU = 10, - IN = 11, - MX = 12, - TR = 13, - XX = 14 - }; - - enum ImageSize { - SmallImage=0, - MediumImage=1, - LargeImage=2, - NoImage=3 - }; +friend class ::AmazonFetcherTest; +public: AmazonFetcher(QObject* parent); virtual ~AmazonFetcher(); virtual QString source() const Q_DECL_OVERRIDE; virtual QString attribution() const Q_DECL_OVERRIDE; virtual bool isSearching() const Q_DECL_OVERRIDE { return m_started; } virtual void continueSearch() Q_DECL_OVERRIDE; // amazon can search title, person, isbn, or keyword. No Raw for now. virtual bool canSearch(FetchKey k) const Q_DECL_OVERRIDE; virtual void stop() Q_DECL_OVERRIDE; virtual Data::EntryPtr fetchEntryHook(uint uid) Q_DECL_OVERRIDE; virtual Type type() const Q_DECL_OVERRIDE { return Amazon; } virtual bool canFetch(int type) const Q_DECL_OVERRIDE; struct SiteData { QString title; - QUrl url; + QByteArray host; + QByteArray region; QString country; QString countryName; }; static const SiteData& siteData(int site); /** * Returns a widget for modifying the fetcher's config. */ virtual Fetch::ConfigWidget* configWidget(QWidget* parent) const Q_DECL_OVERRIDE; class ConfigWidget; friend class ConfigWidget; static QString defaultName(); static QString defaultIcon(); static StringHash allOptionalFields(); private Q_SLOTS: void slotComplete(KJob* job); private: virtual void search() Q_DECL_OVERRIDE; virtual FetchRequest updateRequest(Data::EntryPtr entry) Q_DECL_OVERRIDE; virtual void readConfigHook(const KConfigGroup& config) Q_DECL_OVERRIDE; - void initXSLTHandler(); void doSearch(); + QByteArray requestPayload(FetchRequest request); + Data::CollPtr createCollection(); + void populateEntry(Data::EntryPtr entry, const QJsonObject& info); void parseTitle(Data::EntryPtr entry); bool parseTitleToken(Data::EntryPtr entry, const QString& token); - QString secretKey() const; - XSLTHandler* m_xsltHandler; + enum Site { + Unknown = -1, + US = 0, + UK = 1, + DE = 2, + JP = 3, + FR = 4, + CA = 5, + CN = 6, + ES = 7, + IT = 8, + BR = 9, + AU = 10, + IN = 11, + MX = 12, + TR = 13, + SG = 14, + AE = 15, + XX = 16 + }; + + enum ImageSize { + SmallImage=0, + MediumImage=1, + LargeImage=2, + NoImage=3 + }; + Site m_site; ImageSize m_imageSize; - QString m_access; QString m_assoc; - // mutable so that secretKey() can be const - mutable QByteArray m_amazonKey; - bool m_addLinkField; + QString m_accessKey; + QString m_secretKey; int m_limit; int m_countOffset; int m_page; int m_total; int m_numResults; QHash m_entries; // they get modified after collection is created, so can't be const QPointer m_job; bool m_started; + QString m_testResultsFile; + bool m_enableLog; }; class AmazonFetcher::ConfigWidget : public Fetch::ConfigWidget { Q_OBJECT public: explicit ConfigWidget(QWidget* parent_, const AmazonFetcher* fetcher = nullptr); virtual void saveConfigHook(KConfigGroup& config) Q_DECL_OVERRIDE; virtual QString preferredName() const Q_DECL_OVERRIDE; private Q_SLOTS: void slotSiteChanged(); private: QLineEdit* m_accessEdit; QLineEdit* m_secretKeyEdit; QLineEdit* m_assocEdit; GUI::ComboBox* m_siteCombo; GUI::ComboBox* m_imageCombo; }; } // end namespace } // end namespace #endif diff --git a/src/fetch/amazonrequest.cpp b/src/fetch/amazonrequest.cpp index 7594a684..258d53d6 100644 --- a/src/fetch/amazonrequest.cpp +++ b/src/fetch/amazonrequest.cpp @@ -1,69 +1,156 @@ /*************************************************************************** - Copyright (C) 2007-2009 Robby Stephenson + Copyright (C) 2007-2020 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "amazonrequest.h" -#include "hmac_sha2.h" +#include "../tellico_debug.h" #include +#include +#include + +namespace { + static const char* AMAZON_REQUEST = "aws4_request"; + static const char* AMAZON_REQUEST_ALGORITHM = "AWS4-HMAC-SHA256"; + static const char* AMAZON_REQUEST_NAMESPACE = "com.amazon.paapi5"; + static const char* AMAZON_REQUEST_VERSION = "v1"; + static const char* AMAZON_REQUEST_SERVICE = "ProductAdvertisingAPI"; +} using Tellico::Fetch::AmazonRequest; -AmazonRequest::AmazonRequest(const QUrl& site_, const QByteArray& key_) : m_siteUrl(site_), m_key(key_) { +AmazonRequest::AmazonRequest(const QString& accessKey_, const QString& secretKey_) + : m_accessKey(accessKey_.toUtf8()) + , m_secretKey(secretKey_.toUtf8()) + , m_method("POST") + , m_service(AMAZON_REQUEST_SERVICE) + , m_operation(SearchItems) { + m_amzDate = QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd'T'hhmmss'Z'")).toUtf8(); +} + +void AmazonRequest::setHost(const QByteArray& host_) { + m_host = host_; } -QUrl AmazonRequest::signedRequest(const QMap& params_) const { - QMap allParams = params_; - allParams.insert(QStringLiteral("Timestamp"), - QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyy-MM-dd'T'hh:mm:ss'Z'"))); +void AmazonRequest::setRegion(const QByteArray& region_) { + m_region = region_; +} + +void AmazonRequest::setOperation(int op_) { + m_operation = static_cast(op_); +} - QByteArray query; - // has to be a map so that the query elements are sorted - QMapIterator i(allParams); +QMap AmazonRequest::headers(const QByteArray& payload_) { + m_headers.insert("content-encoding", "amz-1.0"); + m_headers.insert("content-type", "application/json; charset=utf-8"); + m_headers.insert("host", m_host); + m_headers.insert("x-amz-date", m_amzDate); + m_headers.insert("x-amz-target", targetOperation()); + + const QByteArray canonicalURL = prepareCanonicalRequest(payload_); + const QByteArray stringToSign = prepareStringToSign(canonicalURL); + const QByteArray signature = calculateSignature(stringToSign); + m_headers.insert("Authorization", buildAuthorizationString(signature)); + return m_headers; +} + +QByteArray AmazonRequest::prepareCanonicalRequest(const QByteArray& payload_) const { + QByteArray req = m_method + '\n' + + m_path + '\n' + '\n'; + + m_signedHeaders.clear(); + // a map so that the query elements are sorted + QMapIterator i(m_headers); while(i.hasNext()) { i.next(); - query.append(i.key().toUtf8().toPercentEncoding()); - query.append('='); - query.append(i.value().toUtf8().toPercentEncoding()); - if(i.hasNext()) { - query.append('&'); - } + m_signedHeaders.append(i.key()); + m_signedHeaders.append(';'); + + req.append(i.key()); + req.append(':'); + req.append(i.value()); + req.append('\n'); } + req.append('\n'); + + m_signedHeaders.chop(1); // remove final ';' + req.append(m_signedHeaders); + req.append('\n'); - const QByteArray toSign = "GET\n" - + m_siteUrl.host().toUtf8() + '\n' - + m_siteUrl.path().toUtf8() + '\n' - + query; - - QByteArray hmac_buffer(SHA256_DIGEST_SIZE, '\0'); - hmac_sha256(reinterpret_cast(const_cast(m_key.data())), m_key.length(), - reinterpret_cast(const_cast(toSign.data())), toSign.length(), - reinterpret_cast(hmac_buffer.data()), hmac_buffer.length()); - const QByteArray sig = hmac_buffer.toBase64().toPercentEncoding(); -// myDebug() << sig; - - QUrl url = m_siteUrl; - url.setQuery(QString::fromLocal8Bit(query + "&Signature=" + sig)); - return url; +// myDebug() << req; + + req.append(toHexHash(payload_)); + return req; +} + +QByteArray AmazonRequest::prepareStringToSign(const QByteArray& canonicalUrl_) const { + QByteArray stringToSign(AMAZON_REQUEST_ALGORITHM); + stringToSign += '\n'; + stringToSign += m_amzDate + '\n'; + stringToSign += m_amzDate.left(8) + '/' + m_region + '/' + m_service + '/' + AMAZON_REQUEST + '\n'; + stringToSign += toHexHash(canonicalUrl_); + return stringToSign; +} + +QByteArray AmazonRequest::calculateSignature(const QByteArray& stringToSign_) const { + QByteArray signatureKey; + signatureKey = QMessageAuthenticationCode::hash(m_amzDate.left(8), "AWS4" + m_secretKey, QCryptographicHash::Sha256); + signatureKey = QMessageAuthenticationCode::hash(m_region, signatureKey, QCryptographicHash::Sha256); + signatureKey = QMessageAuthenticationCode::hash(m_service, signatureKey, QCryptographicHash::Sha256); + signatureKey = QMessageAuthenticationCode::hash(AMAZON_REQUEST, signatureKey, QCryptographicHash::Sha256); + return QMessageAuthenticationCode::hash(stringToSign_, signatureKey, QCryptographicHash::Sha256).toHex(0); +} + +QByteArray AmazonRequest::buildAuthorizationString(const QByteArray& signature_) const { + QByteArray authString(AMAZON_REQUEST_ALGORITHM); + authString += ' '; + authString += "Credential=" + m_accessKey; + authString += '/' + m_amzDate.left(8) + '/' + m_region + '/' + m_service + "/" + AMAZON_REQUEST + ", "; + authString += "SignedHeaders=" + m_signedHeaders + ", " + "Signature=" + signature_; + return authString; +} + +QByteArray AmazonRequest::toHexHash(const QByteArray& data_) const { + const QByteArray hash = QCryptographicHash::hash(data_, QCryptographicHash::Sha256); + return hash.toHex(0); // '0' says no separators between hex encoded characters +} + +QByteArray AmazonRequest::targetOperation() const { + QByteArray target(AMAZON_REQUEST_NAMESPACE); + target.append('.'); + target.append(AMAZON_REQUEST_VERSION); + target.append('.'); + target.append(m_service); + target.append(AMAZON_REQUEST_VERSION); + target.append('.'); + switch(m_operation) { + case SearchItems: + target.append("SearchItems"); + break; + case GetItems: + target.append("GetItems"); + break; + } + return target; } diff --git a/src/fetch/amazonrequest.h b/src/fetch/amazonrequest.h index aa6013a6..ecdddfe3 100644 --- a/src/fetch/amazonrequest.h +++ b/src/fetch/amazonrequest.h @@ -1,52 +1,80 @@ /*************************************************************************** - Copyright (C) 2007-2009 Robby Stephenson + Copyright (C) 2007-2020 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef TELLICO_FETCH_AMAZONREQUEST_H #define TELLICO_FETCH_AMAZONREQUEST_H #include - #include +class AmazonFetcherTest; + namespace Tellico { namespace Fetch { /** * @author Robby Stephenson */ class AmazonRequest { + +friend class ::AmazonFetcherTest; + public: - AmazonRequest(const QUrl& site, const QByteArray& key); + enum Operation { + SearchItems, + GetItems + }; + + AmazonRequest(const QString& accessKey_, const QString& secretKey_); - QUrl signedRequest(const QMap& params) const; + void setHost(const QByteArray& host); + void setRegion(const QByteArray& region); + void setOperation(int op); + QMap headers(const QByteArray& payload); private: - QUrl m_siteUrl; - QByteArray m_key; + QByteArray prepareCanonicalRequest(const QByteArray& payload) const; + QByteArray prepareStringToSign(const QByteArray& canonicalUrl) const; + QByteArray calculateSignature(const QByteArray& stringToSign) const; + QByteArray buildAuthorizationString(const QByteArray& signature) const; + QByteArray toHexHash(const QByteArray& data) const; + QByteArray targetOperation() const; + + QMap m_headers; + QByteArray m_accessKey; + QByteArray m_secretKey; + QByteArray m_method; + QByteArray m_service; + QByteArray m_host; + QByteArray m_region; + QByteArray m_path; + QByteArray m_amzDate; + Operation m_operation; + mutable QByteArray m_signedHeaders; }; } // end Fetch namespace } // end Tellico namespace #endif diff --git a/src/fetch/hmac_sha2.c b/src/fetch/hmac_sha2.c deleted file mode 100644 index 63ccdefb..00000000 --- a/src/fetch/hmac_sha2.c +++ /dev/null @@ -1,544 +0,0 @@ -/*- - * HMAC-SHA-224/256/384/512 implementation - * Last update: 06/15/2005 - * Issue date: 06/15/2005 - * - * Copyright (C) 2005 Olivier Gay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include - -#include "hmac_sha2.h" - -/* HMAC-SHA-224 functions */ - -void hmac_sha224_init(hmac_sha224_ctx *ctx, unsigned char *key, - unsigned int key_size) -{ - unsigned int fill; - unsigned int num; - - unsigned char *key_used; - unsigned char key_temp[SHA224_DIGEST_SIZE]; - unsigned int i; - - if (key_size == SHA224_BLOCK_SIZE) { - key_used = key; - num = SHA224_BLOCK_SIZE; - } else { - if (key_size > SHA224_BLOCK_SIZE){ - key_used = key_temp; - num = SHA224_DIGEST_SIZE; - sha224(key, key_size, key_used); - } else { /* key_size > SHA224_BLOCK_SIZE */ - key_used = key; - num = key_size; - } - fill = SHA224_BLOCK_SIZE - num; - - memset(ctx->block_ipad + num, 0x36, fill); - memset(ctx->block_opad + num, 0x5c, fill); - } - - for (i = 0; i < num; i++) { - ctx->block_ipad[i] = key_used[i] ^ 0x36; - ctx->block_opad[i] = key_used[i] ^ 0x5c; - } - - sha224_init(&ctx->ctx_inside); - sha224_update(&ctx->ctx_inside, ctx->block_ipad, SHA224_BLOCK_SIZE); - - sha224_init(&ctx->ctx_outside); - sha224_update(&ctx->ctx_outside, ctx->block_opad, - SHA224_BLOCK_SIZE); - - /* for hmac_reinit */ - memcpy(&ctx->ctx_inside_reinit, &ctx->ctx_inside, - sizeof(sha224_ctx)); - memcpy(&ctx->ctx_outside_reinit, &ctx->ctx_outside, - sizeof(sha224_ctx)); -} - -void hmac_sha224_reinit(hmac_sha224_ctx *ctx) -{ - memcpy(&ctx->ctx_inside, &ctx->ctx_inside_reinit, - sizeof(sha224_ctx)); - memcpy(&ctx->ctx_outside, &ctx->ctx_outside_reinit, - sizeof(sha224_ctx)); -} - -void hmac_sha224_update(hmac_sha224_ctx *ctx, unsigned char *message, - unsigned int message_len) -{ - sha224_update(&ctx->ctx_inside, message, message_len); -} - -void hmac_sha224_final(hmac_sha224_ctx *ctx, unsigned char *mac, - unsigned int mac_size) -{ - unsigned char digest_inside[SHA224_DIGEST_SIZE]; - unsigned char mac_temp[SHA224_DIGEST_SIZE]; - - sha224_final(&ctx->ctx_inside, digest_inside); - sha224_update(&ctx->ctx_outside, digest_inside, SHA224_DIGEST_SIZE); - sha224_final(&ctx->ctx_outside, mac_temp); - memcpy(mac, mac_temp, mac_size); -} - -void hmac_sha224(unsigned char *key, unsigned int key_size, - unsigned char *message, unsigned int message_len, - unsigned char *mac, unsigned mac_size) -{ - hmac_sha224_ctx ctx; - - hmac_sha224_init(&ctx, key, key_size); - hmac_sha224_update(&ctx, message, message_len); - hmac_sha224_final(&ctx, mac, mac_size); -} - -/* HMAC-SHA-256 functions */ - -void hmac_sha256_init(hmac_sha256_ctx *ctx, unsigned char *key, - unsigned int key_size) -{ - unsigned int fill; - unsigned int num; - - unsigned char *key_used; - unsigned char key_temp[SHA256_DIGEST_SIZE]; - unsigned int i; - - if (key_size == SHA256_BLOCK_SIZE) { - key_used = key; - num = SHA256_BLOCK_SIZE; - } else { - if (key_size > SHA256_BLOCK_SIZE){ - key_used = key_temp; - num = SHA256_DIGEST_SIZE; - sha256(key, key_size, key_used); - } else { /* key_size > SHA256_BLOCK_SIZE */ - key_used = key; - num = key_size; - } - fill = SHA256_BLOCK_SIZE - num; - - memset(ctx->block_ipad + num, 0x36, fill); - memset(ctx->block_opad + num, 0x5c, fill); - } - - for (i = 0; i < num; i++) { - ctx->block_ipad[i] = key_used[i] ^ 0x36; - ctx->block_opad[i] = key_used[i] ^ 0x5c; - } - - sha256_init(&ctx->ctx_inside); - sha256_update(&ctx->ctx_inside, ctx->block_ipad, SHA256_BLOCK_SIZE); - - sha256_init(&ctx->ctx_outside); - sha256_update(&ctx->ctx_outside, ctx->block_opad, - SHA256_BLOCK_SIZE); - - /* for hmac_reinit */ - memcpy(&ctx->ctx_inside_reinit, &ctx->ctx_inside, - sizeof(sha256_ctx)); - memcpy(&ctx->ctx_outside_reinit, &ctx->ctx_outside, - sizeof(sha256_ctx)); -} - -void hmac_sha256_reinit(hmac_sha256_ctx *ctx) -{ - memcpy(&ctx->ctx_inside, &ctx->ctx_inside_reinit, - sizeof(sha256_ctx)); - memcpy(&ctx->ctx_outside, &ctx->ctx_outside_reinit, - sizeof(sha256_ctx)); -} - -void hmac_sha256_update(hmac_sha256_ctx *ctx, unsigned char *message, - unsigned int message_len) -{ - sha256_update(&ctx->ctx_inside, message, message_len); -} - -void hmac_sha256_final(hmac_sha256_ctx *ctx, unsigned char *mac, - unsigned int mac_size) -{ - unsigned char digest_inside[SHA256_DIGEST_SIZE]; - unsigned char mac_temp[SHA256_DIGEST_SIZE]; - - sha256_final(&ctx->ctx_inside, digest_inside); - sha256_update(&ctx->ctx_outside, digest_inside, SHA256_DIGEST_SIZE); - sha256_final(&ctx->ctx_outside, mac_temp); - memcpy(mac, mac_temp, mac_size); -} - -void hmac_sha256(unsigned char *key, unsigned int key_size, - unsigned char *message, unsigned int message_len, - unsigned char *mac, unsigned mac_size) -{ - hmac_sha256_ctx ctx; - - hmac_sha256_init(&ctx, key, key_size); - hmac_sha256_update(&ctx, message, message_len); - hmac_sha256_final(&ctx, mac, mac_size); -} - -/* HMAC-SHA-384 functions */ - -void hmac_sha384_init(hmac_sha384_ctx *ctx, unsigned char *key, - unsigned int key_size) -{ - unsigned int fill; - unsigned int num; - - unsigned char *key_used; - unsigned char key_temp[SHA384_DIGEST_SIZE]; - unsigned int i; - - if (key_size == SHA384_BLOCK_SIZE) { - key_used = key; - num = SHA384_BLOCK_SIZE; - } else { - if (key_size > SHA384_BLOCK_SIZE){ - key_used = key_temp; - num = SHA384_DIGEST_SIZE; - sha384(key, key_size, key_used); - } else { /* key_size > SHA384_BLOCK_SIZE */ - key_used = key; - num = key_size; - } - fill = SHA384_BLOCK_SIZE - num; - - memset(ctx->block_ipad + num, 0x36, fill); - memset(ctx->block_opad + num, 0x5c, fill); - } - - for (i = 0; i < num; i++) { - ctx->block_ipad[i] = key_used[i] ^ 0x36; - ctx->block_opad[i] = key_used[i] ^ 0x5c; - } - - sha384_init(&ctx->ctx_inside); - sha384_update(&ctx->ctx_inside, ctx->block_ipad, SHA384_BLOCK_SIZE); - - sha384_init(&ctx->ctx_outside); - sha384_update(&ctx->ctx_outside, ctx->block_opad, - SHA384_BLOCK_SIZE); - - /* for hmac_reinit */ - memcpy(&ctx->ctx_inside_reinit, &ctx->ctx_inside, - sizeof(sha384_ctx)); - memcpy(&ctx->ctx_outside_reinit, &ctx->ctx_outside, - sizeof(sha384_ctx)); -} - -void hmac_sha384_reinit(hmac_sha384_ctx *ctx) -{ - memcpy(&ctx->ctx_inside, &ctx->ctx_inside_reinit, - sizeof(sha384_ctx)); - memcpy(&ctx->ctx_outside, &ctx->ctx_outside_reinit, - sizeof(sha384_ctx)); -} - -void hmac_sha384_update(hmac_sha384_ctx *ctx, unsigned char *message, - unsigned int message_len) -{ - sha384_update(&ctx->ctx_inside, message, message_len); -} - -void hmac_sha384_final(hmac_sha384_ctx *ctx, unsigned char *mac, - unsigned int mac_size) -{ - unsigned char digest_inside[SHA384_DIGEST_SIZE]; - unsigned char mac_temp[SHA384_DIGEST_SIZE]; - - sha384_final(&ctx->ctx_inside, digest_inside); - sha384_update(&ctx->ctx_outside, digest_inside, SHA384_DIGEST_SIZE); - sha384_final(&ctx->ctx_outside, mac_temp); - memcpy(mac, mac_temp, mac_size); -} - -void hmac_sha384(unsigned char *key, unsigned int key_size, - unsigned char *message, unsigned int message_len, - unsigned char *mac, unsigned mac_size) -{ - hmac_sha384_ctx ctx; - - hmac_sha384_init(&ctx, key, key_size); - hmac_sha384_update(&ctx, message, message_len); - hmac_sha384_final(&ctx, mac, mac_size); -} - -/* HMAC-SHA-512 functions */ - -void hmac_sha512_init(hmac_sha512_ctx *ctx, unsigned char *key, - unsigned int key_size) -{ - unsigned int fill; - unsigned int num; - - unsigned char *key_used; - unsigned char key_temp[SHA512_DIGEST_SIZE]; - unsigned int i; - - if (key_size == SHA512_BLOCK_SIZE) { - key_used = key; - num = SHA512_BLOCK_SIZE; - } else { - if (key_size > SHA512_BLOCK_SIZE){ - key_used = key_temp; - num = SHA512_DIGEST_SIZE; - sha512(key, key_size, key_used); - } else { /* key_size > SHA512_BLOCK_SIZE */ - key_used = key; - num = key_size; - } - fill = SHA512_BLOCK_SIZE - num; - - memset(ctx->block_ipad + num, 0x36, fill); - memset(ctx->block_opad + num, 0x5c, fill); - } - - for (i = 0; i < num; i++) { - ctx->block_ipad[i] = key_used[i] ^ 0x36; - ctx->block_opad[i] = key_used[i] ^ 0x5c; - } - - sha512_init(&ctx->ctx_inside); - sha512_update(&ctx->ctx_inside, ctx->block_ipad, SHA512_BLOCK_SIZE); - - sha512_init(&ctx->ctx_outside); - sha512_update(&ctx->ctx_outside, ctx->block_opad, - SHA512_BLOCK_SIZE); - - /* for hmac_reinit */ - memcpy(&ctx->ctx_inside_reinit, &ctx->ctx_inside, - sizeof(sha512_ctx)); - memcpy(&ctx->ctx_outside_reinit, &ctx->ctx_outside, - sizeof(sha512_ctx)); -} - -void hmac_sha512_reinit(hmac_sha512_ctx *ctx) -{ - memcpy(&ctx->ctx_inside, &ctx->ctx_inside_reinit, - sizeof(sha512_ctx)); - memcpy(&ctx->ctx_outside, &ctx->ctx_outside_reinit, - sizeof(sha512_ctx)); -} - -void hmac_sha512_update(hmac_sha512_ctx *ctx, unsigned char *message, - unsigned int message_len) -{ - sha512_update(&ctx->ctx_inside, message, message_len); -} - -void hmac_sha512_final(hmac_sha512_ctx *ctx, unsigned char *mac, - unsigned int mac_size) -{ - unsigned char digest_inside[SHA512_DIGEST_SIZE]; - unsigned char mac_temp[SHA512_DIGEST_SIZE]; - - sha512_final(&ctx->ctx_inside, digest_inside); - sha512_update(&ctx->ctx_outside, digest_inside, SHA512_DIGEST_SIZE); - sha512_final(&ctx->ctx_outside, mac_temp); - memcpy(mac, mac_temp, mac_size); -} - -void hmac_sha512(unsigned char *key, unsigned int key_size, - unsigned char *message, unsigned int message_len, - unsigned char *mac, unsigned mac_size) -{ - hmac_sha512_ctx ctx; - - hmac_sha512_init(&ctx, key, key_size); - hmac_sha512_update(&ctx, message, message_len); - hmac_sha512_final(&ctx, mac, mac_size); -} - -#ifdef TEST_VECTORS - -/* IETF Validation tests */ - -#include -#include - -void test(unsigned char *vector, unsigned char *digest, - unsigned int digest_size) -{ - unsigned char output[2 * SHA512_DIGEST_SIZE + 1]; - unsigned int i; - - output[2 * digest_size] = '\0'; - - for (i = 0; i < digest_size ; i++) { - sprintf((char *) output + 2*i, "%02x", digest[i]); - } - - printf("H: %s\n", output); - if (strcmp((char *) vector, (char *) output)) { - fprintf(stderr, "Test failed.\n"); - exit(1); - } -} - -int main() -{ - static unsigned char *vectors[] = - { - /* HMAC-SHA-224 */ - "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22", - "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44", - "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea", - "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a", - "0e2aea68a90c8d37c988bcdb9fca6fa8", - "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e", - "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1", - /* HMAC-SHA-256 */ - "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", - "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", - "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", - "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b", - "a3b6167473100ee06e0c796c2955552b", - "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54", - "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2", - /* HMAC-SHA-384 */ - "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59c" - "faea9ea9076ede7f4af152e8b2fa9cb6", - "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e" - "8e2240ca5e69e2c78b3239ecfab21649", - "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b" - "2a5ab39dc13814b94e3ab6e101a34f27", - "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e" - "6801dd23c4a7d679ccf8a386c674cffb", - "3abf34c3503b2a23a46efc619baef897", - "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c6" - "0c2ef6ab4030fe8296248df163f44952", - "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5" - "a678cc31e799176d3860e6110c46523e", - /* HMAC-SHA-512 */ - "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde" - "daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854", - "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554" - "9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737", - "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39" - "bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb", - "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db" - "a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd", - "415fad6271580a531d4179bc891d87a6", - "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352" - "6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598", - "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944" - "b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58" - }; - - static unsigned char *messages[] = - { - "Hi There", - "what do ya want for nothing?", - NULL, - NULL, - "Test With Truncation", - "Test Using Larger Than Block-Size Key - Hash Key First", - "This is a test using a larger than block-size key " - "and a larger than block-size data. The key needs" - " to be hashed before being used by the HMAC algorithm." - }; - - unsigned char mac[SHA512_DIGEST_SIZE]; - unsigned char *keys[7]; - unsigned int keys_len[7] = {20, 4, 20, 25, 20, 131, 131}; - unsigned int messages2and3_len = 50; - unsigned int mac_224_size, mac_256_size, mac_384_size, mac_512_size; - unsigned int i; - - for (i = 0; i < 7; i++) { - keys[i] = malloc(keys_len[i]); - if (keys[i] == NULL) { - fprintf(stderr, "Can't allocate memory\n"); - return 1; - } - } - - memset(keys[0], 0x0b, keys_len[0]); - strcpy(keys[1], "Jefe"); - memset(keys[2], 0xaa, keys_len[2]); - for (i = 0; i < keys_len[3]; i++) - keys[3][i] = (unsigned char) i + 1; - memset(keys[4], 0x0c, keys_len[4]); - memset(keys[5], 0xaa, keys_len[5]); - memset(keys[6], 0xaa, keys_len[6]); - - messages[2] = malloc(messages2and3_len + 1); - messages[3] = malloc(messages2and3_len + 1); - - if (messages[2] == NULL || messages[3] == NULL) { - fprintf(stderr, "Can't allocate memory\n"); - return 1; - } - - messages[2][messages2and3_len] = '\0'; - messages[3][messages2and3_len] = '\0'; - - memset(messages[2], 0xdd, messages2and3_len); - memset(messages[3], 0xcd, messages2and3_len); - - printf("HMAC-SHA-2 IETF Validation tests\n\n"); - - for (i = 0; i < 7; i++) { - if (i != 4) { - mac_224_size = SHA224_DIGEST_SIZE; - mac_256_size = SHA256_DIGEST_SIZE; - mac_384_size = SHA384_DIGEST_SIZE; - mac_512_size = SHA512_DIGEST_SIZE; - } else { - mac_224_size = 128 / 8; mac_256_size = 128 / 8; - mac_384_size = 128 / 8; mac_512_size = 128 / 8; - } - - printf("Test %d:\n", i + 1); - - hmac_sha224(keys[i], keys_len[i], messages[i], strlen(messages[i]), - mac, mac_224_size); - test(vectors[i], mac, mac_224_size ); - hmac_sha256(keys[i], keys_len[i], messages[i], strlen(messages[i]), - mac, mac_256_size); - test(vectors[7 + i], mac, mac_256_size); - hmac_sha384(keys[i], keys_len[i], messages[i], strlen(messages[i]), - mac, mac_384_size); - test(vectors[14 + i], mac, mac_384_size); - hmac_sha512(keys[i], keys_len[i], messages[i], strlen(messages[i]), - mac, mac_512_size); - test(vectors[21 + i], mac, mac_512_size); - } - - printf("All tests passed.\n"); - - return 0; -} - -#endif /* TEST_VECTORS */ - diff --git a/src/fetch/hmac_sha2.h b/src/fetch/hmac_sha2.h deleted file mode 100644 index 2c9bcfaa..00000000 --- a/src/fetch/hmac_sha2.h +++ /dev/null @@ -1,139 +0,0 @@ -/*- - * HMAC-SHA-224/256/384/512 implementation - * Last update: 06/15/2005 - * Issue date: 06/15/2005 - * - * Copyright (C) 2005 Olivier Gay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _HMAC_SHA2_H -#define _HMAC_SHA2_H - -#include "sha2.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - sha224_ctx ctx_inside; - sha224_ctx ctx_outside; - - /* for hmac_reinit */ - sha224_ctx ctx_inside_reinit; - sha224_ctx ctx_outside_reinit; - - unsigned char block_ipad[SHA224_BLOCK_SIZE]; - unsigned char block_opad[SHA224_BLOCK_SIZE]; -} hmac_sha224_ctx; - -typedef struct { - sha256_ctx ctx_inside; - sha256_ctx ctx_outside; - - /* for hmac_reinit */ - sha256_ctx ctx_inside_reinit; - sha256_ctx ctx_outside_reinit; - - unsigned char block_ipad[SHA256_BLOCK_SIZE]; - unsigned char block_opad[SHA256_BLOCK_SIZE]; -} hmac_sha256_ctx; - -typedef struct { - sha384_ctx ctx_inside; - sha384_ctx ctx_outside; - - /* for hmac_reinit */ - sha384_ctx ctx_inside_reinit; - sha384_ctx ctx_outside_reinit; - - unsigned char block_ipad[SHA384_BLOCK_SIZE]; - unsigned char block_opad[SHA384_BLOCK_SIZE]; -} hmac_sha384_ctx; - -typedef struct { - sha512_ctx ctx_inside; - sha512_ctx ctx_outside; - - /* for hmac_reinit */ - sha512_ctx ctx_inside_reinit; - sha512_ctx ctx_outside_reinit; - - unsigned char block_ipad[SHA512_BLOCK_SIZE]; - unsigned char block_opad[SHA512_BLOCK_SIZE]; -} hmac_sha512_ctx; - -void hmac_sha224_init(hmac_sha224_ctx *ctx, unsigned char *key, - unsigned int key_size); -void hmac_sha224_reinit(hmac_sha224_ctx *ctx); -void hmac_sha224_update(hmac_sha224_ctx *ctx, unsigned char *message, - unsigned int message_len); -void hmac_sha224_final(hmac_sha224_ctx *ctx, unsigned char *mac, - unsigned int mac_size); -void hmac_sha224(unsigned char *key, unsigned int key_size, - unsigned char *message, unsigned int message_len, - unsigned char *mac, unsigned mac_size); - -void hmac_sha256_init(hmac_sha256_ctx *ctx, unsigned char *key, - unsigned int key_size); -void hmac_sha256_reinit(hmac_sha256_ctx *ctx); -void hmac_sha256_update(hmac_sha256_ctx *ctx, unsigned char *message, - unsigned int message_len); -void hmac_sha256_final(hmac_sha256_ctx *ctx, unsigned char *mac, - unsigned int mac_size); -void hmac_sha256(unsigned char *key, unsigned int key_size, - unsigned char *message, unsigned int message_len, - unsigned char *mac, unsigned mac_size); - -void hmac_sha384_init(hmac_sha384_ctx *ctx, unsigned char *key, - unsigned int key_size); -void hmac_sha384_reinit(hmac_sha384_ctx *ctx); -void hmac_sha384_update(hmac_sha384_ctx *ctx, unsigned char *message, - unsigned int message_len); -void hmac_sha384_final(hmac_sha384_ctx *ctx, unsigned char *mac, - unsigned int mac_size); -void hmac_sha384(unsigned char *key, unsigned int key_size, - unsigned char *message, unsigned int message_len, - unsigned char *mac, unsigned mac_size); - -void hmac_sha512_init(hmac_sha512_ctx *ctx, unsigned char *key, - unsigned int key_size); -void hmac_sha512_reinit(hmac_sha512_ctx *ctx); -void hmac_sha512_update(hmac_sha512_ctx *ctx, unsigned char *message, - unsigned int message_len); -void hmac_sha512_final(hmac_sha512_ctx *ctx, unsigned char *mac, - unsigned int mac_size); -void hmac_sha512(unsigned char *key, unsigned int key_size, - unsigned char *message, unsigned int message_len, - unsigned char *mac, unsigned mac_size); - -#ifdef __cplusplus -} -#endif - -#endif /* ! _HMAC_SHA2_H */ diff --git a/src/fetch/messagehandler.cpp b/src/fetch/messagehandler.cpp index b67fc255..50e0eda0 100644 --- a/src/fetch/messagehandler.cpp +++ b/src/fetch/messagehandler.cpp @@ -1,52 +1,52 @@ /*************************************************************************** Copyright (C) 2005-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "messagehandler.h" #include "fetchmanager.h" #include "../utils/guiproxy.h" #include "../utils/cursorsaver.h" #include using Tellico::Fetch::ManagerMessage; -// all messages go to manager +// default: all messages go to manager void ManagerMessage::send(const QString& message_, Type type_) { GUI::CursorSaver cs(Qt::ArrowCursor); // plus errors get a message box if(type_ == Error) { KMessageBox::sorry(GUI::Proxy::widget(), message_); // QString(), // caption // KMessageBox::Options(KMessageBox::Notify | KMessageBox::AllowLink)); } else if(type_ == Warning) { KMessageBox::information(GUI::Proxy::widget(), message_); } else { Fetch::Manager::self()->updateStatus(message_); } } void ManagerMessage::infoList(const QString& message_, const QStringList& list_) { GUI::CursorSaver cs(Qt::ArrowCursor); KMessageBox::informationList(GUI::Proxy::widget(), message_, list_); } diff --git a/src/fetch/amazonrequest.h b/src/fetch/messagelogger.cpp similarity index 75% copy from src/fetch/amazonrequest.h copy to src/fetch/messagelogger.cpp index aa6013a6..ef896d0a 100644 --- a/src/fetch/amazonrequest.h +++ b/src/fetch/messagelogger.cpp @@ -1,52 +1,42 @@ /*************************************************************************** - Copyright (C) 2007-2009 Robby Stephenson + Copyright (C) 2020 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ -#ifndef TELLICO_FETCH_AMAZONREQUEST_H -#define TELLICO_FETCH_AMAZONREQUEST_H +#include "messagelogger.h" -#include +using Tellico::Fetch::MessageLogger; -#include +void MessageLogger::send(const QString& message_, Type type_) { + if(type_ == Error) { + errorList += message_; + } else if(type_ == Warning) { + warningList += message_; + } else { + statusList += message_; + } +} -namespace Tellico { - namespace Fetch { - -/** - * @author Robby Stephenson - */ -class AmazonRequest { -public: - AmazonRequest(const QUrl& site, const QByteArray& key); - - QUrl signedRequest(const QMap& params) const; - -private: - QUrl m_siteUrl; - QByteArray m_key; -}; - - } // end Fetch namespace -} // end Tellico namespace - -#endif +void MessageLogger::infoList(const QString& message_, const QStringList& list_) { + Q_UNUSED(message_); + Q_UNUSED(list_); +} diff --git a/src/fetch/amazonrequest.h b/src/fetch/messagelogger.h similarity index 74% copy from src/fetch/amazonrequest.h copy to src/fetch/messagelogger.h index aa6013a6..64b60f75 100644 --- a/src/fetch/amazonrequest.h +++ b/src/fetch/messagelogger.h @@ -1,52 +1,55 @@ /*************************************************************************** - Copyright (C) 2007-2009 Robby Stephenson + Copyright (C) 2020 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ -#ifndef TELLICO_FETCH_AMAZONREQUEST_H -#define TELLICO_FETCH_AMAZONREQUEST_H +#ifndef TELLICO_FETCH_MESSAGELOGGER_H +#define TELLICO_FETCH_MESSAGELOGGER_H -#include +#include "messagehandler.h" -#include +#include +#include namespace Tellico { namespace Fetch { /** * @author Robby Stephenson */ -class AmazonRequest { +class MessageLogger : public MessageHandler { public: - AmazonRequest(const QUrl& site, const QByteArray& key); + MessageLogger() : MessageHandler() {} + virtual ~MessageLogger() {} - QUrl signedRequest(const QMap& params) const; + virtual void send(const QString& message, Type type) Q_DECL_OVERRIDE; + virtual void infoList(const QString& message, const QStringList& list) Q_DECL_OVERRIDE; -private: - QUrl m_siteUrl; - QByteArray m_key; + QStringList statusList; + QStringList warningList; + QStringList errorList; }; - } // end Fetch namespace -} // end Tellico namespace + } // end namespace +} // end namespace #endif diff --git a/src/fetch/sha2.c b/src/fetch/sha2.c deleted file mode 100644 index a32427ed..00000000 --- a/src/fetch/sha2.c +++ /dev/null @@ -1,950 +0,0 @@ -/* - * FIPS 180-2 SHA-224/256/384/512 implementation - * Last update: 02/02/2007 - * Issue date: 04/30/2005 - * - * Copyright (C) 2005, 2007 Olivier Gay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#define UNROLL_LOOPS /* Enable loops unrolling */ -#endif - -#include - -#include "sha2.h" - -#define SHFR(x, n) (x >> n) -#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) -#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) -#define CH(x, y, z) ((x & y) ^ (~x & z)) -#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) - -#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) -#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) -#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) -#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) - -#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) -#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) -#define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7)) -#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6)) - -#define UNPACK32(x, str) \ -{ \ - *((str) + 3) = (uint8) ((x) ); \ - *((str) + 2) = (uint8) ((x) >> 8); \ - *((str) + 1) = (uint8) ((x) >> 16); \ - *((str) + 0) = (uint8) ((x) >> 24); \ -} - -#define PACK32(str, x) \ -{ \ - *(x) = ((uint32) *((str) + 3) ) \ - | ((uint32) *((str) + 2) << 8) \ - | ((uint32) *((str) + 1) << 16) \ - | ((uint32) *((str) + 0) << 24); \ -} - -#define UNPACK64(x, str) \ -{ \ - *((str) + 7) = (uint8) ((x) ); \ - *((str) + 6) = (uint8) ((x) >> 8); \ - *((str) + 5) = (uint8) ((x) >> 16); \ - *((str) + 4) = (uint8) ((x) >> 24); \ - *((str) + 3) = (uint8) ((x) >> 32); \ - *((str) + 2) = (uint8) ((x) >> 40); \ - *((str) + 1) = (uint8) ((x) >> 48); \ - *((str) + 0) = (uint8) ((x) >> 56); \ -} - -#define PACK64(str, x) \ -{ \ - *(x) = ((uint64) *((str) + 7) ) \ - | ((uint64) *((str) + 6) << 8) \ - | ((uint64) *((str) + 5) << 16) \ - | ((uint64) *((str) + 4) << 24) \ - | ((uint64) *((str) + 3) << 32) \ - | ((uint64) *((str) + 2) << 40) \ - | ((uint64) *((str) + 1) << 48) \ - | ((uint64) *((str) + 0) << 56); \ -} - -/* Macros used for loops unrolling */ - -#define SHA256_SCR(i) \ -{ \ - w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ - + SHA256_F3(w[i - 15]) + w[i - 16]; \ -} - -#define SHA512_SCR(i) \ -{ \ - w[i] = SHA512_F4(w[i - 2]) + w[i - 7] \ - + SHA512_F3(w[i - 15]) + w[i - 16]; \ -} - -#define SHA256_EXP(a, b, c, d, e, f, g, h, j) \ -{ \ - t1 = wv[h] + SHA256_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \ - + sha256_k[j] + w[j]; \ - t2 = SHA256_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ - wv[d] += t1; \ - wv[h] = t1 + t2; \ -} - -#define SHA512_EXP(a, b, c, d, e, f, g ,h, j) \ -{ \ - t1 = wv[h] + SHA512_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \ - + sha512_k[j] + w[j]; \ - t2 = SHA512_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ - wv[d] += t1; \ - wv[h] = t1 + t2; \ -} - -uint32 sha224_h0[8] = - {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, - 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4}; - -uint32 sha256_h0[8] = - {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - -uint64 sha384_h0[8] = - {0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, - 0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL, - 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, - 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL}; - -uint64 sha512_h0[8] = - {0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL}; - -uint32 sha256_k[64] = - {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; - -uint64 sha512_k[80] = - {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, - 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, - 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, - 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, - 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, - 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, - 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, - 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, - 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, - 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, - 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, - 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, - 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, - 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, - 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, - 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, - 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, - 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, - 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, - 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, - 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, - 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, - 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, - 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, - 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, - 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, - 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; - -/* SHA-256 functions */ - -void sha256_transf(sha256_ctx *ctx, const unsigned char *message, - unsigned int block_nb) -{ - uint32 w[64]; - uint32 wv[8]; - uint32 t1, t2; - const unsigned char *sub_block; - int i; - -#ifndef UNROLL_LOOPS - int j; -#endif - - for (i = 0; i < (int) block_nb; i++) { - sub_block = message + (i << 6); - -#ifndef UNROLL_LOOPS - for (j = 0; j < 16; j++) { - PACK32(&sub_block[j << 2], &w[j]); - } - - for (j = 16; j < 64; j++) { - SHA256_SCR(j); - } - - for (j = 0; j < 8; j++) { - wv[j] = ctx->h[j]; - } - - for (j = 0; j < 64; j++) { - t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) - + sha256_k[j] + w[j]; - t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - - for (j = 0; j < 8; j++) { - ctx->h[j] += wv[j]; - } -#else - PACK32(&sub_block[ 0], &w[ 0]); PACK32(&sub_block[ 4], &w[ 1]); - PACK32(&sub_block[ 8], &w[ 2]); PACK32(&sub_block[12], &w[ 3]); - PACK32(&sub_block[16], &w[ 4]); PACK32(&sub_block[20], &w[ 5]); - PACK32(&sub_block[24], &w[ 6]); PACK32(&sub_block[28], &w[ 7]); - PACK32(&sub_block[32], &w[ 8]); PACK32(&sub_block[36], &w[ 9]); - PACK32(&sub_block[40], &w[10]); PACK32(&sub_block[44], &w[11]); - PACK32(&sub_block[48], &w[12]); PACK32(&sub_block[52], &w[13]); - PACK32(&sub_block[56], &w[14]); PACK32(&sub_block[60], &w[15]); - - SHA256_SCR(16); SHA256_SCR(17); SHA256_SCR(18); SHA256_SCR(19); - SHA256_SCR(20); SHA256_SCR(21); SHA256_SCR(22); SHA256_SCR(23); - SHA256_SCR(24); SHA256_SCR(25); SHA256_SCR(26); SHA256_SCR(27); - SHA256_SCR(28); SHA256_SCR(29); SHA256_SCR(30); SHA256_SCR(31); - SHA256_SCR(32); SHA256_SCR(33); SHA256_SCR(34); SHA256_SCR(35); - SHA256_SCR(36); SHA256_SCR(37); SHA256_SCR(38); SHA256_SCR(39); - SHA256_SCR(40); SHA256_SCR(41); SHA256_SCR(42); SHA256_SCR(43); - SHA256_SCR(44); SHA256_SCR(45); SHA256_SCR(46); SHA256_SCR(47); - SHA256_SCR(48); SHA256_SCR(49); SHA256_SCR(50); SHA256_SCR(51); - SHA256_SCR(52); SHA256_SCR(53); SHA256_SCR(54); SHA256_SCR(55); - SHA256_SCR(56); SHA256_SCR(57); SHA256_SCR(58); SHA256_SCR(59); - SHA256_SCR(60); SHA256_SCR(61); SHA256_SCR(62); SHA256_SCR(63); - - wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; - wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; - wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; - wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; - - SHA256_EXP(0,1,2,3,4,5,6,7, 0); SHA256_EXP(7,0,1,2,3,4,5,6, 1); - SHA256_EXP(6,7,0,1,2,3,4,5, 2); SHA256_EXP(5,6,7,0,1,2,3,4, 3); - SHA256_EXP(4,5,6,7,0,1,2,3, 4); SHA256_EXP(3,4,5,6,7,0,1,2, 5); - SHA256_EXP(2,3,4,5,6,7,0,1, 6); SHA256_EXP(1,2,3,4,5,6,7,0, 7); - SHA256_EXP(0,1,2,3,4,5,6,7, 8); SHA256_EXP(7,0,1,2,3,4,5,6, 9); - SHA256_EXP(6,7,0,1,2,3,4,5,10); SHA256_EXP(5,6,7,0,1,2,3,4,11); - SHA256_EXP(4,5,6,7,0,1,2,3,12); SHA256_EXP(3,4,5,6,7,0,1,2,13); - SHA256_EXP(2,3,4,5,6,7,0,1,14); SHA256_EXP(1,2,3,4,5,6,7,0,15); - SHA256_EXP(0,1,2,3,4,5,6,7,16); SHA256_EXP(7,0,1,2,3,4,5,6,17); - SHA256_EXP(6,7,0,1,2,3,4,5,18); SHA256_EXP(5,6,7,0,1,2,3,4,19); - SHA256_EXP(4,5,6,7,0,1,2,3,20); SHA256_EXP(3,4,5,6,7,0,1,2,21); - SHA256_EXP(2,3,4,5,6,7,0,1,22); SHA256_EXP(1,2,3,4,5,6,7,0,23); - SHA256_EXP(0,1,2,3,4,5,6,7,24); SHA256_EXP(7,0,1,2,3,4,5,6,25); - SHA256_EXP(6,7,0,1,2,3,4,5,26); SHA256_EXP(5,6,7,0,1,2,3,4,27); - SHA256_EXP(4,5,6,7,0,1,2,3,28); SHA256_EXP(3,4,5,6,7,0,1,2,29); - SHA256_EXP(2,3,4,5,6,7,0,1,30); SHA256_EXP(1,2,3,4,5,6,7,0,31); - SHA256_EXP(0,1,2,3,4,5,6,7,32); SHA256_EXP(7,0,1,2,3,4,5,6,33); - SHA256_EXP(6,7,0,1,2,3,4,5,34); SHA256_EXP(5,6,7,0,1,2,3,4,35); - SHA256_EXP(4,5,6,7,0,1,2,3,36); SHA256_EXP(3,4,5,6,7,0,1,2,37); - SHA256_EXP(2,3,4,5,6,7,0,1,38); SHA256_EXP(1,2,3,4,5,6,7,0,39); - SHA256_EXP(0,1,2,3,4,5,6,7,40); SHA256_EXP(7,0,1,2,3,4,5,6,41); - SHA256_EXP(6,7,0,1,2,3,4,5,42); SHA256_EXP(5,6,7,0,1,2,3,4,43); - SHA256_EXP(4,5,6,7,0,1,2,3,44); SHA256_EXP(3,4,5,6,7,0,1,2,45); - SHA256_EXP(2,3,4,5,6,7,0,1,46); SHA256_EXP(1,2,3,4,5,6,7,0,47); - SHA256_EXP(0,1,2,3,4,5,6,7,48); SHA256_EXP(7,0,1,2,3,4,5,6,49); - SHA256_EXP(6,7,0,1,2,3,4,5,50); SHA256_EXP(5,6,7,0,1,2,3,4,51); - SHA256_EXP(4,5,6,7,0,1,2,3,52); SHA256_EXP(3,4,5,6,7,0,1,2,53); - SHA256_EXP(2,3,4,5,6,7,0,1,54); SHA256_EXP(1,2,3,4,5,6,7,0,55); - SHA256_EXP(0,1,2,3,4,5,6,7,56); SHA256_EXP(7,0,1,2,3,4,5,6,57); - SHA256_EXP(6,7,0,1,2,3,4,5,58); SHA256_EXP(5,6,7,0,1,2,3,4,59); - SHA256_EXP(4,5,6,7,0,1,2,3,60); SHA256_EXP(3,4,5,6,7,0,1,2,61); - SHA256_EXP(2,3,4,5,6,7,0,1,62); SHA256_EXP(1,2,3,4,5,6,7,0,63); - - ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; - ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; - ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; - ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; -#endif /* !UNROLL_LOOPS */ - } -} - -void sha256(const unsigned char *message, unsigned int len, unsigned char *digest) -{ - sha256_ctx ctx; - - sha256_init(&ctx); - sha256_update(&ctx, message, len); - sha256_final(&ctx, digest); -} - -void sha256_init(sha256_ctx *ctx) -{ -#ifndef UNROLL_LOOPS - int i; - for (i = 0; i < 8; i++) { - ctx->h[i] = sha256_h0[i]; - } -#else - ctx->h[0] = sha256_h0[0]; ctx->h[1] = sha256_h0[1]; - ctx->h[2] = sha256_h0[2]; ctx->h[3] = sha256_h0[3]; - ctx->h[4] = sha256_h0[4]; ctx->h[5] = sha256_h0[5]; - ctx->h[6] = sha256_h0[6]; ctx->h[7] = sha256_h0[7]; -#endif /* !UNROLL_LOOPS */ - - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha256_update(sha256_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA256_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA256_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA256_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha256_transf(ctx, ctx->block, 1); - sha256_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA256_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 6], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 6; -} - -void sha256_final(sha256_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - -#ifndef UNROLL_LOOPS - int i; -#endif - - block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) - < (ctx->len % SHA256_BLOCK_SIZE))); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 6; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha256_transf(ctx, ctx->block, block_nb); - -#ifndef UNROLL_LOOPS - for (i = 0 ; i < 8; i++) { - UNPACK32(ctx->h[i], &digest[i << 2]); - } -#else - UNPACK32(ctx->h[0], &digest[ 0]); - UNPACK32(ctx->h[1], &digest[ 4]); - UNPACK32(ctx->h[2], &digest[ 8]); - UNPACK32(ctx->h[3], &digest[12]); - UNPACK32(ctx->h[4], &digest[16]); - UNPACK32(ctx->h[5], &digest[20]); - UNPACK32(ctx->h[6], &digest[24]); - UNPACK32(ctx->h[7], &digest[28]); -#endif /* !UNROLL_LOOPS */ -} - -/* SHA-512 functions */ - -void sha512_transf(sha512_ctx *ctx, const unsigned char *message, - unsigned int block_nb) -{ - uint64 w[80]; - uint64 wv[8]; - uint64 t1, t2; - const unsigned char *sub_block; - int i, j; - - for (i = 0; i < (int) block_nb; i++) { - sub_block = message + (i << 7); - -#ifndef UNROLL_LOOPS - for (j = 0; j < 16; j++) { - PACK64(&sub_block[j << 3], &w[j]); - } - - for (j = 16; j < 80; j++) { - SHA512_SCR(j); - } - - for (j = 0; j < 8; j++) { - wv[j] = ctx->h[j]; - } - - for (j = 0; j < 80; j++) { - t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) - + sha512_k[j] + w[j]; - t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - - for (j = 0; j < 8; j++) { - ctx->h[j] += wv[j]; - } -#else - PACK64(&sub_block[ 0], &w[ 0]); PACK64(&sub_block[ 8], &w[ 1]); - PACK64(&sub_block[ 16], &w[ 2]); PACK64(&sub_block[ 24], &w[ 3]); - PACK64(&sub_block[ 32], &w[ 4]); PACK64(&sub_block[ 40], &w[ 5]); - PACK64(&sub_block[ 48], &w[ 6]); PACK64(&sub_block[ 56], &w[ 7]); - PACK64(&sub_block[ 64], &w[ 8]); PACK64(&sub_block[ 72], &w[ 9]); - PACK64(&sub_block[ 80], &w[10]); PACK64(&sub_block[ 88], &w[11]); - PACK64(&sub_block[ 96], &w[12]); PACK64(&sub_block[104], &w[13]); - PACK64(&sub_block[112], &w[14]); PACK64(&sub_block[120], &w[15]); - - SHA512_SCR(16); SHA512_SCR(17); SHA512_SCR(18); SHA512_SCR(19); - SHA512_SCR(20); SHA512_SCR(21); SHA512_SCR(22); SHA512_SCR(23); - SHA512_SCR(24); SHA512_SCR(25); SHA512_SCR(26); SHA512_SCR(27); - SHA512_SCR(28); SHA512_SCR(29); SHA512_SCR(30); SHA512_SCR(31); - SHA512_SCR(32); SHA512_SCR(33); SHA512_SCR(34); SHA512_SCR(35); - SHA512_SCR(36); SHA512_SCR(37); SHA512_SCR(38); SHA512_SCR(39); - SHA512_SCR(40); SHA512_SCR(41); SHA512_SCR(42); SHA512_SCR(43); - SHA512_SCR(44); SHA512_SCR(45); SHA512_SCR(46); SHA512_SCR(47); - SHA512_SCR(48); SHA512_SCR(49); SHA512_SCR(50); SHA512_SCR(51); - SHA512_SCR(52); SHA512_SCR(53); SHA512_SCR(54); SHA512_SCR(55); - SHA512_SCR(56); SHA512_SCR(57); SHA512_SCR(58); SHA512_SCR(59); - SHA512_SCR(60); SHA512_SCR(61); SHA512_SCR(62); SHA512_SCR(63); - SHA512_SCR(64); SHA512_SCR(65); SHA512_SCR(66); SHA512_SCR(67); - SHA512_SCR(68); SHA512_SCR(69); SHA512_SCR(70); SHA512_SCR(71); - SHA512_SCR(72); SHA512_SCR(73); SHA512_SCR(74); SHA512_SCR(75); - SHA512_SCR(76); SHA512_SCR(77); SHA512_SCR(78); SHA512_SCR(79); - - wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; - wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; - wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; - wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; - - j = 0; - - do { - SHA512_EXP(0,1,2,3,4,5,6,7,j); j++; - SHA512_EXP(7,0,1,2,3,4,5,6,j); j++; - SHA512_EXP(6,7,0,1,2,3,4,5,j); j++; - SHA512_EXP(5,6,7,0,1,2,3,4,j); j++; - SHA512_EXP(4,5,6,7,0,1,2,3,j); j++; - SHA512_EXP(3,4,5,6,7,0,1,2,j); j++; - SHA512_EXP(2,3,4,5,6,7,0,1,j); j++; - SHA512_EXP(1,2,3,4,5,6,7,0,j); j++; - } while (j < 80); - - ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; - ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; - ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; - ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; -#endif /* !UNROLL_LOOPS */ - } -} - -void sha512(const unsigned char *message, unsigned int len, - unsigned char *digest) -{ - sha512_ctx ctx; - - sha512_init(&ctx); - sha512_update(&ctx, message, len); - sha512_final(&ctx, digest); -} - -void sha512_init(sha512_ctx *ctx) -{ -#ifndef UNROLL_LOOPS - int i; - for (i = 0; i < 8; i++) { - ctx->h[i] = sha512_h0[i]; - } -#else - ctx->h[0] = sha512_h0[0]; ctx->h[1] = sha512_h0[1]; - ctx->h[2] = sha512_h0[2]; ctx->h[3] = sha512_h0[3]; - ctx->h[4] = sha512_h0[4]; ctx->h[5] = sha512_h0[5]; - ctx->h[6] = sha512_h0[6]; ctx->h[7] = sha512_h0[7]; -#endif /* !UNROLL_LOOPS */ - - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha512_update(sha512_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA512_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA512_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA512_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha512_transf(ctx, ctx->block, 1); - sha512_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA512_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 7], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 7; -} - -void sha512_final(sha512_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - -#ifndef UNROLL_LOOPS - int i; -#endif - - block_nb = 1 + ((SHA512_BLOCK_SIZE - 17) - < (ctx->len % SHA512_BLOCK_SIZE)); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 7; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha512_transf(ctx, ctx->block, block_nb); - -#ifndef UNROLL_LOOPS - for (i = 0 ; i < 8; i++) { - UNPACK64(ctx->h[i], &digest[i << 3]); - } -#else - UNPACK64(ctx->h[0], &digest[ 0]); - UNPACK64(ctx->h[1], &digest[ 8]); - UNPACK64(ctx->h[2], &digest[16]); - UNPACK64(ctx->h[3], &digest[24]); - UNPACK64(ctx->h[4], &digest[32]); - UNPACK64(ctx->h[5], &digest[40]); - UNPACK64(ctx->h[6], &digest[48]); - UNPACK64(ctx->h[7], &digest[56]); -#endif /* !UNROLL_LOOPS */ -} - -/* SHA-384 functions */ - -void sha384(const unsigned char *message, unsigned int len, - unsigned char *digest) -{ - sha384_ctx ctx; - - sha384_init(&ctx); - sha384_update(&ctx, message, len); - sha384_final(&ctx, digest); -} - -void sha384_init(sha384_ctx *ctx) -{ -#ifndef UNROLL_LOOPS - int i; - for (i = 0; i < 8; i++) { - ctx->h[i] = sha384_h0[i]; - } -#else - ctx->h[0] = sha384_h0[0]; ctx->h[1] = sha384_h0[1]; - ctx->h[2] = sha384_h0[2]; ctx->h[3] = sha384_h0[3]; - ctx->h[4] = sha384_h0[4]; ctx->h[5] = sha384_h0[5]; - ctx->h[6] = sha384_h0[6]; ctx->h[7] = sha384_h0[7]; -#endif /* !UNROLL_LOOPS */ - - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha384_update(sha384_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA384_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA384_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA384_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha512_transf(ctx, ctx->block, 1); - sha512_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA384_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 7], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 7; -} - -void sha384_final(sha384_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - -#ifndef UNROLL_LOOPS - int i; -#endif - - block_nb = (1 + ((SHA384_BLOCK_SIZE - 17) - < (ctx->len % SHA384_BLOCK_SIZE))); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 7; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha512_transf(ctx, ctx->block, block_nb); - -#ifndef UNROLL_LOOPS - for (i = 0 ; i < 6; i++) { - UNPACK64(ctx->h[i], &digest[i << 3]); - } -#else - UNPACK64(ctx->h[0], &digest[ 0]); - UNPACK64(ctx->h[1], &digest[ 8]); - UNPACK64(ctx->h[2], &digest[16]); - UNPACK64(ctx->h[3], &digest[24]); - UNPACK64(ctx->h[4], &digest[32]); - UNPACK64(ctx->h[5], &digest[40]); -#endif /* !UNROLL_LOOPS */ -} - -/* SHA-224 functions */ - -void sha224(const unsigned char *message, unsigned int len, - unsigned char *digest) -{ - sha224_ctx ctx; - - sha224_init(&ctx); - sha224_update(&ctx, message, len); - sha224_final(&ctx, digest); -} - -void sha224_init(sha224_ctx *ctx) -{ -#ifndef UNROLL_LOOPS - int i; - for (i = 0; i < 8; i++) { - ctx->h[i] = sha224_h0[i]; - } -#else - ctx->h[0] = sha224_h0[0]; ctx->h[1] = sha224_h0[1]; - ctx->h[2] = sha224_h0[2]; ctx->h[3] = sha224_h0[3]; - ctx->h[4] = sha224_h0[4]; ctx->h[5] = sha224_h0[5]; - ctx->h[6] = sha224_h0[6]; ctx->h[7] = sha224_h0[7]; -#endif /* !UNROLL_LOOPS */ - - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha224_update(sha224_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA224_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA224_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA224_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha256_transf(ctx, ctx->block, 1); - sha256_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA224_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 6], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 6; -} - -void sha224_final(sha224_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - -#ifndef UNROLL_LOOPS - int i; -#endif - - block_nb = (1 + ((SHA224_BLOCK_SIZE - 9) - < (ctx->len % SHA224_BLOCK_SIZE))); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 6; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha256_transf(ctx, ctx->block, block_nb); - -#ifndef UNROLL_LOOPS - for (i = 0 ; i < 7; i++) { - UNPACK32(ctx->h[i], &digest[i << 2]); - } -#else - UNPACK32(ctx->h[0], &digest[ 0]); - UNPACK32(ctx->h[1], &digest[ 4]); - UNPACK32(ctx->h[2], &digest[ 8]); - UNPACK32(ctx->h[3], &digest[12]); - UNPACK32(ctx->h[4], &digest[16]); - UNPACK32(ctx->h[5], &digest[20]); - UNPACK32(ctx->h[6], &digest[24]); -#endif /* !UNROLL_LOOPS */ -} - -#ifdef TEST_VECTORS - -/* FIPS 180-2 Validation tests */ - -#include -#include - -void test(const unsigned char *vector, unsigned char *digest, - unsigned int digest_size) -{ - unsigned char output[2 * SHA512_DIGEST_SIZE + 1]; - int i; - - output[2 * digest_size] = '\0'; - - for (i = 0; i < (int) digest_size ; i++) { - sprintf((char *) output + 2 * i, "%02x", digest[i]); - } - - printf("H: %s\n", output); - if (strcmp((char *) vector, (char *) output)) { - fprintf(stderr, "Test failed.\n"); - exit(EXIT_FAILURE); - } -} - -int main() -{ - static const unsigned char *vectors[4][3] = - { /* SHA-224 */ - { - "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", - "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", - "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67", - }, - /* SHA-256 */ - { - "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", - "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", - "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", - }, - /* SHA-384 */ - { - "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed" - "8086072ba1e7cc2358baeca134c825a7", - "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712" - "fcc7c71a557e2db966c3e9fa91746039", - "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b" - "07b8b3dc38ecc4ebae97ddd87f3d8985", - }, - /* SHA-512 */ - { - "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" - "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", - "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" - "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", - "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" - "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b" - } - }; - - static const unsigned char message1[] = "abc"; - static const unsigned char message2a[] = "abcdbcdecdefdefgefghfghighijhi" - "jkijkljklmklmnlmnomnopnopq"; - static const unsigned char message2b[] = - "abcdefghbcdefghicdefghijdefghijkefghij" - "klfghijklmghijklmnhijklmnoijklmnopjklm" - "nopqklmnopqrlmnopqrsmnopqrstnopqrstu"; - unsigned char *message3; - unsigned int message3_len = 1000000; - unsigned char digest[SHA512_DIGEST_SIZE]; - - message3 = malloc(message3_len); - if (message3 == NULL) { - fprintf(stderr, "Can't allocate memory\n"); - return -1; - } - memset(message3, 'a', message3_len); - - printf("SHA-2 FIPS 180-2 Validation tests\n\n"); - printf("SHA-224 Test vectors\n"); - - sha224(message1, strlen((char *) message1), digest); - test(vectors[0][0], digest, SHA224_DIGEST_SIZE); - sha224(message2a, strlen((char *) message2a), digest); - test(vectors[0][1], digest, SHA224_DIGEST_SIZE); - sha224(message3, message3_len, digest); - test(vectors[0][2], digest, SHA224_DIGEST_SIZE); - printf("\n"); - - printf("SHA-256 Test vectors\n"); - - sha256(message1, strlen((char *) message1), digest); - test(vectors[1][0], digest, SHA256_DIGEST_SIZE); - sha256(message2a, strlen((char *) message2a), digest); - test(vectors[1][1], digest, SHA256_DIGEST_SIZE); - sha256(message3, message3_len, digest); - test(vectors[1][2], digest, SHA256_DIGEST_SIZE); - printf("\n"); - - printf("SHA-384 Test vectors\n"); - - sha384(message1, strlen((char *) message1), digest); - test(vectors[2][0], digest, SHA384_DIGEST_SIZE); - sha384(message2b, strlen((char *) message2b), digest); - test(vectors[2][1], digest, SHA384_DIGEST_SIZE); - sha384(message3, message3_len, digest); - test(vectors[2][2], digest, SHA384_DIGEST_SIZE); - printf("\n"); - - printf("SHA-512 Test vectors\n"); - - sha512(message1, strlen((char *) message1), digest); - test(vectors[3][0], digest, SHA512_DIGEST_SIZE); - sha512(message2b, strlen((char *) message2b), digest); - test(vectors[3][1], digest, SHA512_DIGEST_SIZE); - sha512(message3, message3_len, digest); - test(vectors[3][2], digest, SHA512_DIGEST_SIZE); - printf("\n"); - - printf("All tests passed.\n"); - - return 0; -} - -#endif /* TEST_VECTORS */ - diff --git a/src/fetch/sha2.h b/src/fetch/sha2.h deleted file mode 100644 index f4218614..00000000 --- a/src/fetch/sha2.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * FIPS 180-2 SHA-224/256/384/512 implementation - * Last update: 02/02/2007 - * Issue date: 04/30/2005 - * - * Copyright (C) 2005, 2007 Olivier Gay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef SHA2_H -#define SHA2_H - -#define SHA224_DIGEST_SIZE ( 224 / 8) -#define SHA256_DIGEST_SIZE ( 256 / 8) -#define SHA384_DIGEST_SIZE ( 384 / 8) -#define SHA512_DIGEST_SIZE ( 512 / 8) - -#define SHA256_BLOCK_SIZE ( 512 / 8) -#define SHA512_BLOCK_SIZE (1024 / 8) -#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE -#define SHA224_BLOCK_SIZE SHA256_BLOCK_SIZE - -#ifndef SHA2_TYPES -#define SHA2_TYPES -typedef unsigned char uint8; -typedef unsigned int uint32; -typedef unsigned long long uint64; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - unsigned int tot_len; - unsigned int len; - unsigned char block[2 * SHA256_BLOCK_SIZE]; - uint32 h[8]; -} sha256_ctx; - -typedef struct { - unsigned int tot_len; - unsigned int len; - unsigned char block[2 * SHA512_BLOCK_SIZE]; - uint64 h[8]; -} sha512_ctx; - -typedef sha512_ctx sha384_ctx; -typedef sha256_ctx sha224_ctx; - -void sha224_init(sha224_ctx *ctx); -void sha224_update(sha224_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha224_final(sha224_ctx *ctx, unsigned char *digest); -void sha224(const unsigned char *message, unsigned int len, - unsigned char *digest); - -void sha256_init(sha256_ctx * ctx); -void sha256_update(sha256_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha256_final(sha256_ctx *ctx, unsigned char *digest); -void sha256(const unsigned char *message, unsigned int len, - unsigned char *digest); - -void sha384_init(sha384_ctx *ctx); -void sha384_update(sha384_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha384_final(sha384_ctx *ctx, unsigned char *digest); -void sha384(const unsigned char *message, unsigned int len, - unsigned char *digest); - -void sha512_init(sha512_ctx *ctx); -void sha512_update(sha512_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha512_final(sha512_ctx *ctx, unsigned char *digest); -void sha512(const unsigned char *message, unsigned int len, - unsigned char *digest); - -#ifdef __cplusplus -} -#endif - -#endif /* !SHA2_H */ diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c558fc96..159a2e29 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -1,861 +1,860 @@ include(ECMAddTests) include(ECMMarkNonGuiExecutable) SET( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) # formattest includes fieldformat that include core/tellico_config.h # which is generated in the build directory INCLUDE_DIRECTORIES( ${Tellico_BINARY_DIR}/src ) add_executable(entitytest entitytest.cpp ../fieldformat.cpp) ecm_mark_nongui_executable(entitytest) add_test(entitytest entitytest) ecm_mark_as_test(entitytest) TARGET_LINK_LIBRARIES(entitytest utils config Qt5::Test ) add_executable(cuecattest cuecattest.cpp) ecm_mark_nongui_executable(cuecattest) add_test(cuecattest cuecattest) ecm_mark_as_test(cuecattest) TARGET_LINK_LIBRARIES(cuecattest utils Qt5::Test) add_executable(isbntest isbntest.cpp) ecm_mark_nongui_executable(isbntest) add_test(isbntest isbntest) ecm_mark_as_test(isbntest) TARGET_LINK_LIBRARIES(isbntest utils Qt5::Test) add_executable(lccntest lccntest.cpp) ecm_mark_nongui_executable(lccntest) add_test(lccntest lccntest) ecm_mark_as_test(lccntest) TARGET_LINK_LIBRARIES(lccntest utils Qt5::Test) add_executable(lcctest lcctest.cpp ../field.cpp ../fieldformat.cpp) ecm_mark_nongui_executable(lcctest) add_test(lcctest lcctest) ecm_mark_as_test(lcctest) TARGET_LINK_LIBRARIES(lcctest tellicomodels Qt5::Test) add_executable(formattest formattest.cpp ../fieldformat.cpp) ecm_mark_nongui_executable(formattest) add_test(formattest formattest) ecm_mark_as_test(formattest) TARGET_LINK_LIBRARIES(formattest config Qt5::Test) add_executable(fieldtest fieldtest.cpp ../field.cpp ../fieldformat.cpp ../gui/urlfieldlogic.cpp) ecm_mark_nongui_executable(fieldtest) add_test(fieldtest fieldtest) ecm_mark_as_test(fieldtest) TARGET_LINK_LIBRARIES(fieldtest config utils Qt5::Test) add_executable(comparisontest comparisontest.cpp ../field.cpp ../fieldformat.cpp) ecm_mark_nongui_executable(comparisontest) add_test(comparisontest comparisontest) ecm_mark_as_test(comparisontest) TARGET_LINK_LIBRARIES(comparisontest tellicomodels Qt5::Test) add_executable(imagetest imagetest.cpp ../utils/tellico_utils.cpp ../utils/guiproxy.cpp ../utils/cursorsaver.cpp ../fieldformat.cpp) ecm_mark_nongui_executable(imagetest) add_test(imagetest imagetest) ecm_mark_as_test(imagetest) TARGET_LINK_LIBRARIES(imagetest images KF5::Archive Qt5::Test) add_executable(imagejobtest imagejobtest.cpp ../fieldformat.cpp) ecm_mark_nongui_executable(imagejobtest) add_test(imagejobtest imagejobtest) ecm_mark_as_test(imagejobtest) TARGET_LINK_LIBRARIES(imagejobtest images KF5::Archive Qt5::Test) add_executable(iso6937test iso6937test.cpp) ecm_mark_nongui_executable(iso6937test) add_test(iso6937test iso6937test) ecm_mark_as_test(iso6937test) TARGET_LINK_LIBRARIES(iso6937test utils Qt5::Test) SET(tellicotest_SRCS ../collection.cpp ../entry.cpp ../entrygroup.cpp ../entrycomparison.cpp ../field.cpp ../fieldformat.cpp ../filter.cpp ../borrower.cpp ../collectionfactory.cpp ../derivedvalue.cpp ../progressmanager.cpp ) add_library(tellicotest STATIC ${tellicotest_SRCS}) TARGET_LINK_LIBRARIES(tellicotest Qt5::Core Qt5::Gui KF5::I18n KF5::ConfigWidgets) ADD_DEPENDENCIES(tellicotest tellico_config) SET(translatorstest_SRCS ../translators/tellicoimporter.cpp ../translators/xsltimporter.cpp ../translators/textimporter.cpp ../translators/dataimporter.cpp ../translators/importer.cpp ../translators/tellicoxmlhandler.cpp ../translators/tellico_xml.cpp ../translators/xmlstatehandler.cpp ../translators/xslthandler.cpp ) add_library(translatorstest STATIC ${translatorstest_SRCS}) TARGET_LINK_LIBRARIES(translatorstest Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml KF5::I18n KF5::Archive KF5::KIOWidgets KF5::ConfigWidgets ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${LIBXSLT_EXSLT_LIBRARIES} ) set(TELLICO_TEST_LIBS tellicotest collections utils images core tellicomodels Qt5::Test KF5::KIOCore KF5::Archive ) add_executable(collectiontest collectiontest.cpp ../document.cpp ../translators/tellicoxmlexporter.cpp ../translators/tellicozipexporter.cpp ../translators/exporter.cpp ) ecm_mark_nongui_executable(collectiontest) add_test(collectiontest collectiontest) ecm_mark_as_test(collectiontest) TARGET_LINK_LIBRARIES(collectiontest translatorstest ${TELLICO_TEST_LIBS}) add_executable(documenttest documenttest.cpp ../document.cpp ../translators/tellicoxmlexporter.cpp ../translators/tellicozipexporter.cpp ../translators/exporter.cpp ) ecm_mark_nongui_executable(documenttest) add_test(documenttest documenttest) ecm_mark_as_test(documenttest) TARGET_LINK_LIBRARIES(documenttest translatorstest ${TELLICO_TEST_LIBS}) add_executable(filtertest filtertest.cpp) ecm_mark_nongui_executable(filtertest) add_test(filtertest filtertest) ecm_mark_as_test(filtertest) TARGET_LINK_LIBRARIES(filtertest gui ${TELLICO_TEST_LIBS}) add_executable(tellicomodeltest tellicomodeltest.cpp modeltest.cpp ../document.cpp ../translators/tellicoimporter.cpp ../translators/dataimporter.cpp ../translators/importer.cpp ../translators/tellicoxmlexporter.cpp ../translators/tellicozipexporter.cpp ../translators/exporter.cpp ../translators/tellicoxmlhandler.cpp ../translators/tellico_xml.cpp ../translators/xmlstatehandler.cpp ) ecm_mark_nongui_executable(tellicomodeltest) add_test(tellicomodeltest tellicomodeltest) ecm_mark_as_test(tellicomodeltest) TARGET_LINK_LIBRARIES(tellicomodeltest ${TELLICO_TEST_LIBS} ${LIBXML2_LIBRARIES}) ###################################################### add_executable(adstest adstest.cpp ../translators/adsimporter.cpp ../translators/importer.cpp ) ecm_mark_nongui_executable(adstest) add_test(adstest adstest) ecm_mark_as_test(adstest) TARGET_LINK_LIBRARIES(adstest ${TELLICO_TEST_LIBS}) add_executable(alexandriatest alexandriatest.cpp ../translators/alexandriaimporter.cpp ../translators/importer.cpp ../translators/alexandriaexporter.cpp ../translators/exporter.cpp ) ecm_mark_nongui_executable(alexandriatest) add_test(alexandriatest alexandriatest) ecm_mark_as_test(alexandriatest) TARGET_LINK_LIBRARIES(alexandriatest ${TELLICO_TEST_LIBS}) add_executable(amctest amctest.cpp ../translators/amcimporter.cpp ../translators/dataimporter.cpp ../translators/importer.cpp ) ecm_mark_nongui_executable(amctest) add_test(amctest amctest) ecm_mark_as_test(amctest) TARGET_LINK_LIBRARIES(amctest ${TELLICO_TEST_LIBS}) IF( TAGLIB_FOUND ) add_executable(audiofiletest audiofiletest.cpp ../translators/audiofileimporter.cpp ../translators/dataimporter.cpp ../translators/importer.cpp ) ecm_mark_nongui_executable(audiofiletest) add_test(audiofiletest audiofiletest) ecm_mark_as_test(audiofiletest) TARGET_LINK_LIBRARIES(audiofiletest ${TELLICO_TEST_LIBS} ${TAGLIB_LIBRARIES}) ENDIF( TAGLIB_FOUND ) add_executable(bibtextest bibtextest.cpp ../translators/bibteximporter.cpp ../translators/importer.cpp ../translators/bibtexexporter.cpp ../translators/exporter.cpp ) ecm_mark_nongui_executable(bibtextest) add_test(bibtextest bibtextest) ecm_mark_as_test(bibtextest) TARGET_LINK_LIBRARIES(bibtextest ${TELLICO_BTPARSE_LIBS} ${TELLICO_TEST_LIBS}) add_executable(bibtexmltest bibtexmltest.cpp ../translators/bibtexmlimporter.cpp ../translators/bibtexmlexporter.cpp ../translators/exporter.cpp ) ecm_mark_nongui_executable(bibtexmltest) add_test(bibtexmltest bibtexmltest) ecm_mark_as_test(bibtexmltest) TARGET_LINK_LIBRARIES(bibtexmltest translatorstest core ${TELLICO_TEST_LIBS}) add_executable(citetest citetest.cpp ../collections/bibtexcollection.cpp ../core/filehandler.cpp ) ecm_mark_nongui_executable(citetest) add_test(citetest citetest) ecm_mark_as_test(citetest) TARGET_LINK_LIBRARIES(citetest cite tellicotest config images Qt5::Test) ADD_DEPENDENCIES(citetest tellico_config) add_executable(ciwtest ciwtest.cpp ../translators/ciwimporter.cpp ../translators/importer.cpp ) ecm_mark_nongui_executable(ciwtest) add_test(ciwtest ciwtest) ecm_mark_as_test(ciwtest) TARGET_LINK_LIBRARIES(ciwtest ${TELLICO_TEST_LIBS}) add_executable(csvtest csvtest.cpp ../translators/csvparser.cpp ../translators/csvexporter.cpp ../translators/exporter.cpp ) ecm_mark_nongui_executable(csvtest) add_test(csvtest csvtest) ecm_mark_as_test(csvtest) TARGET_LINK_LIBRARIES(csvtest ${TELLICO_CSV_LIBS} translatorstest ${TELLICO_TEST_LIBS}) add_executable(delicioustest delicioustest.cpp ../translators/deliciousimporter.cpp ) ecm_mark_nongui_executable(delicioustest) add_test(delicioustest delicioustest) ecm_mark_as_test(delicioustest) TARGET_LINK_LIBRARIES(delicioustest rtf2html-tellico translatorstest ${TELLICO_TEST_LIBS}) add_executable(filelistingtest filelistingtest.cpp ../translators/filelistingimporter.cpp ../translators/xmphandler.cpp ) ecm_mark_nongui_executable(filelistingtest) add_test(filelistingtest filelistingtest) ecm_mark_as_test(filelistingtest) TARGET_LINK_LIBRARIES(filelistingtest translatorstest ${TELLICO_TEST_LIBS} KF5::Solid ) IF( KF5FileMetaData_FOUND ) TARGET_LINK_LIBRARIES(filelistingtest KF5::FileMetaData) ENDIF( KF5FileMetaData_FOUND ) IF( Exempi_FOUND ) TARGET_LINK_LIBRARIES(filelistingtest ${Exempi_LIBRARIES}) ENDIF( Exempi_FOUND ) add_executable(gcstartest gcstartest.cpp ../translators/gcstarimporter.cpp ../translators/gcstarexporter.cpp ../translators/tellicoxmlexporter.cpp ../translators/tellicozipexporter.cpp ../translators/exporter.cpp ../document.cpp ) ecm_mark_nongui_executable(gcstartest) add_test(gcstartest gcstartest) ecm_mark_as_test(gcstartest) TARGET_LINK_LIBRARIES(gcstartest translatorstest ${TELLICO_TEST_LIBS}) add_executable(griffithtest griffithtest.cpp ../translators/griffithimporter.cpp ) ecm_mark_nongui_executable(griffithtest) add_test(griffithtest griffithtest) ecm_mark_as_test(griffithtest) TARGET_LINK_LIBRARIES(griffithtest translatorstest ${TELLICO_TEST_LIBS}) add_executable(htmlexportertest htmlexportertest.cpp ../translators/htmlexporter.cpp ../translators/tellicoxmlexporter.cpp ../translators/tellicozipexporter.cpp ../translators/exporter.cpp ../document.cpp ) ecm_mark_nongui_executable(htmlexportertest) add_test(htmlexportertest htmlexportertest) ecm_mark_as_test(htmlexportertest) TARGET_LINK_LIBRARIES(htmlexportertest translatorstest ${TELLICO_TEST_LIBS}) add_executable(modstest modstest.cpp) ecm_mark_nongui_executable(modstest) add_test(modstest modstest) ecm_mark_as_test(modstest) TARGET_LINK_LIBRARIES(modstest translatorstest ${TELLICO_TEST_LIBS}) add_executable(referencertest referencertest.cpp ../translators/referencerimporter.cpp ) ecm_mark_nongui_executable(referencertest) add_test(referencertest referencertest) ecm_mark_as_test(referencertest) TARGET_LINK_LIBRARIES(referencertest translatorstest ${TELLICO_TEST_LIBS}) add_executable(ristest ristest.cpp ../translators/risimporter.cpp ../translators/importer.cpp ) ecm_mark_nongui_executable(ristest) add_test(ristest ristest) ecm_mark_as_test(ristest) TARGET_LINK_LIBRARIES(ristest ${TELLICO_TEST_LIBS}) add_executable(tellicoreadtest tellicoreadtest.cpp ../translators/tellicoxmlexporter.cpp ../translators/tellicozipexporter.cpp ../translators/exporter.cpp ../document.cpp ) ecm_mark_nongui_executable(tellicoreadtest) add_test(tellicoreadtest tellicoreadtest) ecm_mark_as_test(tellicoreadtest) TARGET_LINK_LIBRARIES(tellicoreadtest translatorstest ${TELLICO_TEST_LIBS}) add_executable(vinoxmltest vinoxmltest.cpp ../translators/vinoxmlimporter.cpp ) ecm_mark_nongui_executable(vinoxmltest) add_test(vinoxmltest vinoxmltest) ecm_mark_as_test(vinoxmltest) TARGET_LINK_LIBRARIES(vinoxmltest translatorstest ${TELLICO_TEST_LIBS}) SET(fetcherstest_SRCS ../fetch/fetcher.cpp ../fetch/fetcherjob.cpp ../fetch/fetchresult.cpp ../fetch/fetchmanager.cpp ../fetch/messagehandler.cpp ../fetch/configwidget.cpp ../document.cpp ../translators/tellicoxmlexporter.cpp ../translators/tellicozipexporter.cpp ../translators/exporter.cpp ) # FetchManager links to SRUFetcher for the default sources # so have to include everything it needs SET(fetcherstest_SRCS ${fetcherstest_SRCS} ../fetch/srufetcher.cpp ../fetch/xmlfetcher.cpp ../gui/lineedit.cpp ../gui/combobox.cpp ../gui/stringmapwidget.cpp ) add_library(fetcherstest STATIC ${fetcherstest_SRCS}) TARGET_LINK_LIBRARIES(fetcherstest translatorstest Qt5::Core Qt5::Gui Qt5::DBus KF5::ConfigCore KF5::ConfigGui KF5::ConfigWidgets KF5::I18n KF5::Archive KF5::IconThemes KF5::XmlGui KF5::SonnetCore KF5::SonnetUi ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${LIBXSLT_EXSLT_LIBRARIES} ) ADD_DEPENDENCIES(fetcherstest tellico_config) IF(${KF5KIO_VERSION} VERSION_GREATER "5.18.0") TARGET_LINK_LIBRARIES(fetcherstest KF5::KIOGui ) ENDIF(${KF5KIO_VERSION} VERSION_GREATER "5.18.0") # the PDF importer uses CrossRefFetcher, so include the test in the fetchers add_executable(pdftest pdftest.cpp ../translators/pdfimporter.cpp ../translators/xmphandler.cpp ../entryupdatejob.cpp ) ecm_mark_nongui_executable(pdftest) add_test(pdftest pdftest) ecm_mark_as_test(pdftest) TARGET_LINK_LIBRARIES(pdftest translatorstest fetcherstest ${TELLICO_TEST_LIBS}) IF( Exempi_FOUND ) TARGET_LINK_LIBRARIES(pdftest ${Exempi_LIBRARIES}) ENDIF( Exempi_FOUND ) IF( Poppler_Qt5_FOUND ) TARGET_LINK_LIBRARIES(pdftest Poppler::Qt5) ENDIF( Poppler_Qt5_FOUND ) # fetcher tests from here down IF(BUILD_FETCHER_TESTS) add_executable(entryupdatejobtest entryupdatejobtest.cpp ../entryupdatejob.cpp ../fetch/arxivfetcher.cpp ) ecm_mark_nongui_executable(entryupdatejobtest) add_test(entryupdatejobtest entryupdatejobtest) ecm_mark_as_test(entryupdatejobtest) TARGET_LINK_LIBRARIES(entryupdatejobtest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(allocinefetchertest allocinefetchertest.cpp abstractfetchertest.cpp ../fetch/allocinefetcher.cpp ../fetch/execexternalfetcher.cpp ../translators/bibteximporter.cpp ../translators/risimporter.cpp ../gui/collectiontypecombo.cpp ) ecm_mark_nongui_executable(allocinefetchertest) add_test(allocinefetchertest allocinefetchertest) ecm_mark_as_test(allocinefetchertest) # SonnetCore is needed for GUI::LineEdit TARGET_LINK_LIBRARIES(allocinefetchertest fetcherstest translatorstest newstuff ${TELLICO_BTPARSE_LIBS} ${TELLICO_TEST_LIBS} ) add_executable(amazonfetchertest amazonfetchertest.cpp abstractfetchertest.cpp ../fetch/amazonfetcher.cpp ../fetch/amazonrequest.cpp - ../fetch/hmac_sha2.c - ../fetch/sha2.c + ../fetch/messagelogger.cpp ) ecm_mark_nongui_executable(amazonfetchertest) add_test(amazonfetchertest amazonfetchertest) ecm_mark_as_test(amazonfetchertest) TARGET_LINK_LIBRARIES(amazonfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(animenfofetchertest animenfofetchertest.cpp abstractfetchertest.cpp ../fetch/animenfofetcher.cpp ) ecm_mark_nongui_executable(animenfofetchertest) add_test(animenfofetchertest animenfofetchertest) ecm_mark_as_test(animenfofetchertest) TARGET_LINK_LIBRARIES(animenfofetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(arxivfetchertest arxivfetchertest.cpp abstractfetchertest.cpp ../fetch/arxivfetcher.cpp ) ecm_mark_nongui_executable(arxivfetchertest) add_test(arxivfetchertest arxivfetchertest) ecm_mark_as_test(arxivfetchertest) TARGET_LINK_LIBRARIES(arxivfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(bedethequefetchertest bedethequefetchertest.cpp abstractfetchertest.cpp ../fetch/bedethequefetcher.cpp ) ecm_mark_nongui_executable(bedethequefetchertest) add_test(bedethequefetchertest bedethequefetchertest) ecm_mark_as_test(bedethequefetchertest) TARGET_LINK_LIBRARIES(bedethequefetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(bibliosharefetchertest bibliosharefetchertest.cpp abstractfetchertest.cpp ../fetch/bibliosharefetcher.cpp ) ecm_mark_nongui_executable(bibliosharefetchertest) add_test(bibliosharefetchertest bibliosharefetchertest) ecm_mark_as_test(bibliosharefetchertest) TARGET_LINK_LIBRARIES(bibliosharefetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(boardgamegeekfetchertest boardgamegeekfetchertest.cpp abstractfetchertest.cpp ../fetch/boardgamegeekfetcher.cpp ) ecm_mark_nongui_executable(boardgamegeekfetchertest) add_test(boardgamegeekfetchertest boardgamegeekfetchertest) ecm_mark_as_test(boardgamegeekfetchertest) TARGET_LINK_LIBRARIES(boardgamegeekfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(comicvinefetchertest comicvinefetchertest.cpp abstractfetchertest.cpp ../fetch/comicvinefetcher.cpp ) ecm_mark_nongui_executable(comicvinefetchertest) add_test(comicvinefetchertest comicvinefetchertest) ecm_mark_as_test(comicvinefetchertest) TARGET_LINK_LIBRARIES(comicvinefetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(colnectfetchertest colnectfetchertest.cpp abstractfetchertest.cpp ../fetch/colnectfetcher.cpp ) ecm_mark_nongui_executable(colnectfetchertest) add_test(colnectfetchertest colnectfetchertest) ecm_mark_as_test(colnectfetchertest) TARGET_LINK_LIBRARIES(colnectfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(crossreffetchertest crossreffetchertest.cpp abstractfetchertest.cpp ../fetch/crossreffetcher.cpp ) ecm_mark_nongui_executable(crossreffetchertest) add_test(crossreffetchertest crossreffetchertest) ecm_mark_as_test(crossreffetchertest) TARGET_LINK_LIBRARIES(crossreffetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(darkhorsefetchertest darkhorsefetchertest.cpp abstractfetchertest.cpp ../fetch/execexternalfetcher.cpp ../translators/bibteximporter.cpp ../translators/risimporter.cpp ../gui/collectiontypecombo.cpp ) ecm_mark_nongui_executable(darkhorsefetchertest) add_test(darkhorsefetchertest darkhorsefetchertest) ecm_mark_as_test(darkhorsefetchertest) TARGET_LINK_LIBRARIES(darkhorsefetchertest fetcherstest newstuff ${TELLICO_BTPARSE_LIBS} ${TELLICO_TEST_LIBS} ) add_executable(dbcfetchertest dbcfetchertest.cpp abstractfetchertest.cpp ../fetch/dbcfetcher.cpp ) ecm_mark_nongui_executable(dbcfetchertest) add_test(dbcfetchertest dbcfetchertest) ecm_mark_as_test(dbcfetchertest) TARGET_LINK_LIBRARIES(dbcfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(dblpfetchertest dblpfetchertest.cpp abstractfetchertest.cpp ../fetch/dblpfetcher.cpp ) ecm_mark_nongui_executable(dblpfetchertest) add_test(dblpfetchertest dblpfetchertest) ecm_mark_as_test(dblpfetchertest) TARGET_LINK_LIBRARIES(dblpfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(discogsfetchertest discogsfetchertest.cpp abstractfetchertest.cpp ../fetch/discogsfetcher.cpp ) ecm_mark_nongui_executable(discogsfetchertest) add_test(discogsfetchertest discogsfetchertest) ecm_mark_as_test(discogsfetchertest) TARGET_LINK_LIBRARIES(discogsfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(doubanfetchertest doubanfetchertest.cpp abstractfetchertest.cpp ../fetch/doubanfetcher.cpp ) ecm_mark_nongui_executable(doubanfetchertest) add_test(doubanfetchertest doubanfetchertest) ecm_mark_as_test(doubanfetchertest) TARGET_LINK_LIBRARIES(doubanfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(dvdfrfetchertest dvdfrfetchertest.cpp abstractfetchertest.cpp ../fetch/dvdfrfetcher.cpp ) ecm_mark_nongui_executable(dvdfrfetchertest) add_test(dvdfrfetchertest dvdfrfetchertest) ecm_mark_as_test(dvdfrfetchertest) TARGET_LINK_LIBRARIES(dvdfrfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(entrezfetchertest entrezfetchertest.cpp abstractfetchertest.cpp ../fetch/entrezfetcher.cpp ) ecm_mark_nongui_executable(entrezfetchertest) add_test(entrezfetchertest entrezfetchertest) ecm_mark_as_test(entrezfetchertest) TARGET_LINK_LIBRARIES(entrezfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(externalfetchertest externalfetchertest.cpp abstractfetchertest.cpp ../fetch/execexternalfetcher.cpp ../translators/bibteximporter.cpp ../translators/risimporter.cpp ../gui/collectiontypecombo.cpp ) ecm_mark_nongui_executable(externalfetchertest) add_test(externalfetchertest externalfetchertest) ecm_mark_as_test(externalfetchertest) TARGET_LINK_LIBRARIES(externalfetchertest fetcherstest newstuff ${TELLICO_BTPARSE_LIBS} ${TELLICO_TEST_LIBS} KF5::SonnetCore KF5::SonnetUi KF5::XmlGui ) add_executable(filmasterfetchertest filmasterfetchertest.cpp abstractfetchertest.cpp ../fetch/filmasterfetcher.cpp ) ecm_mark_nongui_executable(filmasterfetchertest) add_test(filmasterfetchertest filmasterfetchertest) ecm_mark_as_test(filmasterfetchertest) TARGET_LINK_LIBRARIES(filmasterfetchertest fetcherstest translatorstest ${TELLICO_TEST_LIBS}) # running gcstar in the fetcher is really unreliable add_executable(gcstarfetchertest gcstarfetchertest.cpp abstractfetchertest ../fetch/gcstarpluginfetcher.cpp ../fetch/gcstarthread.cpp ../translators/gcstarimporter.cpp ../gui/collectiontypecombo.cpp ) ecm_mark_nongui_executable(gcstarfetchertest) #add_test(gcstarfetchertest gcstarfetchertest) #ecm_mark_as_test(gcstarfetchertest) TARGET_LINK_LIBRARIES(gcstarfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(giantbombfetchertest giantbombfetchertest.cpp abstractfetchertest.cpp ../fetch/giantbombfetcher.cpp ) ecm_mark_nongui_executable(giantbombfetchertest) add_test(giantbombfetchertest giantbombfetchertest) ecm_mark_as_test(giantbombfetchertest) TARGET_LINK_LIBRARIES(giantbombfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(googlebookfetchertest googlebookfetchertest.cpp abstractfetchertest.cpp ../fetch/googlebookfetcher.cpp ) ecm_mark_nongui_executable(googlebookfetchertest) add_test(googlebookfetchertest googlebookfetchertest) ecm_mark_as_test(googlebookfetchertest) TARGET_LINK_LIBRARIES(googlebookfetchertest fetcherstest translatorstest ${TELLICO_TEST_LIBS}) add_executable(googlescholarfetchertest googlescholarfetchertest.cpp abstractfetchertest.cpp ../fetch/googlescholarfetcher.cpp ../translators/bibteximporter.cpp ) ecm_mark_nongui_executable(googlescholarfetchertest) add_test(googlescholarfetchertest googlescholarfetchertest) ecm_mark_as_test(googlescholarfetchertest) TARGET_LINK_LIBRARIES(googlescholarfetchertest fetcherstest ${TELLICO_BTPARSE_LIBS} ${TELLICO_TEST_LIBS}) add_executable(hathitrustfetchertest hathitrustfetchertest.cpp abstractfetchertest.cpp ../fetch/hathitrustfetcher.cpp ) ecm_mark_nongui_executable(hathitrustfetchertest) add_test(hathitrustfetchertest hathitrustfetchertest) ecm_mark_as_test(hathitrustfetchertest) TARGET_LINK_LIBRARIES(hathitrustfetchertest fetcherstest translatorstest ${TELLICO_TEST_LIBS}) add_executable(ibsfetchertest ibsfetchertest.cpp abstractfetchertest.cpp ../fetch/ibsfetcher.cpp ) ecm_mark_nongui_executable(ibsfetchertest) add_test(ibsfetchertest ibsfetchertest) ecm_mark_as_test(ibsfetchertest) TARGET_LINK_LIBRARIES(ibsfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(igdbfetchertest igdbfetchertest.cpp abstractfetchertest.cpp ../fetch/igdbfetcher.cpp ) ecm_mark_nongui_executable(igdbfetchertest) add_test(igdbfetchertest igdbfetchertest) ecm_mark_as_test(igdbfetchertest) TARGET_LINK_LIBRARIES(igdbfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(imdbfetchertest imdbfetchertest.cpp abstractfetchertest.cpp ../fetch/imdbfetcher.cpp ../gui/listwidgetitem.cpp ) ecm_mark_nongui_executable(imdbfetchertest) add_test(imdbfetchertest imdbfetchertest) ecm_mark_as_test(imdbfetchertest) TARGET_LINK_LIBRARIES(imdbfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(isbndbfetchertest isbndbfetchertest.cpp abstractfetchertest.cpp ../fetch/isbndbfetcher.cpp ) ecm_mark_nongui_executable(isbndbfetchertest) add_test(isbndbfetchertest isbndbfetchertest) ecm_mark_as_test(isbndbfetchertest) TARGET_LINK_LIBRARIES(isbndbfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(kinofetchertest kinofetchertest.cpp abstractfetchertest.cpp ../fetch/kinofetcher.cpp ) ecm_mark_nongui_executable(kinofetchertest) add_test(kinofetchertest kinofetchertest) ecm_mark_as_test(kinofetchertest) TARGET_LINK_LIBRARIES(kinofetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(kinopoiskfetchertest kinopoiskfetchertest.cpp abstractfetchertest.cpp ../fetch/kinopoiskfetcher.cpp ) ecm_mark_nongui_executable(kinopoiskfetchertest) add_test(kinopoiskfetchertest kinopoiskfetchertest) ecm_mark_as_test(kinopoiskfetchertest) TARGET_LINK_LIBRARIES(kinopoiskfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(kinoteatrfetchertest kinoteatrfetchertest.cpp abstractfetchertest.cpp ../fetch/kinoteatrfetcher.cpp ) ecm_mark_nongui_executable(kinoteatrfetchertest) add_test(kinoteatrfetchertest kinoteatrfetchertest) ecm_mark_as_test(kinoteatrfetchertest) TARGET_LINK_LIBRARIES(kinoteatrfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(mobygamesfetchertest mobygamesfetchertest.cpp abstractfetchertest.cpp ../fetch/mobygamesfetcher.cpp ) ecm_mark_nongui_executable(mobygamesfetchertest) add_test(mobygamesfetchertest mobygamesfetchertest) ecm_mark_as_test(mobygamesfetchertest) TARGET_LINK_LIBRARIES(mobygamesfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(moviemeterfetchertest moviemeterfetchertest.cpp abstractfetchertest.cpp ../fetch/moviemeterfetcher.cpp ) ecm_mark_nongui_executable(moviemeterfetchertest) add_test(moviemeterfetchertest moviemeterfetchertest) ecm_mark_as_test(moviemeterfetchertest) TARGET_LINK_LIBRARIES(moviemeterfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(mrlookupfetchertest mrlookupfetchertest.cpp abstractfetchertest.cpp ../fetch/mrlookupfetcher.cpp ../translators/bibteximporter.cpp ) ecm_mark_nongui_executable(mrlookupfetchertest) add_test(mrlookupfetchertest mrlookupfetchertest) ecm_mark_as_test(mrlookupfetchertest) TARGET_LINK_LIBRARIES(mrlookupfetchertest fetcherstest ${TELLICO_BTPARSE_LIBS} ${TELLICO_TEST_LIBS}) add_executable(musicbrainzfetchertest musicbrainzfetchertest.cpp abstractfetchertest.cpp ../fetch/musicbrainzfetcher.cpp ) ecm_mark_nongui_executable(musicbrainzfetchertest) add_test(musicbrainzfetchertest musicbrainzfetchertest) ecm_mark_as_test(musicbrainzfetchertest) TARGET_LINK_LIBRARIES(musicbrainzfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(openlibraryfetchertest openlibraryfetchertest.cpp abstractfetchertest.cpp ../fetch/openlibraryfetcher.cpp ) ecm_mark_nongui_executable(openlibraryfetchertest) add_test(openlibraryfetchertest openlibraryfetchertest) ecm_mark_as_test(openlibraryfetchertest) TARGET_LINK_LIBRARIES(openlibraryfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(omdbfetchertest omdbfetchertest.cpp abstractfetchertest.cpp ../fetch/omdbfetcher.cpp ) ecm_mark_nongui_executable(omdbfetchertest) add_test(omdbfetchertest omdbfetchertest) ecm_mark_as_test(omdbfetchertest) TARGET_LINK_LIBRARIES(omdbfetchertest fetcherstest translatorstest ${TELLICO_TEST_LIBS}) add_executable(springerfetchertest springerfetchertest.cpp abstractfetchertest.cpp ../fetch/springerfetcher.cpp ) ecm_mark_nongui_executable(springerfetchertest) add_test(springerfetchertest springerfetchertest) ecm_mark_as_test(springerfetchertest) TARGET_LINK_LIBRARIES(springerfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(srufetchertest srufetchertest.cpp abstractfetchertest.cpp ) ecm_mark_nongui_executable(srufetchertest) add_test(srufetchertest srufetchertest) ecm_mark_as_test(srufetchertest) # Sonnet needed for gui/lineedit TARGET_LINK_LIBRARIES(srufetchertest fetcherstest ${TELLICO_TEST_LIBS} ) add_executable(thegamesdbfetchertest thegamesdbfetchertest.cpp abstractfetchertest.cpp ../fetch/thegamesdbfetcher.cpp ) ecm_mark_nongui_executable(thegamesdbfetchertest) add_test(thegamesdbfetchertest thegamesdbfetchertest) ecm_mark_as_test(thegamesdbfetchertest) TARGET_LINK_LIBRARIES(thegamesdbfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(themoviedbfetchertest themoviedbfetchertest.cpp abstractfetchertest.cpp ../fetch/themoviedbfetcher.cpp ) ecm_mark_nongui_executable(themoviedbfetchertest) add_test(themoviedbfetchertest themoviedbfetchertest) ecm_mark_as_test(themoviedbfetchertest) TARGET_LINK_LIBRARIES(themoviedbfetchertest fetcherstest translatorstest ${TELLICO_TEST_LIBS}) add_executable(videogamegeekfetchertest videogamegeekfetchertest.cpp abstractfetchertest.cpp ../fetch/videogamegeekfetcher.cpp ) ecm_mark_nongui_executable(videogamegeekfetchertest) add_test(videogamegeekfetchertest videogamegeekfetchertest) ecm_mark_as_test(videogamegeekfetchertest) TARGET_LINK_LIBRARIES(videogamegeekfetchertest fetcherstest ${TELLICO_TEST_LIBS}) add_executable(vndbfetchertest vndbfetchertest.cpp abstractfetchertest.cpp ../fetch/vndbfetcher.cpp ) ecm_mark_nongui_executable(vndbfetchertest) add_test(vndbfetchertest vndbfetchertest) ecm_mark_as_test(vndbfetchertest) TARGET_LINK_LIBRARIES(vndbfetchertest fetcherstest ${TELLICO_TEST_LIBS}) IF( Yaz_FOUND ) add_executable(z3950fetchertest z3950fetchertest.cpp abstractfetchertest.cpp ../fetch/z3950fetcher.cpp ../fetch/z3950connection.cpp ../translators/grs1importer.cpp ../translators/adsimporter.cpp ) ecm_mark_nongui_executable(z3950fetchertest) add_test(z3950fetchertest z3950fetchertest) ecm_mark_as_test(z3950fetchertest) TARGET_LINK_LIBRARIES(z3950fetchertest fetcherstest ${Yaz_LIBRARIES} ${TELLICO_TEST_LIBS} ) ENDIF( Yaz_FOUND ) ENDIF(BUILD_FETCHER_TESTS) diff --git a/src/tests/amazonfetchertest.cpp b/src/tests/amazonfetchertest.cpp index f69ebcc9..c7d17de2 100644 --- a/src/tests/amazonfetchertest.cpp +++ b/src/tests/amazonfetchertest.cpp @@ -1,389 +1,642 @@ /*************************************************************************** Copyright (C) 2010-2011 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #undef QT_NO_CAST_FROM_ASCII #include "amazonfetchertest.h" #include "../fetch/amazonfetcher.h" +#include "../fetch/amazonrequest.h" +#include "../fetch/messagelogger.h" #include "../collections/bookcollection.h" #include "../collections/musiccollection.h" #include "../collections/videocollection.h" #include "../collections/gamecollection.h" #include "../collectionfactory.h" #include "../entry.h" #include "../images/imagefactory.h" -#include "../utils/datafileregistry.h" #include #include QTEST_GUILESS_MAIN( AmazonFetcherTest ) AmazonFetcherTest::AmazonFetcherTest() : AbstractFetcherTest(), m_hasConfigFile(false) , m_config(QFINDTESTDATA("tellicotest_private.config"), KConfig::SimpleConfig) { } void AmazonFetcherTest::initTestCase() { Tellico::RegisterCollection registerBook(Tellico::Data::Collection::Book, "book"); Tellico::RegisterCollection registerMusic(Tellico::Data::Collection::Album, "music"); Tellico::RegisterCollection registerVideo(Tellico::Data::Collection::Video, "mvideo"); Tellico::RegisterCollection registerGame(Tellico::Data::Collection::Game, "game"); - // since we use an XSL file - Tellico::DataFileRegistry::self()->addDataLocation(QFINDTESTDATA("../../xslt/amazon2tellico.xsl")); Tellico::ImageFactory::init(); m_hasConfigFile = QFile::exists(QFINDTESTDATA("tellicotest_private.config")); QHash practicalRdf; practicalRdf.insert(QStringLiteral("title"), QStringLiteral("Practical RDF")); practicalRdf.insert(QStringLiteral("isbn"), QStringLiteral("0-596-00263-7")); practicalRdf.insert(QStringLiteral("author"), QStringLiteral("Shelley Powers")); practicalRdf.insert(QStringLiteral("binding"), QStringLiteral("Paperback")); practicalRdf.insert(QStringLiteral("publisher"), QStringLiteral("O'Reilly Media")); practicalRdf.insert(QStringLiteral("pages"), QStringLiteral("331")); QHash gloryRevealed; gloryRevealed.insert(QStringLiteral("title"), QStringLiteral("Glory Revealed II")); gloryRevealed.insert(QStringLiteral("medium"), QStringLiteral("Compact Disc")); // gloryRevealed.insert(QStringLiteral("artist"), QStringLiteral("Various Artists")); gloryRevealed.insert(QStringLiteral("label"), QStringLiteral("Reunion")); gloryRevealed.insert(QStringLiteral("year"), QStringLiteral("2009")); QHash incredibles; incredibles.insert(QStringLiteral("title"), QStringLiteral("Incredibles")); incredibles.insert(QStringLiteral("medium"), QStringLiteral("DVD")); // incredibles.insert(QStringLiteral("certification"), QStringLiteral("PG (USA)")); // incredibles.insert(QStringLiteral("studio"), QStringLiteral("Walt Disney Home Entertainment")); // incredibles.insert(QStringLiteral("year"), QStringLiteral("2004")); incredibles.insert(QStringLiteral("widescreen"), QStringLiteral("true")); incredibles.insert(QStringLiteral("director"), QStringLiteral("Brad Bird; Bud Luckey; Roger Gould")); QHash pacteDesLoups; pacteDesLoups.insert(QStringLiteral("title"), QStringLiteral("Le Pacte des Loups")); pacteDesLoups.insert(QStringLiteral("medium"), QStringLiteral("Blu-ray")); // pacteDesLoups.insert(QStringLiteral("region"), QStringLiteral("Region 2")); pacteDesLoups.insert(QStringLiteral("studio"), QStringLiteral("StudioCanal")); pacteDesLoups.insert(QStringLiteral("year"), QStringLiteral("2001")); pacteDesLoups.insert(QStringLiteral("director"), QStringLiteral("Christophe Gans")); // pacteDesLoups.insert(QStringLiteral("format"), QStringLiteral("PAL")); QHash petitPrinceCN; petitPrinceCN.insert(QStringLiteral("title"), QStringLiteral("小王子(65周年纪念版)")); petitPrinceCN.insert(QStringLiteral("author"), QStringLiteral("圣埃克絮佩里 (Saint-Exupery)")); m_fieldValues.insert(QStringLiteral("practicalRdf"), practicalRdf); m_fieldValues.insert(QStringLiteral("gloryRevealed"), gloryRevealed); m_fieldValues.insert(QStringLiteral("incredibles"), incredibles); m_fieldValues.insert(QStringLiteral("pacteDesLoups"), pacteDesLoups); m_fieldValues.insert(QStringLiteral("petitPrinceCN"), petitPrinceCN); } void AmazonFetcherTest::testTitle() { + return; // re-enable if/when Amazon searches are not so heavily throttled QFETCH(QString, locale); QFETCH(int, collType); QFETCH(QString, searchValue); QFETCH(QString, resultName); QString groupName = QStringLiteral("Amazon ") + locale; if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { QSKIP("This test requires a config file with Amazon settings.", SkipAll); } KConfigGroup cg(&m_config, groupName); Tellico::Fetch::FetchRequest request(collType, Tellico::Fetch::Title, searchValue); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AmazonFetcher(this)); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH(fetcher, request); QVERIFY(!results.isEmpty()); Tellico::Data::EntryPtr entry = results.at(0); QHashIterator i(m_fieldValues.value(resultName)); while(i.hasNext()) { i.next(); // a known bug is CA video titles result in music results, so only title matches if(i.key() != QStringLiteral("title")) { QEXPECT_FAIL("CA video title", "CA video titles show music results for some reason", Continue); } QString result = entry->field(i.key()).toLower(); // several titles have edition info in the title if(collType == Tellico::Data::Collection::Video && i.key() == QStringLiteral("title") && (locale == QStringLiteral("CA") || locale == QStringLiteral("FR") || locale == QStringLiteral("ES") || locale == QStringLiteral("CN") || locale == QStringLiteral("IT") || locale == QStringLiteral("DE"))) { QVERIFY2(result.contains(i.value(), Qt::CaseInsensitive), qPrintable(i.key())); } else if(collType == Tellico::Data::Collection::Video && i.key() == QStringLiteral("year") && locale == QStringLiteral("FR")) { // france has no year for movie QCOMPARE(result, QString()); } else if(collType == Tellico::Data::Collection::Video && i.key() == QStringLiteral("medium") && (locale == QStringLiteral("ES") || locale == QStringLiteral("IT"))) { // ES and IT think it's a DVD QCOMPARE(result, QStringLiteral("dvd")); } else if(i.key() == QStringLiteral("pages") && (locale == QStringLiteral("UK") || locale == QStringLiteral("CA"))) { // UK and CA have different page count QCOMPARE(result, QStringLiteral("352")); } else if((i.key() == QStringLiteral("director") || i.key() == QStringLiteral("studio") || i.key() == QStringLiteral("year")) && (locale == QStringLiteral("ES") || locale == QStringLiteral("IT"))) { // ES and IT have no director or studio or year info QCOMPARE(result, QString()); } else { QCOMPARE(result, i.value().toLower()); } } QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); } void AmazonFetcherTest::testTitle_data() { QTest::addColumn("locale"); QTest::addColumn("collType"); QTest::addColumn("searchValue"); QTest::addColumn("resultName"); QTest::newRow("US book title") << QStringLiteral("US") << static_cast(Tellico::Data::Collection::Book) << QStringLiteral("Practical RDF") << QStringLiteral("practicalRdf"); QTest::newRow("UK book title") << QStringLiteral("UK") << static_cast(Tellico::Data::Collection::Book) << QStringLiteral("Practical RDF") << QStringLiteral("practicalRdf"); // QTest::newRow("DE") << QString::fromLatin1("DE"); // QTest::newRow("JP") << QString::fromLatin1("JP"); // QTest::newRow("FR") << QString::fromLatin1("FR"); QTest::newRow("CA book title") << QStringLiteral("CA") << static_cast(Tellico::Data::Collection::Book) << QStringLiteral("Practical RDF") << QStringLiteral("practicalRdf"); QTest::newRow("CN book title") << QStringLiteral("CN") << static_cast(Tellico::Data::Collection::Book) << QStringLiteral("小王子(65周年纪念版)") << QStringLiteral("petitPrinceCN"); // a known bug is CA video titles result in music results, so only title matches // QTest::newRow("CA video title") << QString::fromLatin1("CA") // << static_cast(Tellico::Data::Collection::Video) // << QString::fromLatin1("Le Pacte des Loups") // << QString::fromLatin1("pacteDesLoups"); QTest::newRow("FR video title") << QStringLiteral("FR") << static_cast(Tellico::Data::Collection::Video) << QStringLiteral("Le Pacte des Loups") << QStringLiteral("pacteDesLoups"); QTest::newRow("ES video title") << QStringLiteral("ES") << static_cast(Tellico::Data::Collection::Video) << QStringLiteral("Le Pacte des Loups") << QStringLiteral("pacteDesLoups"); QTest::newRow("IT video title") << QStringLiteral("IT") << static_cast(Tellico::Data::Collection::Video) << QStringLiteral("Le Pacte des Loups") << QStringLiteral("pacteDesLoups"); } void AmazonFetcherTest::testTitleVideoGame() { + return; // re-enable if/when Amazon searches are not so heavily throttled QString groupName = QStringLiteral("Amazon US"); if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { QSKIP("This test requires a config file with Amazon settings.", SkipAll); } KConfigGroup cg(&m_config, groupName); Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Game, Tellico::Fetch::Title, QStringLiteral("Ghostbusters Story Pack - LEGO Dimensions")); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AmazonFetcher(this)); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH(fetcher, request); QVERIFY(!results.isEmpty()); Tellico::Data::EntryPtr entry = results.at(0); QVERIFY(entry); QCOMPARE(entry->field("title"), QStringLiteral("Ghostbusters Story Pack - LEGO Dimensions")); QCOMPARE(entry->field("publisher"), QStringLiteral("Warner Home Video - Games")); // the E10+ ESRB rating was added to Tellico in 2017 in version 3.0.1 QCOMPARE(entry->field("certification"), QStringLiteral("Everyone 10+")); } void AmazonFetcherTest::testIsbn() { + return; // re-enable if/when Amazon searches are not so heavily throttled QFETCH(QString, locale); QFETCH(QString, searchValue); QFETCH(QString, resultName); QString groupName = QStringLiteral("Amazon ") + locale; if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { QSKIP("This test requires a config file with Amazon settings.", SkipAll); } KConfigGroup cg(&m_config, groupName); // also testing multiple values Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Book, Tellico::Fetch::ISBN, searchValue); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AmazonFetcher(this)); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH(fetcher, request); QCOMPARE(results.size(), 2); Tellico::Data::EntryPtr entry = results.at(0); QHashIterator i(m_fieldValues.value(resultName)); while(i.hasNext()) { i.next(); QString result = entry->field(i.key()).toLower(); if(i.key() == QStringLiteral("pages") && (locale == QStringLiteral("UK") || locale == QStringLiteral("CA"))) { // UK and CA have different page count QCOMPARE(result, QStringLiteral("352")); } else { QCOMPARE(result, i.value().toLower()); } } QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); } void AmazonFetcherTest::testIsbn_data() { QTest::addColumn("locale"); QTest::addColumn("searchValue"); QTest::addColumn("resultName"); QTest::newRow("US isbn") << QStringLiteral("US") << QStringLiteral("0-596-00263-7; 978-1-59059-831-3") << QStringLiteral("practicalRdf"); QTest::newRow("UK isbn") << QStringLiteral("UK") << QStringLiteral("0-596-00263-7; 978-1-59059-831-3") << QStringLiteral("practicalRdf"); // QTest::newRow("DE") << QString::fromLatin1("DE"); // QTest::newRow("JP") << QString::fromLatin1("JP"); // QTest::newRow("FR") << QString::fromLatin1("FR"); QTest::newRow("CA isbn") << QStringLiteral("CA") << QStringLiteral("0-596-00263-7; 978-1-59059-831-3") << QStringLiteral("practicalRdf"); /* QTest::newRow("CN isbn") << QString::fromLatin1("CN") << QString::fromLatin1("7511305202") << QString::fromLatin1("petitPrinceCN"); */ } void AmazonFetcherTest::testUpc() { + return; // re-enable if/when Amazon searches are not so heavily throttled QFETCH(QString, locale); QFETCH(int, collType); QFETCH(QString, searchValue); QFETCH(QString, resultName); QString groupName = QStringLiteral("Amazon ") + locale; if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { QSKIP("This test requires a config file with Amazon settings.", SkipAll); } KConfigGroup cg(&m_config, groupName); Tellico::Fetch::FetchRequest request(collType, Tellico::Fetch::UPC, searchValue); Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AmazonFetcher(this)); fetcher->readConfig(cg, cg.name()); Tellico::Data::EntryList results = DO_FETCH(fetcher, request); QVERIFY(!results.isEmpty()); Tellico::Data::EntryPtr entry = results.at(0); QHashIterator i(m_fieldValues.value(resultName)); while(i.hasNext()) { i.next(); QString result = entry->field(i.key()).toLower(); // exception for UK label different than US // and FR title having edition info if((i.key() == QStringLiteral("label") && locale == QStringLiteral("UK")) || (i.key() == QStringLiteral("title"))) { QVERIFY(result.contains(i.value(), Qt::CaseInsensitive)); } else if(i.key() == QStringLiteral("year") && locale == QStringLiteral("FR")) { // france has no year for movie QCOMPARE(result, QString()); } else { QCOMPARE(result, i.value().toLower()); } } QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); if(collType == Tellico::Data::Collection::Album) { QVERIFY(!entry->field(QStringLiteral("genre")).isEmpty()); QVERIFY(!entry->field(QStringLiteral("track")).isEmpty()); } else if(collType == Tellico::Data::Collection::Video) { QVERIFY(!entry->field(QStringLiteral("cast")).isEmpty()); } } void AmazonFetcherTest::testUpc_data() { QTest::addColumn("locale"); QTest::addColumn("collType"); QTest::addColumn("searchValue"); QTest::addColumn("resultName"); QTest::newRow("US music upc") << QStringLiteral("US") << static_cast(Tellico::Data::Collection::Album) << QStringLiteral("602341013727") << QStringLiteral("gloryRevealed"); // non-US should work with or without the initial 0 country code QTest::newRow("UK music upc1") << QStringLiteral("UK") << static_cast(Tellico::Data::Collection::Album) << QStringLiteral("602341013727") << QStringLiteral("gloryRevealed"); QTest::newRow("UK music upc2") << QStringLiteral("UK") << static_cast(Tellico::Data::Collection::Album) << QStringLiteral("0602341013727") << QStringLiteral("gloryRevealed"); QTest::newRow("CA music upc") << QStringLiteral("CA") << static_cast(Tellico::Data::Collection::Album) << QStringLiteral("0602341013727") << QStringLiteral("gloryRevealed"); QTest::newRow("US movie upc") << QStringLiteral("US") << static_cast(Tellico::Data::Collection::Video) << QStringLiteral("786936244250") << QStringLiteral("incredibles"); QTest::newRow("UK movie upc") << QStringLiteral("UK") << static_cast(Tellico::Data::Collection::Video) << QStringLiteral("0786936244250") << QStringLiteral("incredibles"); QTest::newRow("CA movie upc") << QStringLiteral("CA") << static_cast(Tellico::Data::Collection::Video) << QStringLiteral("0786936244250") << QStringLiteral("incredibles"); QTest::newRow("FR movie upc") << QStringLiteral("FR") << static_cast(Tellico::Data::Collection::Video) << QStringLiteral("5050582560985") << QStringLiteral("pacteDesLoups"); } + +void AmazonFetcherTest::testRequest() { + // from aws-sig-v4-test-suite/post-vanilla + Tellico::Fetch::AmazonRequest req("AKIDEXAMPLE", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"); + + req.setHost("example.amazonaws.com"); + req.m_headers.insert("host", req.m_host); + req.m_amzDate = "20150830T123600Z"; + req.m_headers.insert("x-amz-date", req.m_amzDate); + req.m_path = "/"; + QByteArray res1("POST\n/\n\nhost:example.amazonaws.com\nx-amz-date:20150830T123600Z\n\nhost;x-amz-date\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + QCOMPARE(req.prepareCanonicalRequest(""), res1); + + req.m_region = "us-east-1"; + req.m_service = "service"; + QByteArray res2("AWS4-HMAC-SHA256\n20150830T123600Z\n20150830/us-east-1/service/aws4_request\n553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87"); + QCOMPARE(req.prepareStringToSign(res1), res2); + + QByteArray res3("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b"); + QCOMPARE(req.buildAuthorizationString(req.calculateSignature(res2)), res3); + + QByteArray res4("com.amazon.paapi5.v1.servicev1.SearchItems"); + QCOMPARE(req.targetOperation(), res4); +} + +void AmazonFetcherTest::testPayload() { + QString groupName = QStringLiteral("Amazon US"); + if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { + QSKIP("This test requires a config file with Amazon settings.", SkipAll); + } + KConfigGroup cg(&m_config, groupName); + + Tellico::Fetch::AmazonFetcher* fetcher = new Tellico::Fetch::AmazonFetcher(this); + fetcher->readConfig(cg, cg.name()); + + Tellico::Fetch::FetchRequest req(Tellico::Data::Collection::Book, Tellico::Fetch::UPC, "717356278525"); + QByteArray payload = fetcher->requestPayload(req); + QByteArray res1("{\ +\"Keywords\":\"717356278525\",\ +\"Operation\":\"SearchItems\",\ +\"PartnerTag\":\"tellico-20\",\ +\"PartnerType\":\"Associates\",\ +\"Resources\":[\"ItemInfo.Title\",\"ItemInfo.ContentInfo\",\"ItemInfo.ByLineInfo\",\"ItemInfo.TechnicalInfo\",\"ItemInfo.ExternalIds\",\"ItemInfo.ManufactureInfo\",\"Images.Primary.Medium\"],\ +\"SearchIndex\":\"Books\",\ +\"SortBy\":\"Relevance\"\ +}"); + QCOMPARE(payload.right(100), res1.right(100)); + QCOMPARE(payload, res1); +} + +void AmazonFetcherTest::testError() { + Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Book, Tellico::Fetch::UPC, "717356278525"); + Tellico::Fetch::AmazonFetcher* f = new Tellico::Fetch::AmazonFetcher(this); + Tellico::Fetch::Fetcher::Ptr fetcher(f); + + Tellico::Fetch::MessageLogger* logger = new Tellico::Fetch::MessageLogger; + f->setMessageHandler(logger); + f->m_site = Tellico::Fetch::AmazonFetcher::US; + f->m_accessKey = QStringLiteral("test"); + f->m_secretKey = QStringLiteral("test"); + f->m_testResultsFile = QFINDTESTDATA("data/amazon-paapi-error1.json"); + + Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); + QVERIFY(!logger->errorList.isEmpty()); + QCOMPARE(logger->errorList[0], QStringLiteral("The Access Key ID or security token included in the request is invalid.")); +} + +void AmazonFetcherTest::testUpc1() { + QString groupName = QStringLiteral("Amazon US"); + if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { + QSKIP("This test requires a config file with Amazon settings.", SkipAll); + } + KConfigGroup cg(&m_config, groupName); + + Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Book, Tellico::Fetch::UPC, "717356278525"); + Tellico::Fetch::AmazonFetcher* f = new Tellico::Fetch::AmazonFetcher(this); + Tellico::Fetch::Fetcher::Ptr fetcher(f); + fetcher->readConfig(cg, cg.name()); + + f->m_testResultsFile = QFINDTESTDATA("data/amazon-paapi-upc1.json"); + + Tellico::Data::EntryList results = DO_FETCH(fetcher, request); + + QCOMPARE(results.size(), 1); + + Tellico::Data::EntryPtr entry = results.at(0); + QVERIFY(entry); + QCOMPARE(entry->field("title"), QStringLiteral("Harry Potter Paperback Box Set (Books 1-7)")); + QCOMPARE(entry->field("isbn"), QStringLiteral("0545162076")); + QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); + QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); +} + +void AmazonFetcherTest::testUpc2() { + QString groupName = QStringLiteral("Amazon US"); + if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { + QSKIP("This test requires a config file with Amazon settings.", SkipAll); + } + KConfigGroup cg(&m_config, groupName); + + Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Book, Tellico::Fetch::UPC, "717356278525; 842776102270"); + Tellico::Fetch::AmazonFetcher* f = new Tellico::Fetch::AmazonFetcher(this); + Tellico::Fetch::Fetcher::Ptr fetcher(f); + fetcher->readConfig(cg, cg.name()); + + QByteArray payload = f->requestPayload(request); + // verify the format of the multiple UPC keyword + QVERIFY(payload.contains("\"Keywords\":\"717356278525|842776102270\"")); + + f->m_testResultsFile = QFINDTESTDATA("data/amazon-paapi-upc2.json"); + + Tellico::Data::EntryList results = DO_FETCH(fetcher, request); + + QCOMPARE(results.size(), 2); + + Tellico::Data::EntryPtr entry = results.at(0); + QVERIFY(entry); + QCOMPARE(entry->field("title"), QStringLiteral("Harry Potter Paperback Box Set (Books 1-7)")); + QCOMPARE(entry->field("isbn"), QStringLiteral("0545162076")); + QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); + QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); +} + +// from https://github.com/dkam/paapi/blob/master/test/data/get_item_no_author.json +void AmazonFetcherTest::testBasicBook() { + QString groupName = QStringLiteral("Amazon UK"); + if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { + QSKIP("This test requires a config file with Amazon settings.", SkipAll); + } + KConfigGroup cg(&m_config, groupName); + + Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Book, Tellico::Fetch::ISBN, "1921878657"); + Tellico::Fetch::AmazonFetcher* f = new Tellico::Fetch::AmazonFetcher(this); + Tellico::Fetch::Fetcher::Ptr fetcher(f); + fetcher->readConfig(cg, cg.name()); + + f->m_testResultsFile = QFINDTESTDATA("data/amazon-paapi-book.json"); + + Tellico::Data::EntryList results = DO_FETCH(fetcher, request); + + QCOMPARE(results.size(), 1); + + Tellico::Data::EntryPtr entry = results.at(0); + QVERIFY(entry); + QCOMPARE(entry->field("title"), QStringLiteral("Muscle Car Mania: 100 legendary Australian motoring stories")); + QCOMPARE(entry->field("author"), QStringLiteral("No Author")); + QCOMPARE(entry->field("publisher"), QStringLiteral("Rockpool Publishing")); + QCOMPARE(entry->field("edition"), QStringLiteral("Slp")); + QCOMPARE(entry->field("binding"), QStringLiteral("Paperback")); + QCOMPARE(entry->field("series"), QStringLiteral("Motoring Series")); + QCOMPARE(entry->field("isbn"), QStringLiteral("1921878657")); + QCOMPARE(entry->field("pages"), QStringLiteral("224")); + QCOMPARE(entry->field("language"), QStringLiteral("English")); + QCOMPARE(entry->field("pub_year"), QStringLiteral("2013")); + QCOMPARE(entry->field("amazon"), QStringLiteral("https://www.amazon.com/dp/1921878657?tag=bookie09-20&linkCode=ogi&th=1&psc=1")); + QVERIFY(entry->field(QStringLiteral("cover")).isEmpty()); // because image size as NoImage +} + +void AmazonFetcherTest::testTitleParsing() { + Tellico::Data::CollPtr coll(new Tellico::Data::VideoCollection(true)); + Tellico::Data::EntryPtr entry(new Tellico::Data::Entry(coll)); + entry->setField(QStringLiteral("title"), QStringLiteral("title1 [DVD] (Widescreen) (NTSC) [Region 1]")); + + Tellico::Fetch::AmazonFetcher* f = new Tellico::Fetch::AmazonFetcher(this); + Tellico::Fetch::Fetcher::Ptr fetcher(f); + + f->parseTitle(entry); + // the fetcher leaves widescreen in the title but adds the field value + QCOMPARE(entry->field("title"), QStringLiteral("title1 (Widescreen)")); + QCOMPARE(entry->field("medium"), QStringLiteral("DVD")); + QCOMPARE(entry->field("widescreen"), QStringLiteral("true")); + QCOMPARE(entry->field("format"), QStringLiteral("NTSC")); + QCOMPARE(entry->field("region"), QStringLiteral("Region 1")); +} + +// from https://github.com/utekaravinash/gopaapi5/blob/master/_response/search_items.json +void AmazonFetcherTest::testSearchItems_gopaapi5() { + QString groupName = QStringLiteral("Amazon UK"); + if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { + QSKIP("This test requires a config file with Amazon settings.", SkipAll); + } + KConfigGroup cg(&m_config, groupName); + + Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Book, Tellico::Fetch::ISBN, "1921878657"); + Tellico::Fetch::AmazonFetcher* f = new Tellico::Fetch::AmazonFetcher(this); + Tellico::Fetch::Fetcher::Ptr fetcher(f); + fetcher->readConfig(cg, cg.name()); + + f->m_testResultsFile = QFINDTESTDATA("data/amazon-paapi-search-items-gopaapi5.json"); + + Tellico::Data::EntryList results = DO_FETCH(fetcher, request); + + QCOMPARE(results.size(), 3); + + Tellico::Data::EntryPtr entry = results.at(0); + QVERIFY(entry); + QCOMPARE(entry->field("title"), QStringLiteral("Go Programming Language, The")); + QCOMPARE(entry->field("author"), QStringLiteral("Donovan, Alan A. A.")); + QCOMPARE(entry->field("publisher"), QStringLiteral("Addison-Wesley Professional")); + QCOMPARE(entry->field("edition"), QStringLiteral("1")); + QCOMPARE(entry->field("binding"), QStringLiteral("Paperback")); + QCOMPARE(entry->field("series"), QStringLiteral("Addison-Wesley Professional Computing Series")); + QCOMPARE(entry->field("isbn"), QStringLiteral("0134190440")); + QCOMPARE(entry->field("pages"), QStringLiteral("398")); + QCOMPARE(entry->field("language"), QStringLiteral("English")); + QCOMPARE(entry->field("pub_year"), QStringLiteral("2015")); + QCOMPARE(entry->field("amazon"), QStringLiteral("https://www.amazon.com/dp/0134190440?tag=associateTag-20&linkCode=osi&th=1&psc=1")); + QVERIFY(entry->field(QStringLiteral("cover")).isEmpty()); // because image size as NoImage + + entry = results.at(2); + QVERIFY(entry); + QCOMPARE(entry->field("title"), QStringLiteral("Black Hat Go: Go Programming For Hackers and Pentesters")); + QCOMPARE(entry->field("author"), QStringLiteral("Steele, Tom; Patten, Chris; Kottmann, Dan")); +} + +// from https://github.com/utekaravinash/gopaapi5/blob/master/_response/get_items.json +void AmazonFetcherTest::testGetItems_gopaapi5() { + QString groupName = QStringLiteral("Amazon UK"); + if(!m_hasConfigFile || !m_config.hasGroup(groupName)) { + QSKIP("This test requires a config file with Amazon settings.", SkipAll); + } + KConfigGroup cg(&m_config, groupName); + + Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Book, Tellico::Fetch::ISBN, "1921878657"); + Tellico::Fetch::AmazonFetcher* f = new Tellico::Fetch::AmazonFetcher(this); + Tellico::Fetch::Fetcher::Ptr fetcher(f); + fetcher->readConfig(cg, cg.name()); + + f->m_testResultsFile = QFINDTESTDATA("data/amazon-paapi-get-items-gopaapi5.json"); + + Tellico::Data::EntryList results = DO_FETCH(fetcher, request); + + QCOMPARE(results.size(), 2); + + Tellico::Data::EntryPtr entry = results.at(0); + QVERIFY(entry); + QVERIFY(entry->collection()); + QCOMPARE(entry->collection()->type(), Tellico::Data::Collection::Book); + QCOMPARE(entry->field("title"), QStringLiteral("Light on Yoga: The Bible of Modern Yoga")); + QCOMPARE(entry->field("author"), QStringLiteral("B. K. S. Iyengar")); + QCOMPARE(entry->field("publisher"), QStringLiteral("Schocken")); + QCOMPARE(entry->field("edition"), QStringLiteral("Revised")); + QCOMPARE(entry->field("binding"), QStringLiteral("Paperback")); + QCOMPARE(entry->field("isbn"), QStringLiteral("0805210318")); + QCOMPARE(entry->field("pages"), QStringLiteral("544")); + QCOMPARE(entry->field("language"), QStringLiteral("English")); + QCOMPARE(entry->field("pub_year"), QStringLiteral("1979")); // it's a 1995 revised edition of a 1979 publication apparently + QCOMPARE(entry->field("amazon"), QStringLiteral("https://www.amazon.com/dp/0805210318?tag=associateTag-20&linkCode=ogi&th=1&psc=1")); + QVERIFY(entry->field(QStringLiteral("cover")).isEmpty()); // because image size as NoImage +} diff --git a/src/tests/amazonfetchertest.h b/src/tests/amazonfetchertest.h index 25ac5ad9..dc7148bc 100644 --- a/src/tests/amazonfetchertest.h +++ b/src/tests/amazonfetchertest.h @@ -1,53 +1,62 @@ /*************************************************************************** Copyright (C) 2010-2011 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef AMAZONFETCHERTEST_H #define AMAZONFETCHERTEST_H #include "abstractfetchertest.h" #include class AmazonFetcherTest : public AbstractFetcherTest { Q_OBJECT public: AmazonFetcherTest(); private Q_SLOTS: void initTestCase(); void testTitle(); void testTitle_data(); void testTitleVideoGame(); void testIsbn(); void testIsbn_data(); void testUpc(); void testUpc_data(); + void testRequest(); + void testPayload(); + void testError(); + void testUpc1(); + void testUpc2(); + void testBasicBook(); + void testTitleParsing(); + void testSearchItems_gopaapi5(); + void testGetItems_gopaapi5(); private: bool m_hasConfigFile; KConfig m_config; QHash > m_fieldValues; }; #endif diff --git a/src/tests/data/amazon-paapi-book.json b/src/tests/data/amazon-paapi-book.json new file mode 100644 index 00000000..acbc3334 --- /dev/null +++ b/src/tests/data/amazon-paapi-book.json @@ -0,0 +1,182 @@ +{ + "ItemsResult": { + "Items": [ + { + "ASIN": "1921878657", + "DetailPageURL": "https://www.amazon.com/dp/1921878657?tag=bookie09-20&linkCode=ogi&th=1&psc=1", + "Images": { + "Primary": { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/61xe6KCK16L.jpg", + "Width": 499 + } + } + }, + "ItemInfo": { + "ByLineInfo": { + "Contributors": [ + { + "Locale": "en_US", + "Name": "No Author", + "Role": "Author" + } + ], + "Manufacturer": { + "DisplayValue": "Rockpool Publishing", + "Label": "Manufacturer", + "Locale": "en_US" + } + }, + "ContentInfo": { + "Edition": { + "DisplayValue": "Slp", + "Label": "Edition", + "Locale": "en_US" + }, + "Languages": { + "DisplayValues": [ + { + "DisplayValue": "English", + "Type": "Published" + }, + { + "DisplayValue": "English", + "Type": "Original Language" + }, + { + "DisplayValue": "English", + "Type": "Unknown" + } + ], + "Label": "Language", + "Locale": "en_US" + }, + "PagesCount": { + "DisplayValue": 224, + "Label": "NumberOfPages", + "Locale": "en_US" + }, + "PublicationDate": { + "DisplayValue": "2013-08-01T00:00:01Z", + "Label": "PublicationDate", + "Locale": "en_US" + } + }, + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "9781921878657" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "ISBNs": { + "DisplayValues": [ + "1921878657" + ], + "Label": "ISBN", + "Locale": "en_US" + } + }, + "ManufactureInfo": { + "ItemPartNumber": { + "DisplayValue": "200 colour photos", + "Label": "PartNumber", + "Locale": "en_US" + } + }, + "ProductInfo": { + "ItemDimensions": { + "Height": { + "DisplayValue": 9.0, + "Label": "Height", + "Locale": "en_US", + "Unit": "Inches" + }, + "Length": { + "DisplayValue": 9.0, + "Label": "Length", + "Locale": "en_US", + "Unit": "Inches" + }, + "Weight": { + "DisplayValue": 1.65, + "Label": "Weight", + "Locale": "en_US", + "Unit": "Pounds" + }, + "Width": { + "DisplayValue": 0.6, + "Label": "Width", + "Locale": "en_US", + "Unit": "Inches" + } + }, + "UnitCount": { + "DisplayValue": 1, + "Label": "NumberOfItems", + "Locale": "en_US" + } + }, + "TechnicalInfo": { + "Formats": { + "DisplayValues": [ + "Paperback" + ], + "Label": "Format", + "Locale": "en_GB" + } + }, + "Title": { + "DisplayValue": "Muscle Car Mania: 100 legendary Australian motoring stories (Motoring Series)", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Availability": { + "Message": "In Stock." + }, + "Condition": { + "SubCondition": { + "Value": "New" + }, + "Value": "New" + }, + "DeliveryInfo": { + "IsAmazonFulfilled": true, + "IsFreeShippingEligible": true, + "IsPrimeEligible": true + }, + "Id": "oGEK9dN87uwQuSy0Cy2FsGZgiQlK9AMvmDO0cvPSFLdVtNKcF4nDn97DsoZMfMiHzI8W4FvyXDbV25mf3XuEwD8FU9znwQ1eZXjWM0aYpY5F1QIJ0Pm44w%3D%3D", + "MerchantInfo": { + "Id": "ATVPDKIKX0DER", + "Name": "Amazon.com" + }, + "Price": { + "Amount": 10.36, + "Currency": "USD", + "DisplayAmount": "$10.36", + "Savings": { + "Amount": 11.59, + "Currency": "USD", + "DisplayAmount": "$11.59 (53%)", + "Percentage": 53 + } + }, + "SavingBasis": { + "Amount": 21.95, + "Currency": "USD", + "DisplayAmount": "$21.95" + }, + "ViolatesMAP": false + } + ] + } + } + ] + } +} diff --git a/src/tests/data/amazon-paapi-error1.json b/src/tests/data/amazon-paapi-error1.json new file mode 100644 index 00000000..47269b16 --- /dev/null +++ b/src/tests/data/amazon-paapi-error1.json @@ -0,0 +1,9 @@ +{ + "__type": "com.amazon.paapi5#UnrecognizedClientException", + "Errors": [ + { + "Code": "UnrecognizedClient", + "Message": "The Access Key ID or security token included in the request is invalid." + } + ] +} diff --git a/src/tests/data/amazon-paapi-get-items-gopaapi5.json b/src/tests/data/amazon-paapi-get-items-gopaapi5.json new file mode 100644 index 00000000..3830bd27 --- /dev/null +++ b/src/tests/data/amazon-paapi-get-items-gopaapi5.json @@ -0,0 +1,717 @@ +{ + "ItemsResult": { + "Items": [ + { + "ASIN": "0805210318", + "BrowseNodeInfo": { + "BrowseNodes": [ + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Health, Fitness & Dieting", + "DisplayName": "Health, Fitness & Dieting", + "Id": "10" + }, + "ContextFreeName": "Alternative Medicine", + "DisplayName": "Alternative Medicine", + "Id": "4696" + }, + "ContextFreeName": "Meditation", + "DisplayName": "Meditation", + "Id": "4713", + "IsRoot": false, + "SalesRank": 30 + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Health, Fitness & Dieting", + "DisplayName": "Health, Fitness & Dieting", + "Id": "10" + }, + "ContextFreeName": "Exercise & Fitness", + "DisplayName": "Exercise & Fitness", + "Id": "4645" + }, + "ContextFreeName": "Yoga", + "DisplayName": "Yoga", + "Id": "4653", + "IsRoot": false, + "SalesRank": 5 + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Religion & Spirituality", + "DisplayName": "Religion & Spirituality", + "Id": "22" + }, + "ContextFreeName": "New Age & Spirituality", + "DisplayName": "New Age & Spirituality", + "Id": "12621" + }, + "ContextFreeName": "Chakras", + "DisplayName": "Chakras", + "Id": "12631", + "IsRoot": false, + "SalesRank": 10 + } + ], + "WebsiteSalesRank": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "SalesRank": 1565 + } + }, + "DetailPageURL": "https://www.amazon.com/dp/0805210318?tag=associateTag-20&linkCode=ogi&th=1&psc=1", + "Images": { + "Primary": { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/41CnWolqViL.jpg", + "Width": 324 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/41CnWolqViL._SL160_.jpg", + "Width": 104 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/41CnWolqViL._SL75_.jpg", + "Width": 49 + } + }, + "Variants": [ + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/31vJLdB77mL.jpg", + "Width": 375 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/31vJLdB77mL._SL160_.jpg", + "Width": 120 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/31vJLdB77mL._SL75_.jpg", + "Width": 56 + } + } + ] + }, + "ItemInfo": { + "ByLineInfo": { + "Brand": { + "DisplayValue": "Schocken", + "Label": "Brand", + "Locale": "en_US" + }, + "Contributors": [ + { + "Locale": "en_US", + "Name": "B. K. S. Iyengar", + "Role": "Author", + "RoleType": "author" + }, + { + "Locale": "en_US", + "Name": "Yehudi Menuhin", + "Role": "Foreword", + "RoleType": "foreword" + } + ], + "Manufacturer": { + "DisplayValue": "Schocken", + "Label": "Manufacturer", + "Locale": "en_US" + } + }, + "Classifications": { + "Binding": { + "DisplayValue": "Paperback", + "Label": "Binding", + "Locale": "en_US" + }, + "ProductGroup": { + "DisplayValue": "Book", + "Label": "ProductGroup", + "Locale": "en_US" + } + }, + "ContentInfo": { + "Edition": { + "DisplayValue": "Revised", + "Label": "Edition", + "Locale": "en_US" + }, + "Languages": { + "DisplayValues": [ + { + "DisplayValue": "English", + "Type": "Published" + }, + { + "DisplayValue": "English", + "Type": "Original Language" + }, + { + "DisplayValue": "English", + "Type": "Unknown" + } + ], + "Label": "Language", + "Locale": "en_US" + }, + "PagesCount": { + "DisplayValue": 544, + "Label": "NumberOfPages", + "Locale": "en_US" + }, + "PublicationDate": { + "DisplayValue": "1979T", + "Label": "PublicationDate", + "Locale": "en_US" + } + }, + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "8580001044736", + "8601404412103", + "8601300502786", + "9780805210316" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "ISBNs": { + "DisplayValues": [ + "0805210318", + "9780805210316" + ], + "Label": "ISBN", + "Locale": "en_US" + } + }, + "Features": { + "DisplayValues": [ + "With complete descriptions and illustations of all the postures and breathing techniques." + ], + "Label": "Features", + "Locale": "en_US" + }, + "ManufactureInfo": { + "ItemPartNumber": { + "DisplayValue": "9780805210316", + "Label": "PartNumber", + "Locale": "en_US" + } + }, + "ProductInfo": { + "Color": { + "DisplayValue": "White", + "Label": "Color", + "Locale": "en_US" + }, + "IsAdultProduct": { + "DisplayValue": false, + "Label": "IsAdultProduct", + "Locale": "en_US" + }, + "ItemDimensions": { + "Height": { + "DisplayValue": 7.93, + "Label": "Height", + "Locale": "en_US", + "Unit": "Inches" + }, + "Length": { + "DisplayValue": 5.15, + "Label": "Length", + "Locale": "en_US", + "Unit": "Inches" + }, + "Weight": { + "DisplayValue": 1.10010668738, + "Label": "Weight", + "Locale": "en_US", + "Unit": "Pounds" + }, + "Width": { + "DisplayValue": 1.06, + "Label": "Width", + "Locale": "en_US", + "Unit": "Inches" + } + }, + "ReleaseDate": { + "DisplayValue": "1995-01-03T00:00:01Z", + "Label": "ReleaseDate", + "Locale": "en_US" + }, + "UnitCount": { + "DisplayValue": 1, + "Label": "NumberOfItems", + "Locale": "en_US" + } + }, + "Title": { + "DisplayValue": "Light on Yoga: The Bible of Modern Yoga", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Availability": { + "Message": "In Stock.", + "MinOrderQuantity": 1, + "Type": "Now" + }, + "Condition": { + "SubCondition": { + "Value": "New" + }, + "Value": "New" + }, + "DeliveryInfo": { + "IsAmazonFulfilled": true, + "IsFreeShippingEligible": true, + "IsPrimeEligible": true + }, + "Id": "a1Z9Bxb%2BApxyhGByHRFDSEzp6S0Axr2oLZWtZlajzohbj2jHRTW1cuwljnC%2BKdnB8wo50vkIX1iVMbLp3QrMOMkheLZoWngSJFVBHEmDzjo%3D", + "IsBuyBoxWinner": true, + "MerchantInfo": { + "Id": "ATVPDKIKX0DER", + "Name": "Amazon.com" + }, + "Price": { + "Amount": 13.59, + "Currency": "USD", + "DisplayAmount": "$13.59", + "Savings": { + "Amount": 8.41, + "Currency": "USD", + "DisplayAmount": "$8.41 (38%)", + "Percentage": 38 + } + }, + "ProgramEligibility": { + "IsPrimeExclusive": false, + "IsPrimePantry": false + }, + "SavingBasis": { + "Amount": 22, + "Currency": "USD", + "DisplayAmount": "$22.00" + }, + "ViolatesMAP": false + } + ], + "Summaries": [ + { + "Condition": { + "Value": "New" + }, + "HighestPrice": { + "Amount": 50.95, + "Currency": "USD", + "DisplayAmount": "$50.95" + }, + "LowestPrice": { + "Amount": 13.25, + "Currency": "USD", + "DisplayAmount": "$13.25" + }, + "OfferCount": 53 + }, + { + "Condition": { + "Value": "Used" + }, + "HighestPrice": { + "Amount": 515.4, + "Currency": "USD", + "DisplayAmount": "$515.40" + }, + "LowestPrice": { + "Amount": 3.85, + "Currency": "USD", + "DisplayAmount": "$3.85" + }, + "OfferCount": 181 + }, + { + "Condition": { + "Value": "Collectible" + }, + "HighestPrice": { + "Amount": 678, + "Currency": "USD", + "DisplayAmount": "$678.00" + }, + "LowestPrice": { + "Amount": 12.95, + "Currency": "USD", + "DisplayAmount": "$12.95" + }, + "OfferCount": 2 + } + ] + } + }, + { + "ASIN": "0892131349", + "BrowseNodeInfo": { + "BrowseNodes": [ + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Religion & Spirituality", + "DisplayName": "Religion & Spirituality", + "Id": "22" + }, + "ContextFreeName": "Hinduism", + "DisplayName": "Hinduism", + "Id": "12506" + }, + "ContextFreeName": "Sacred Hindu Writings", + "DisplayName": "Sacred Writings", + "Id": "11309340011" + }, + "ContextFreeName": "Bhagavad Gita", + "DisplayName": "Bhagavad Gita", + "Id": "12508", + "IsRoot": false, + "SalesRank": 6 + } + ], + "WebsiteSalesRank": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "SalesRank": 15006 + } + }, + "DetailPageURL": "https://www.amazon.com/dp/0892131349?tag=associateTag-20&linkCode=ogi&th=1&psc=1", + "Images": { + "Primary": { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/51iixzFhJDL.jpg", + "Width": 287 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/51iixzFhJDL._SL160_.jpg", + "Width": 92 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/51iixzFhJDL._SL75_.jpg", + "Width": 43 + } + }, + "Variants": [ + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/31zlupl4ZiL.jpg", + "Width": 375 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/31zlupl4ZiL._SL160_.jpg", + "Width": 120 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/31zlupl4ZiL._SL75_.jpg", + "Width": 56 + } + } + ] + }, + "ItemInfo": { + "ByLineInfo": { + "Contributors": [ + { + "Locale": "en_US", + "Name": "A. C. Bhaktivedanta Swami Prabhupada", + "Role": "Author", + "RoleType": "author" + } + ], + "Manufacturer": { + "DisplayValue": "Bhaktivedanta Book Trust", + "Label": "Manufacturer", + "Locale": "en_US" + } + }, + "Classifications": { + "Binding": { + "DisplayValue": "Mass Market Paperback", + "Label": "Binding", + "Locale": "en_US" + }, + "ProductGroup": { + "DisplayValue": "Book", + "Label": "ProductGroup", + "Locale": "en_US" + } + }, + "ContentInfo": { + "Edition": { + "DisplayValue": "De Luxe Ed", + "Label": "Edition", + "Locale": "en_US" + }, + "Languages": { + "DisplayValues": [ + { + "DisplayValue": "English", + "Type": "Published" + }, + { + "DisplayValue": "Sanskrit", + "Type": "Original Language" + }, + { + "DisplayValue": "English", + "Type": "Unknown" + } + ], + "Label": "Language", + "Locale": "en_US" + }, + "PagesCount": { + "DisplayValue": 703, + "Label": "NumberOfPages", + "Locale": "en_US" + }, + "PublicationDate": { + "DisplayValue": "1989-06-01T00:00:01Z", + "Label": "PublicationDate", + "Locale": "en_US" + } + }, + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "9780892131341" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "ISBNs": { + "DisplayValues": [ + "9780892131341", + "0892131349" + ], + "Label": "ISBN", + "Locale": "en_US" + } + }, + "Features": { + "DisplayValues": [ + "Most popular book", + "song of god", + "life manual", + "gives big picture of life", + "shows how to live happily from inside" + ], + "Label": "Features", + "Locale": "en_US" + }, + "ManufactureInfo": { + "ItemPartNumber": { + "DisplayValue": "colour illustrations", + "Label": "PartNumber", + "Locale": "en_US" + } + }, + "ProductInfo": { + "ItemDimensions": { + "Height": { + "DisplayValue": 7, + "Label": "Height", + "Locale": "en_US", + "Unit": "Inches" + }, + "Length": { + "DisplayValue": 4, + "Label": "Length", + "Locale": "en_US", + "Unit": "Inches" + }, + "Weight": { + "DisplayValue": 0.95, + "Label": "Weight", + "Locale": "en_US", + "Unit": "Pounds" + }, + "Width": { + "DisplayValue": 1.75, + "Label": "Width", + "Locale": "en_US", + "Unit": "Inches" + } + }, + "UnitCount": { + "DisplayValue": 1, + "Label": "NumberOfItems", + "Locale": "en_US" + } + }, + "TechnicalInfo": { + "Formats": { + "DisplayValues": [ + "Deluxe Edition" + ], + "Label": "Format", + "Locale": "en_US" + } + }, + "Title": { + "DisplayValue": "Bhagavad-Gita As It Is (Paperback)", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Availability": { + "MaxOrderQuantity": 80, + "Message": "In Stock.", + "MinOrderQuantity": 1, + "Type": "Now" + }, + "Condition": { + "SubCondition": { + "Value": "New" + }, + "Value": "New" + }, + "DeliveryInfo": { + "IsAmazonFulfilled": true, + "IsFreeShippingEligible": true, + "IsPrimeEligible": true + }, + "Id": "a1Z9Bxb%2BApyW824hgnTU1Bupb9wXmyIXzXGidNbjm2S7k%2BF0L3X9CtpcxSVGU7TLspotILDwRGaIWQhcMpgvV1jyAtjRr0UNypSGY0cSNHY%3D", + "IsBuyBoxWinner": true, + "MerchantInfo": { + "Id": "ATVPDKIKX0DER", + "Name": "Amazon.com" + }, + "Price": { + "Amount": 5.95, + "Currency": "USD", + "DisplayAmount": "$5.95" + }, + "ProgramEligibility": { + "IsPrimeExclusive": false, + "IsPrimePantry": false + }, + "ViolatesMAP": false + } + ], + "Summaries": [ + { + "Condition": { + "Value": "New" + }, + "HighestPrice": { + "Amount": 15.99, + "Currency": "USD", + "DisplayAmount": "$15.99" + }, + "LowestPrice": { + "Amount": 1.5, + "Currency": "USD", + "DisplayAmount": "$1.50" + }, + "OfferCount": 86 + }, + { + "Condition": { + "Value": "Used" + }, + "HighestPrice": { + "Amount": 15.79, + "Currency": "USD", + "DisplayAmount": "$15.79" + }, + "LowestPrice": { + "Amount": 0.1, + "Currency": "USD", + "DisplayAmount": "$0.10" + }, + "OfferCount": 301 + }, + { + "Condition": { + "Value": "Collectible" + }, + "HighestPrice": { + "Amount": 18.99, + "Currency": "USD", + "DisplayAmount": "$18.99" + }, + "LowestPrice": { + "Amount": 3.95, + "Currency": "USD", + "DisplayAmount": "$3.95" + }, + "OfferCount": 9 + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/src/tests/data/amazon-paapi-search-items-gopaapi5.json b/src/tests/data/amazon-paapi-search-items-gopaapi5.json new file mode 100644 index 00000000..d39e8cbe --- /dev/null +++ b/src/tests/data/amazon-paapi-search-items-gopaapi5.json @@ -0,0 +1,1191 @@ +{ + "SearchResult": { + "Items": [ + { + "ASIN": "0134190440", + "BrowseNodeInfo": { + "BrowseNodes": [ + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Programming Languages", + "DisplayName": "Programming Languages", + "Id": "3952", + "IsRoot": false, + "SalesRank": 35 + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Specialty Boutique", + "DisplayName": "Specialty Boutique", + "Id": "2349030011" + }, + "ContextFreeName": "Textbooks", + "DisplayName": "New, Used & Rental Textbooks", + "Id": "465600" + }, + "ContextFreeName": "Computer Science", + "DisplayName": "Computer Science", + "Id": "468204" + }, + "ContextFreeName": "Computer Programming Languages", + "DisplayName": "Programming Languages", + "Id": "491314", + "IsRoot": false, + "SalesRank": 17 + } + ], + "WebsiteSalesRank": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "SalesRank": 11005 + } + }, + "DetailPageURL": "https://www.amazon.com/dp/0134190440?tag=associateTag-20&linkCode=osi&th=1&psc=1", + "Images": { + "Primary": { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/41aSIiETPPL.jpg", + "Width": 404 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/41aSIiETPPL._SL160_.jpg", + "Width": 129 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/41aSIiETPPL._SL75_.jpg", + "Width": 61 + } + }, + "Variants": [ + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/51ul4zg-hoL.jpg", + "Width": 404 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/51ul4zg-hoL._SL160_.jpg", + "Width": 129 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/51ul4zg-hoL._SL75_.jpg", + "Width": 61 + } + }, + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/31WBfgP59cL.jpg", + "Width": 375 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/31WBfgP59cL._SL160_.jpg", + "Width": 120 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/31WBfgP59cL._SL75_.jpg", + "Width": 56 + } + } + ] + }, + "ItemInfo": { + "ByLineInfo": { + "Contributors": [ + { + "Locale": "en_US", + "Name": "Donovan, Alan A. A.", + "Role": "Author", + "RoleType": "author" + } + ], + "Manufacturer": { + "DisplayValue": "Addison-Wesley Professional", + "Label": "Manufacturer", + "Locale": "en_US" + } + }, + "Classifications": { + "Binding": { + "DisplayValue": "Paperback", + "Label": "Binding", + "Locale": "en_US" + }, + "ProductGroup": { + "DisplayValue": "Book", + "Label": "ProductGroup", + "Locale": "en_US" + } + }, + "ContentInfo": { + "Edition": { + "DisplayValue": "1", + "Label": "Edition", + "Locale": "en_US" + }, + "Languages": { + "DisplayValues": [ + { + "DisplayValue": "English", + "Type": "Published" + }, + { + "DisplayValue": "English", + "Type": "Original Language" + }, + { + "DisplayValue": "English", + "Type": "Unknown" + } + ], + "Label": "Language", + "Locale": "en_US" + }, + "PagesCount": { + "DisplayValue": 398, + "Label": "NumberOfPages", + "Locale": "en_US" + }, + "PublicationDate": { + "DisplayValue": "2015-11-05T00:00:01Z", + "Label": "PublicationDate", + "Locale": "en_US" + } + }, + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "9780134190440" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "ISBNs": { + "DisplayValues": [ + "9780134190440", + "0134190440" + ], + "Label": "ISBN", + "Locale": "en_US" + } + }, + "ManufactureInfo": { + "ItemPartNumber": { + "DisplayValue": "42375234", + "Label": "PartNumber", + "Locale": "en_US" + } + }, + "ProductInfo": { + "IsAdultProduct": { + "DisplayValue": false, + "Label": "IsAdultProduct", + "Locale": "en_US" + }, + "ItemDimensions": { + "Height": { + "DisplayValue": 1, + "Label": "Height", + "Locale": "en_US", + "Unit": "Inches" + }, + "Length": { + "DisplayValue": 9.1, + "Label": "Length", + "Locale": "en_US", + "Unit": "Inches" + }, + "Weight": { + "DisplayValue": 1.45064168396, + "Label": "Weight", + "Locale": "en_US", + "Unit": "Pounds" + }, + "Width": { + "DisplayValue": 7.3, + "Label": "Width", + "Locale": "en_US", + "Unit": "Inches" + } + }, + "ReleaseDate": { + "DisplayValue": "2015-10-26T00:00:01Z", + "Label": "ReleaseDate", + "Locale": "en_US" + }, + "UnitCount": { + "DisplayValue": 1, + "Label": "NumberOfItems", + "Locale": "en_US" + } + }, + "Title": { + "DisplayValue": "Go Programming Language, The (Addison-Wesley Professional Computing Series)", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Availability": { + "MaxOrderQuantity": 80, + "Message": "In Stock.", + "MinOrderQuantity": 1, + "Type": "Now" + }, + "Condition": { + "SubCondition": { + "Value": "New" + }, + "Value": "New" + }, + "DeliveryInfo": { + "IsAmazonFulfilled": true, + "IsFreeShippingEligible": true, + "IsPrimeEligible": true + }, + "Id": "H26dkOXFz3wk9n5bAuiKx5GM7RO4h9rOU5VNvDPADlz2evXUj7INzDWa5KETU0wsMkbCD3T0m4Bm%2BqkpGsTzjTj6PqqvbViQpRKgW%2FUtak4pF358CdeS1Q%3D%3D", + "IsBuyBoxWinner": true, + "MerchantInfo": { + "Id": "ATVPDKIKX0DER", + "Name": "Amazon.com" + }, + "Price": { + "Amount": 26.49, + "Currency": "USD", + "DisplayAmount": "$26.49", + "Savings": { + "Amount": 13.5, + "Currency": "USD", + "DisplayAmount": "$13.50 (34%)", + "Percentage": 34 + } + }, + "ProgramEligibility": { + "IsPrimeExclusive": false, + "IsPrimePantry": false + }, + "SavingBasis": { + "Amount": 39.99, + "Currency": "USD", + "DisplayAmount": "$39.99" + }, + "ViolatesMAP": false + } + ], + "Summaries": [ + { + "Condition": { + "Value": "New" + }, + "HighestPrice": { + "Amount": 71.35, + "Currency": "USD", + "DisplayAmount": "$71.35" + }, + "LowestPrice": { + "Amount": 21.18, + "Currency": "USD", + "DisplayAmount": "$21.18" + }, + "OfferCount": 18 + }, + { + "Condition": { + "Value": "Used" + }, + "HighestPrice": { + "Amount": 41.79, + "Currency": "USD", + "DisplayAmount": "$41.79" + }, + "LowestPrice": { + "Amount": 23.23, + "Currency": "USD", + "DisplayAmount": "$23.23" + }, + "OfferCount": 8 + } + ] + } + }, + { + "ASIN": "1838559337", + "BrowseNodeInfo": { + "BrowseNodes": [ + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Databases & Big Data", + "DisplayName": "Databases & Big Data", + "Id": "549646" + }, + "ContextFreeName": "Data Modeling & Design", + "DisplayName": "Data Modeling & Design", + "Id": "379382011", + "IsRoot": false, + "SalesRank": 102 + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Networking & Cloud Computing", + "DisplayName": "Networking & Cloud Computing", + "Id": "3652" + }, + "ContextFreeName": "Enterprise Data Computing", + "DisplayName": "Data in the Enterprise", + "Id": "3666" + }, + "ContextFreeName": "Client-Server Networking Systems", + "DisplayName": "Client-Server Systems", + "Id": "3668", + "IsRoot": false, + "SalesRank": 55 + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Networking & Cloud Computing", + "DisplayName": "Networking & Cloud Computing", + "Id": "3652" + }, + "ContextFreeName": "Cloud Computing", + "DisplayName": "Cloud Computing", + "Id": "10806612011", + "IsRoot": false, + "SalesRank": 154 + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Web Development & Design", + "DisplayName": "Web Development & Design", + "Id": "3510" + }, + "ContextFreeName": "Web Development & Design Programming", + "DisplayName": "Programming", + "Id": "3600", + "IsRoot": false + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Programming Languages", + "DisplayName": "Programming Languages", + "Id": "3952", + "IsRoot": false + } + ], + "WebsiteSalesRank": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "SalesRank": 151024 + } + }, + "DetailPageURL": "https://www.amazon.com/dp/1838559337?tag=associateTag-20&linkCode=osi&th=1&psc=1", + "Images": { + "Primary": { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/51R0SXcmImL.jpg", + "Width": 406 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/51R0SXcmImL._SL160_.jpg", + "Width": 130 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/51R0SXcmImL._SL75_.jpg", + "Width": 61 + } + }, + "Variants": [ + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/51d-h-k-SdL.jpg", + "Width": 406 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/51d-h-k-SdL._SL160_.jpg", + "Width": 130 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/51d-h-k-SdL._SL75_.jpg", + "Width": 61 + } + } + ] + }, + "ItemInfo": { + "ByLineInfo": { + "Contributors": [ + { + "Locale": "en_US", + "Name": "Tsoukalos, Mihalis", + "Role": "Author", + "RoleType": "author" + } + ], + "Manufacturer": { + "DisplayValue": "Packt Publishing", + "Label": "Manufacturer", + "Locale": "en_US" + } + }, + "Classifications": { + "Binding": { + "DisplayValue": "Paperback", + "Label": "Binding", + "Locale": "en_US" + }, + "ProductGroup": { + "DisplayValue": "Book", + "Label": "ProductGroup", + "Locale": "en_US" + } + }, + "ContentInfo": { + "Languages": { + "DisplayValues": [ + { + "DisplayValue": "English", + "Type": "Published" + }, + { + "DisplayValue": "English", + "Type": "Original Language" + }, + { + "DisplayValue": "English", + "Type": "Unknown" + } + ], + "Label": "Language", + "Locale": "en_US" + }, + "PagesCount": { + "DisplayValue": 798, + "Label": "NumberOfPages", + "Locale": "en_US" + }, + "PublicationDate": { + "DisplayValue": "2019-08-29T00:00:01Z", + "Label": "PublicationDate", + "Locale": "en_US" + } + }, + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "9781838559335" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "ISBNs": { + "DisplayValues": [ + "1838559337" + ], + "Label": "ISBN", + "Locale": "en_US" + } + }, + "ProductInfo": { + "IsAdultProduct": { + "DisplayValue": false, + "Label": "IsAdultProduct", + "Locale": "en_US" + }, + "ItemDimensions": { + "Height": { + "DisplayValue": 9.25, + "Label": "Height", + "Locale": "en_US", + "Unit": "Inches" + }, + "Length": { + "DisplayValue": 7.5, + "Label": "Length", + "Locale": "en_US", + "Unit": "Inches" + }, + "Weight": { + "DisplayValue": 2.96, + "Label": "Weight", + "Locale": "en_US", + "Unit": "Pounds" + }, + "Width": { + "DisplayValue": 1.8, + "Label": "Width", + "Locale": "en_US", + "Unit": "Inches" + } + }, + "ReleaseDate": { + "DisplayValue": "2019-08-29T00:00:01Z", + "Label": "ReleaseDate", + "Locale": "en_US" + }, + "UnitCount": { + "DisplayValue": 1, + "Label": "NumberOfItems", + "Locale": "en_US" + } + }, + "Title": { + "DisplayValue": "Mastering Go: Create Golang production applications using network libraries, concurrency, machine learning, and advanced data structures, 2nd Edition", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Availability": { + "Message": "In Stock.", + "MinOrderQuantity": 1, + "Type": "Now" + }, + "Condition": { + "SubCondition": { + "Value": "New" + }, + "Value": "New" + }, + "DeliveryInfo": { + "IsAmazonFulfilled": true, + "IsFreeShippingEligible": true, + "IsPrimeEligible": true + }, + "Id": "H26dkOXFz3zUErquCtGkasp0ByRdzlE8azTBB1I3U7%2F%2BjrmprKqPCogrZ5p5G3387IeqhCTvjl%2BV3kDhzzEy9lgSXS%2Bjq5wEtrkDCJEk6kNSxYzcHU5KjA%3D%3D", + "IsBuyBoxWinner": true, + "MerchantInfo": { + "Id": "ATVPDKIKX0DER", + "Name": "Amazon.com" + }, + "Price": { + "Amount": 49.99, + "Currency": "USD", + "DisplayAmount": "$49.99" + }, + "ProgramEligibility": { + "IsPrimeExclusive": false, + "IsPrimePantry": false + }, + "ViolatesMAP": false + } + ], + "Summaries": [ + { + "Condition": { + "Value": "New" + }, + "HighestPrice": { + "Amount": 82.6, + "Currency": "USD", + "DisplayAmount": "$82.60" + }, + "LowestPrice": { + "Amount": 49.99, + "Currency": "USD", + "DisplayAmount": "$49.99" + }, + "OfferCount": 7 + }, + { + "Condition": { + "Value": "Used" + }, + "HighestPrice": { + "Amount": 92.8, + "Currency": "USD", + "DisplayAmount": "$92.80" + }, + "LowestPrice": { + "Amount": 88.8, + "Currency": "USD", + "DisplayAmount": "$88.80" + }, + "OfferCount": 2 + } + ] + } + }, + { + "ASIN": "1593278659", + "BrowseNodeInfo": { + "BrowseNodes": [ + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Networking & Cloud Computing", + "DisplayName": "Networking & Cloud Computing", + "Id": "3652" + }, + "ContextFreeName": "Computer Network Security", + "DisplayName": "Network Security", + "Id": "3746", + "IsRoot": false, + "SalesRank": 10 + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Computer Programming", + "DisplayName": "Programming", + "Id": "3839" + }, + "ContextFreeName": "Software Design, Testing & Engineering", + "DisplayName": "Software Design, Testing & Engineering", + "Id": "4011" + }, + "ContextFreeName": "Software Testing", + "DisplayName": "Testing", + "Id": "4133", + "IsRoot": false, + "SalesRank": 9 + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Internet & Social Media", + "DisplayName": "Internet & Social Media", + "Id": "69766" + }, + "ContextFreeName": "Computer Hacking", + "DisplayName": "Hacking", + "Id": "3648", + "IsRoot": false + }, + { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "Ancestor": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "Id": "283155" + }, + "ContextFreeName": "Subjects", + "DisplayName": "Subjects", + "Id": "1000" + }, + "ContextFreeName": "Computers & Technology", + "DisplayName": "Computers & Technology", + "Id": "5" + }, + "ContextFreeName": "Computer Security & Encryption", + "DisplayName": "Security & Encryption", + "Id": "377560011" + }, + "ContextFreeName": "Computer Viruses", + "DisplayName": "Viruses", + "Id": "3636", + "IsRoot": false, + "SalesRank": 4 + } + ], + "WebsiteSalesRank": { + "ContextFreeName": "Books", + "DisplayName": "Books", + "SalesRank": 10424 + } + }, + "DetailPageURL": "https://www.amazon.com/dp/1593278659?tag=associateTag-20&linkCode=osi&th=1&psc=1", + "Images": { + "Primary": { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/51Eznheb+3L.jpg", + "Width": 378 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/51Eznheb+3L._SL160_.jpg", + "Width": 121 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/51Eznheb+3L._SL75_.jpg", + "Width": 57 + } + }, + "Variants": [ + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/51PJIRJ6cOL.jpg", + "Width": 378 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/51PJIRJ6cOL._SL160_.jpg", + "Width": 121 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/51PJIRJ6cOL._SL75_.jpg", + "Width": 57 + } + }, + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/41bVAz0e7XL.jpg", + "Width": 378 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/41bVAz0e7XL._SL160_.jpg", + "Width": 121 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/41bVAz0e7XL._SL75_.jpg", + "Width": 57 + } + }, + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/41bbpNdF4FL.jpg", + "Width": 378 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/41bbpNdF4FL._SL160_.jpg", + "Width": 121 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/41bbpNdF4FL._SL75_.jpg", + "Width": 57 + } + }, + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/51ucroxGBVL.jpg", + "Width": 378 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/51ucroxGBVL._SL160_.jpg", + "Width": 121 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/51ucroxGBVL._SL75_.jpg", + "Width": 57 + } + }, + { + "Large": { + "Height": 500, + "URL": "https://m.media-amazon.com/images/I/51EXsdd3bcL.jpg", + "Width": 378 + }, + "Medium": { + "Height": 160, + "URL": "https://m.media-amazon.com/images/I/51EXsdd3bcL._SL160_.jpg", + "Width": 121 + }, + "Small": { + "Height": 75, + "URL": "https://m.media-amazon.com/images/I/51EXsdd3bcL._SL75_.jpg", + "Width": 57 + } + } + ] + }, + "ItemInfo": { + "ByLineInfo": { + "Contributors": [ + { + "Locale": "en_US", + "Name": "Steele, Tom", + "Role": "Author", + "RoleType": "author" + }, + { + "Locale": "en_US", + "Name": "Patten, Chris", + "Role": "Author", + "RoleType": "author" + }, + { + "Locale": "en_US", + "Name": "Kottmann, Dan", + "Role": "Author", + "RoleType": "author" + } + ], + "Manufacturer": { + "DisplayValue": "No Starch Press", + "Label": "Manufacturer", + "Locale": "en_US" + } + }, + "Classifications": { + "Binding": { + "DisplayValue": "Paperback", + "Label": "Binding", + "Locale": "en_US" + }, + "ProductGroup": { + "DisplayValue": "Book", + "Label": "ProductGroup", + "Locale": "en_US" + } + }, + "ContentInfo": { + "Languages": { + "DisplayValues": [ + { + "DisplayValue": "English", + "Type": "Published" + }, + { + "DisplayValue": "English", + "Type": "Original Language" + }, + { + "DisplayValue": "English", + "Type": "Unknown" + } + ], + "Label": "Language", + "Locale": "en_US" + }, + "PagesCount": { + "DisplayValue": 368, + "Label": "NumberOfPages", + "Locale": "en_US" + }, + "PublicationDate": { + "DisplayValue": "2020-01-24T00:00:01Z", + "Label": "PublicationDate", + "Locale": "en_US" + } + }, + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "9781593278656" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "ISBNs": { + "DisplayValues": [ + "1593278659" + ], + "Label": "ISBN", + "Locale": "en_US" + } + }, + "ProductInfo": { + "ItemDimensions": { + "Height": { + "DisplayValue": 9.19, + "Label": "Height", + "Locale": "en_US", + "Unit": "Inches" + }, + "Length": { + "DisplayValue": 7, + "Label": "Length", + "Locale": "en_US", + "Unit": "Inches" + }, + "Weight": { + "DisplayValue": 1.55, + "Label": "Weight", + "Locale": "en_US", + "Unit": "Pounds" + }, + "Width": { + "DisplayValue": 0.86, + "Label": "Width", + "Locale": "en_US", + "Unit": "Inches" + } + }, + "ReleaseDate": { + "DisplayValue": "2020-01-24T00:00:01Z", + "Label": "ReleaseDate", + "Locale": "en_US" + } + }, + "Title": { + "DisplayValue": "Black Hat Go: Go Programming For Hackers and Pentesters", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Availability": { + "Message": "In Stock.", + "MinOrderQuantity": 1, + "Type": "Now" + }, + "Condition": { + "SubCondition": { + "Value": "New" + }, + "Value": "New" + }, + "DeliveryInfo": { + "IsAmazonFulfilled": true, + "IsFreeShippingEligible": true, + "IsPrimeEligible": true + }, + "Id": "H26dkOXFz3wZmCrmj%2FYxrIeDi4T0C7c4DcpkwtKMwqik23CM63QRy5C1CKFdgAhBHMVTIblk06rmzbr2%2F1dHfdEJWfLQOsNwV2aFHfobYBLj4%2BjJ%2Bmm9zg%3D%3D", + "IsBuyBoxWinner": true, + "MerchantInfo": { + "Id": "ATVPDKIKX0DER", + "Name": "Amazon.com" + }, + "Price": { + "Amount": 26.93, + "Currency": "USD", + "DisplayAmount": "$26.93", + "Savings": { + "Amount": 13.02, + "Currency": "USD", + "DisplayAmount": "$13.02 (33%)", + "Percentage": 33 + } + }, + "ProgramEligibility": { + "IsPrimeExclusive": false, + "IsPrimePantry": false + }, + "SavingBasis": { + "Amount": 39.95, + "Currency": "USD", + "DisplayAmount": "$39.95" + }, + "ViolatesMAP": false + } + ], + "Summaries": [ + { + "Condition": { + "Value": "New" + }, + "HighestPrice": { + "Amount": 56.93, + "Currency": "USD", + "DisplayAmount": "$56.93" + }, + "LowestPrice": { + "Amount": 24.49, + "Currency": "USD", + "DisplayAmount": "$24.49" + }, + "OfferCount": 27 + }, + { + "Condition": { + "Value": "Used" + }, + "HighestPrice": { + "Amount": 45.7, + "Currency": "USD", + "DisplayAmount": "$45.70" + }, + "LowestPrice": { + "Amount": 24.54, + "Currency": "USD", + "DisplayAmount": "$24.54" + }, + "OfferCount": 11 + } + ] + } + } + ], + "SearchRefinements": { + "SearchIndex": { + "Bins": [ + { + "DisplayName": "Books", + "Id": "Books" + }, + { + "DisplayName": "Kindle Store", + "Id": "KindleStore" + }, + { + "DisplayName": "CDs & Vinyl", + "Id": "Music" + }, + { + "DisplayName": "Digital Music", + "Id": "DigitalMusic" + }, + { + "DisplayName": "Apps & Games", + "Id": "MobileApps" + } + ], + "DisplayName": "Department", + "Id": "SearchIndex" + } + }, + "SearchURL": "https://www.amazon.com/s?k=golang&rh=p_n_availability%3A-1&tag=associateTag-20&linkCode=osi", + "TotalResultCount": 146 + } +} \ No newline at end of file diff --git a/src/tests/data/amazon-paapi-upc1.json b/src/tests/data/amazon-paapi-upc1.json new file mode 100644 index 00000000..c4ef3f95 --- /dev/null +++ b/src/tests/data/amazon-paapi-upc1.json @@ -0,0 +1,71 @@ +{ + "SearchResult": { + "Items": [ + { + "ASIN": "0545162076", + "DetailPageURL": "https://www.amazon.com/dp/0545162076?tag=xyz-20&linkCode=osi&th=1&psc=1", + "Images": { + "Primary": { + "Medium": { + "Height": 77, + "URL": "https://m.media-amazon.com/images/I/41XlFN304wL._SL160_.jpg", + "Width": 160 + } + } + }, + "ItemInfo": { + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "9780545162074", + "0717356278525" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "ISBNs": { + "DisplayValues": [ + "0545162076" + ], + "Label": "ISBN", + "Locale": "en_US" + }, + "UPCs": { + "DisplayValues": [ + "717356278525" + ], + "Label": "UPC", + "Locale": "en_US" + } + }, + "Title": { + "DisplayValue": "Harry Potter Paperback Box Set (Books 1-7)", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Id": "7zGLRO8zMe91QhHlbpbAVXroIa09en0xXckvtOhL06HTkngu3Gk%2FfQ4SiyWuqvc9JK2OkNlK2%2FgJMc%2B8Ya8kOqXCTlTN8dfY5Kgf29kPMfw3ALaWhxIArbNDf71jc5DLqEEjNTVxMGVFU7A9v4bkSn8KpOLj3Dur", + "Price": { + "Amount": 50, + "Currency": "USD", + "DisplayAmount": "$50.00", + "Savings": { + "Amount": 36.93, + "Currency": "USD", + "DisplayAmount": "$36.93 (42%)", + "Percentage": 42 + } + }, + "ViolatesMAP": false + } + ] + } + } + ], + "SearchURL": "https://www.amazon.com/s?k=717356278525&rh=p_n_availability%3A-1&tag=xyz-20&linkCode=osi", + "TotalResultCount": 1 + } +} diff --git a/src/tests/data/amazon-paapi-upc2.json b/src/tests/data/amazon-paapi-upc2.json new file mode 100644 index 00000000..35e4f2c8 --- /dev/null +++ b/src/tests/data/amazon-paapi-upc2.json @@ -0,0 +1,126 @@ +{ + "SearchResult": { + "Items": [ + { + "ASIN": "0545162076", + "DetailPageURL": "https://www.amazon.com/dp/0545162076?tag=xyz-20&linkCode=osi&th=1&psc=1", + "Images": { + "Primary": { + "Medium": { + "Height": 77, + "URL": "https://m.media-amazon.com/images/I/41XlFN304wL._SL160_.jpg", + "Width": 160 + } + } + }, + "ItemInfo": { + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "9780545162074", + "0717356278525" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "ISBNs": { + "DisplayValues": [ + "0545162076" + ], + "Label": "ISBN", + "Locale": "en_US" + }, + "UPCs": { + "DisplayValues": [ + "717356278525" + ], + "Label": "UPC", + "Locale": "en_US" + } + }, + "Title": { + "DisplayValue": "Harry Potter Paperback Box Set (Books 1-7)", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Id": "l3%2BVAIBi5OEt4atOWHgG01NT%2Bv7Qe%2Fr3y9WgD2WAjbhh%2BEDQ34Dd8dr6sRxEcTZz0bCliYKn2jRSNrcgq7%2BoTxvqWYYUesorAwZaqAGUVG7a6dJVjEF2BA%3D%3D", + "Price": { + "Amount": 39.74, + "Currency": "USD", + "DisplayAmount": "$39.74", + "Savings": { + "Amount": 47.19, + "Currency": "USD", + "DisplayAmount": "$47.19 (54%)", + "Percentage": 54 + } + }, + "ViolatesMAP": false + } + ] + } + }, + { + "ASIN": "B075JSK7TR", + "DetailPageURL": "https://www.amazon.com/dp/B075JSK7TR?tag=xyz-20&linkCode=osi&th=1&psc=1", + "Images": { + "Primary": { + "Medium": { + "Height": 130, + "URL": "https://m.media-amazon.com/images/I/51TxM4dNYhL._SL160_.jpg", + "Width": 160 + } + } + }, + "ItemInfo": { + "ExternalIds": { + "EANs": { + "DisplayValues": [ + "0842776102270" + ], + "Label": "EAN", + "Locale": "en_US" + }, + "UPCs": { + "DisplayValues": [ + "842776102270" + ], + "Label": "UPC", + "Locale": "en_US" + } + }, + "Title": { + "DisplayValue": "Google Pixelbook (i5, 8 GB RAM, 128GB) (GA00122-US)", + "Label": "Title", + "Locale": "en_US" + } + }, + "Offers": { + "Listings": [ + { + "Id": "l3%2BVAIBi5OEiajSVNFFu3E5Iu%2F86pP8tJMgoUu38Mt%2Fvrczl8wlgSwkD6EAXdcKVMgD3eb7EIfaS9LLhEBZPQjSiEqcFpBEnk%2F3D%2FPY0skqahzVWRRdH0Mzh73bDSRaGYRjsyH8rEOAQN5cYVZO%2FI%2B9v3QuaAh9M", + "Price": { + "Amount": 949.99, + "Currency": "USD", + "DisplayAmount": "$949.99", + "Savings": { + "Amount": 49.01, + "Currency": "USD", + "DisplayAmount": "$49.01 (5%)", + "Percentage": 5 + } + }, + "ViolatesMAP": false + } + ] + } + } + ], + "SearchURL": "https://www.amazon.com/s?k=717356278525%7C842776102270&rh=p_n_availability%3A-1&tag=xyz-20&linkCode=osi", + "TotalResultCount": 2 + } +} diff --git a/src/utils/string_utils.cpp b/src/utils/string_utils.cpp index 85110d47..d3db1527 100644 --- a/src/utils/string_utils.cpp +++ b/src/utils/string_utils.cpp @@ -1,222 +1,233 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "string_utils.h" #include "../fieldformat.h" #include #include #include #include #include #include #include namespace { static const int STRING_STORE_SIZE = 4999; // too big, too small? } QString Tellico::decodeHTML(const QByteArray& data_) { return decodeHTML(fromHtmlData(data_)); } QString Tellico::decodeHTML(const QString& text) { return KCharsets::resolveEntities(text); } QString Tellico::uid(int l, bool prefix) { QString uid; if(prefix) { uid = QStringLiteral("Tellico"); } uid.append(KRandom::randomString(qMax(l - uid.length(), 0))); return uid; } uint Tellico::toUInt(const QString& s, bool* ok) { if(s.isEmpty()) { if(ok) { *ok = false; } return 0; } int idx = 0; while(idx < s.length() && s[idx].isDigit()) { ++idx; } if(idx == 0) { if(ok) { *ok = false; } return 0; } return s.leftRef(idx).toUInt(ok); } QString Tellico::i18nReplace(QString text) { // Because QDomDocument sticks in random newlines, go ahead and grab them too static QRegularExpression rx(QStringLiteral("(?:\\n+ *)*(.*?)(?: *\\n+)*"), QRegularExpression::OptimizeOnFirstUsageOption); QRegularExpressionMatch match = rx.match(text); while(match.hasMatch()) { // KDE bug 254863, be sure to escape just in case of spurious & entities text.replace(match.capturedStart(), match.capturedLength(), i18n(match.captured(1).toUtf8().constData()).toHtmlEscaped()); match = rx.match(text, match.capturedStart()+1); } return text; } int Tellico::stringHash(const QString& str) { uint h = 0; uint g = 0; for(int i = 0; i < str.length(); ++i) { h = (h << 4) + str.unicode()[i].cell(); if((g = h & 0xf0000000)) { h ^= g >> 24; } h &= ~g; } int index = h; return index < 0 ? -index : index; } QString Tellico::shareString(const QString& str) { static QString stringStore[STRING_STORE_SIZE]; const int hash = stringHash(str) % STRING_STORE_SIZE; if(stringStore[hash] != str) { stringStore[hash] = str; } return stringStore[hash]; } QString Tellico::minutes(int seconds) { int min = seconds / 60; seconds = seconds % 60; return QString::number(min) + QLatin1Char(':') + QString::number(seconds).rightJustified(2, QLatin1Char('0')); } QString Tellico::fromHtmlData(const QByteArray& data_, const char* codecName) { QTextCodec* codec = codecName ? QTextCodec::codecForHtml(data_, QTextCodec::codecForName(codecName)) : QTextCodec::codecForHtml(data_); return codec->toUnicode(data_); } QString Tellico::removeAccents(const QString& value_) { static QCache stringCache(STRING_STORE_SIZE); if(stringCache.contains(value_)) { return *stringCache.object(value_); } static QRegularExpression rx; if(rx.pattern().isEmpty()) { QString pattern(QStringLiteral("(?:")); for(int i = 0x0300; i <= 0x036F; ++i) { pattern += QChar(i) + QLatin1Char('|'); } pattern.chop(1); pattern += QLatin1Char(')'); rx.setPattern(pattern); rx.optimize(); } // remove accents from table "Combining Diacritical Marks" const QString value2 = value_.normalized(QString::NormalizationForm_D).remove(rx); stringCache.insert(value_, new QString(value2)); return value2; } QString Tellico::mapValue(const QVariantMap& map, const char* name) { const QVariant v = map.value(QLatin1String(name)); if(v.isNull()) { return QString(); } else if(v.canConvert(QVariant::String)) { return v.toString(); } else if(v.canConvert(QVariant::StringList)) { return v.toStringList().join(FieldFormat::delimiterString()); } else if(v.canConvert(QVariant::Map)) { // FilmasterFetcher, OpenLibraryFetcher and VNDBFetcher depend on the default "value" field return v.toMap().value(QStringLiteral("value")).toString(); } else { return QString(); } } -QString Tellico::mapValue(const QVariantMap& map, const char* object, const char* name) { - const QVariant v = map.value(QLatin1String(object)); +QString Tellico::mapValue(const QVariantMap& map, const char* name1, const char* name2) { + const QVariant v = map.value(QLatin1String(name1)); if(v.isNull()) { return QString(); } else if(v.canConvert(QVariant::Map)) { - return mapValue(v.toMap(), name); + return mapValue(v.toMap(), name2); } else if(v.canConvert(QVariant::List)) { QStringList values; foreach(QVariant v, v.toList()) { - const QString s = mapValue(v.toMap(), name); + const QString s = mapValue(v.toMap(), name2); if(!s.isEmpty()) values += s; } return values.join(FieldFormat::delimiterString()); } else { return QString(); } } +QString Tellico::mapValue(const QVariantMap& map, const char* name1, const char* name2, const char* name3) { + const QVariant v = map.value(QLatin1String(name1)); + if(v.isNull()) { + return QString(); + } else if(v.canConvert(QVariant::Map)) { + return mapValue(v.toMap(), name2, name3); + } else { + return QString(); + } +} + QByteArray Tellico::obfuscate(const QString& string) { QByteArray b; b.reserve(string.length() * 2); for(int p = 0; p < string.length(); p++) { char c = KRandom::random() % 255; b.prepend(c ^ string.at(p).unicode()); b.prepend(c); } return b.toHex(); } QString Tellico::reverseObfuscate(const QByteArray& bytes) { if(bytes.length() % 2 != 0 || bytes.isEmpty()) { return QString(); } const QByteArray b = QByteArray::fromHex(bytes); QString result; result.reserve(b.length() / 2); for(int p = b.length()-1; p >= 0; p -= 2) { result.append(QLatin1Char(b.at(p-1) ^ b.at(p))); } return result; } QString Tellico::removeControlCodes(const QString& string) { QString result; result.reserve(string.size()); for(int i = 0; i < string.size(); ++i) { const ushort c = string.at(i).unicode(); // legal control codes in XML 1.0 are U+0009, U+000A, U+000D if(c > 31 || c == 9 || c == 10 || c == 13) { result += string.at(i); } } return result; } diff --git a/src/utils/string_utils.h b/src/utils/string_utils.h index 326bfa1e..f90dfbaf 100644 --- a/src/utils/string_utils.h +++ b/src/utils/string_utils.h @@ -1,77 +1,78 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef STRING_UTILS_H #define STRING_UTILS_H class QByteArray; class QString; #include #include /** * This file contains utility functions for manipulating strings. * * @author Robby Stephenson */ namespace Tellico { /** * Decode HTML entities. Only numeric entities are handled currently. */ QString decodeHTML(const QByteArray& data); QString decodeHTML(const QString& text); /** * Return a random, and almost certainly unique UID. * * @param length The UID starts with "Tellico" and adds enough letters to be @p length long. */ QString uid(int length=20, bool prefix=true); uint toUInt(const QString& string, bool* ok); /** * Replace all occurrences of text with i18n("text") */ QString i18nReplace(QString text); QString removeAccents(const QString& value); int stringHash(const QString& str); /** take advantage string collisions to reduce memory */ QString shareString(const QString& str); QString minutes(int seconds); QString fromHtmlData(const QByteArray& data, const char* codecName = nullptr); // helper methods for the QVariantMaps used by the JSON importers - QString mapValue(const QVariantMap& map, const char* object); - QString mapValue(const QVariantMap& map, const char* object, const char* name); + QString mapValue(const QVariantMap& map, const char* name1); + QString mapValue(const QVariantMap& map, const char* name1, const char* name2); + QString mapValue(const QVariantMap& map, const char* name1, const char* name2, const char* name3); QByteArray obfuscate(const QString& string); QString reverseObfuscate(const QByteArray& bytes); QString removeControlCodes(const QString& string); } #endif diff --git a/xslt/CMakeLists.txt b/xslt/CMakeLists.txt index 0b088fd2..28f1c33c 100644 --- a/xslt/CMakeLists.txt +++ b/xslt/CMakeLists.txt @@ -1,55 +1,54 @@ ADD_SUBDIRECTORY( entry-templates ) ADD_SUBDIRECTORY( report-templates ) ########### install files ############### SET(XSLT_FILES - amazon2tellico.xsl arxiv2tellico.xsl biblioshare2tellico.xsl bibtexml2tellico.xsl bluray-logo.png boardgamegeek2tellico.xsl cassette-logo.png comicvine2tellico.xsl cd-logo.png crossref2tellico.xsl dbc2tellico.xsl dblp2tellico.xsl delicious2tellico.xsl dvd-logo.png dvdfr2tellico.xsl gcstar2tellico.xsl giantbomb2tellico.xsl goodreads2tellico.xsl griffith2tellico.xsl hddvd-logo.png MARC21slim2MODS3.xsl MARC21slimUtils.xsl mods2tellico.xsl musicbrainz2tellico.xsl pubmed2tellico.xsl record-logo.png referencer2tellico.xsl shadowAlpha.png springer2tellico.xsl srw2tellico.xsl tellico2gcstar.xsl tellico2html.js tellico2html.xsl tellico2onix.xsl tellico-by-author.xsl tellico-by-title.xsl tellico-common.xsl tellico-printing.xsl UNIMARC2MODS3.xsl unixref2tellico.xsl vhs-logo.png vinoxml2tellico.xsl welcome.html xmp2tellico.xsl ) INSTALL(FILES ${XSLT_FILES} DESTINATION ${TELLICO_DATA_INSTALL_DIR} ) diff --git a/xslt/amazon2tellico.xsl b/xslt/amazon2tellico.xsl deleted file mode 100644 index 37e1fa9a..00000000 --- a/xslt/amazon2tellico.xsl +++ /dev/null @@ -1,656 +0,0 @@ - - - - - - - - - - - - - - - - - - - Books - - - - - - - - Video - - - - - - - - -abcdefghijklmnopqrstuvwxyz -ABCDEFGHIJKLMNOPQRSTUVWXYZ - - - - - - - - - - - 2 - - - 3 - - - - 4 - - - 11 - - - 13 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - :: - - - - - - - - - - - - - - - - - - - - - - - - <br/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <xsl:value-of select="aws:Title"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Hardback - - - Paperback - - - - Souple - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Compact Disc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - VHS - - - Blu-ray - - - - - - - - - - - Black & White - - - Color - - - - - - - - - - - - - - - true - - - - - - NTSC - - - - - PAL - - - - - SECAM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PG-13 (USA) - - - PG (USA) - - - R (USA) - - - G (USA) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pending - - - - - - - - - - - - - - - - - - - - - - . - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Xbox 360 - - - Xbox - - - Nintendo Wii - - - PSP - - - Windows - - - Mac OS - - - Dreamcast - - - - - PlayStation3 - - - PlayStation2 - - - - - - - - - - - - -