[discident-glib] ean: Add support for Amazon lookups
- From: Bastien Nocera <hadess src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [discident-glib] ean: Add support for Amazon lookups
- Date: Sat, 27 Apr 2013 17:59:41 +0000 (UTC)
commit 5a647ae620eacd064b94995e1ec14e0a11b41925
Author: Bastien Nocera <hadess hadess net>
Date: Mon Apr 8 20:10:41 2013 +0200
ean: Add support for Amazon lookups
discident-glib/Makefile.am | 6 +-
discident-glib/amz-response.xml | 161 +++++++++++
discident-glib/discident-ean-amz-glib.c | 408 +++++++++++++++++++++++++++
discident-glib/discident-ean-amz-glib.h | 43 +++
discident-glib/discident-ean-glib.c | 60 ++++-
discident-glib/discident-ean-private-glib.h | 9 +
discident-glib/discident-glib.symbols | 2 +
discident-glib/test-diglib.c | 101 +++++++
8 files changed, 781 insertions(+), 9 deletions(-)
---
diff --git a/discident-glib/Makefile.am b/discident-glib/Makefile.am
index 4082295..e78f695 100644
--- a/discident-glib/Makefile.am
+++ b/discident-glib/Makefile.am
@@ -1,6 +1,6 @@
include $(top_srcdir)/Makefile.decl
-EXTRA_DIST = discident-glib.symbols rl-response.xml
+EXTRA_DIST = discident-glib.symbols rl-response.xml amz-response.xml
BUILT_GIRSOURCES =
lib_LTLIBRARIES = libdiscident-glib.la
@@ -18,7 +18,9 @@ libdiscident_glib_la_SOURCES = \
discident-glib-private.h \
discident-ean-private-glib.h \
discident-ean-rl-glib.c \
- discident-ean-rl-glib.h
+ discident-ean-rl-glib.h \
+ discident-ean-amz-glib.c \
+ discident-ean-amz-glib.h
libdiscident_glib_la_LIBADD = $(DISCIDENT_LIBS)
diff --git a/discident-glib/amz-response.xml b/discident-glib/amz-response.xml
new file mode 100644
index 0000000..336fae7
--- /dev/null
+++ b/discident-glib/amz-response.xml
@@ -0,0 +1,161 @@
+<?xml version="1.0"?>
+<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
+ <OperationRequest>
+ <RequestId>e49631f6-80c6-49c2-aab4-3dd769616262</RequestId>
+ <Arguments>
+ <Argument Name="Operation" Value="ItemLookup"/>
+ <Argument Name="Service" Value="AWSECommerceService"/>
+ <Argument Name="AssociateTag" Value="hadessnet-21"/>
+ <Argument Name="SearchIndex" Value="DVD"/>
+ <Argument Name="Signature" Value="Q33sFvy7DOXxSTiKbnCSX9u9wYfnXMvsFJPSl8CP1xQ="/>
+ <Argument Name="ItemId" Value="9781449316853"/>
+ <Argument Name="IdType" Value="EAN"/>
+ <Argument Name="AWSAccessKeyId" Value="AKIAILKBE5WAT5HLCKJA"/>
+ <Argument Name="Timestamp" Value="2013-04-11T20:23:09Z"/>
+ <Argument Name="ResponseGroup" Value="ItemAttributes,Images"/>
+ </Arguments>
+ <RequestProcessingTime>0.0240230000000000</RequestProcessingTime>
+ </OperationRequest>
+ <Items>
+ <Request>
+ <IsValid>True</IsValid>
+ <ItemLookupRequest>
+ <IdType>EAN</IdType>
+ <ItemId>9781449316853</ItemId>
+ <ResponseGroup>ItemAttributes</ResponseGroup>
+ <ResponseGroup>Images</ResponseGroup>
+ <SearchIndex>DVD</SearchIndex>
+ <VariationPage>All</VariationPage>
+ </ItemLookupRequest>
+ </Request>
+ <Item>
+ <ASIN>1449316859</ASIN>
+
<DetailPageURL>http://www.amazon.co.uk/JavaScript-Pocket-Reference-OReilly/dp/1449316859%3FSubscriptionId%3DAKIAILKBE5WAT5HLCKJA%26tag%3Dhadessnet-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D1449316859</DetailPageURL>
+ <ItemLinks>
+ <ItemLink>
+ <Description>Add To Wishlist</Description>
+
<URL>http://www.amazon.co.uk/gp/registry/wishlist/add-item.html%3Fasin.0%3D1449316859%26SubscriptionId%3DAKIAILKBE5WAT5HLCKJA%26tag%3Dhadessnet-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3D1449316859</URL>
+ </ItemLink>
+ <ItemLink>
+ <Description>Tell A Friend</Description>
+
<URL>http://www.amazon.co.uk/gp/pdp/taf/1449316859%3FSubscriptionId%3DAKIAILKBE5WAT5HLCKJA%26tag%3Dhadessnet-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3D1449316859</URL>
+ </ItemLink>
+ <ItemLink>
+ <Description>All Customer Reviews</Description>
+
<URL>http://www.amazon.co.uk/review/product/1449316859%3FSubscriptionId%3DAKIAILKBE5WAT5HLCKJA%26tag%3Dhadessnet-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3D1449316859</URL>
+ </ItemLink>
+ <ItemLink>
+ <Description>All Offers</Description>
+
<URL>http://www.amazon.co.uk/gp/offer-listing/1449316859%3FSubscriptionId%3DAKIAILKBE5WAT5HLCKJA%26tag%3Dhadessnet-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3D1449316859</URL>
+ </ItemLink>
+ </ItemLinks>
+ <SmallImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L._SL75_.jpg</URL>
+ <Height Units="pixels">75</Height>
+ <Width Units="pixels">45</Width>
+ </SmallImage>
+ <MediumImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L._SL160_.jpg</URL>
+ <Height Units="pixels">160</Height>
+ <Width Units="pixels">97</Width>
+ </MediumImage>
+ <LargeImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L.jpg</URL>
+ <Height Units="pixels">500</Height>
+ <Width Units="pixels">303</Width>
+ </LargeImage>
+ <ImageSets>
+ <ImageSet Category="primary">
+ <SwatchImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L._SL30_.jpg</URL>
+ <Height Units="pixels">30</Height>
+ <Width Units="pixels">18</Width>
+ </SwatchImage>
+ <SmallImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L._SL75_.jpg</URL>
+ <Height Units="pixels">75</Height>
+ <Width Units="pixels">45</Width>
+ </SmallImage>
+ <ThumbnailImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L._SL75_.jpg</URL>
+ <Height Units="pixels">75</Height>
+ <Width Units="pixels">45</Width>
+ </ThumbnailImage>
+ <TinyImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L._SL110_.jpg</URL>
+ <Height Units="pixels">110</Height>
+ <Width Units="pixels">67</Width>
+ </TinyImage>
+ <MediumImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L._SL160_.jpg</URL>
+ <Height Units="pixels">160</Height>
+ <Width Units="pixels">97</Width>
+ </MediumImage>
+ <LargeImage>
+ <URL>http://ecx.images-amazon.com/images/I/51UL7NX3-6L.jpg</URL>
+ <Height Units="pixels">500</Height>
+ <Width Units="pixels">303</Width>
+ </LargeImage>
+ </ImageSet>
+ </ImageSets>
+ <ItemAttributes>
+ <Author>David Flanagan</Author>
+ <Binding>Paperback</Binding>
+ <EAN>9781449316853</EAN>
+ <EANList>
+ <EANListElement>9781449316853</EANListElement>
+ </EANList>
+ <Edition>3</Edition>
+ <ISBN>1449316859</ISBN>
+ <IsEligibleForTradeIn>1</IsEligibleForTradeIn>
+ <ItemDimensions>
+ <Height Units="hundredths-inches">701</Height>
+ <Length Units="hundredths-inches">425</Length>
+ <Weight Units="hundredths-pounds">44</Weight>
+ <Width Units="hundredths-inches">59</Width>
+ </ItemDimensions>
+ <Label>O'Reilly Media</Label>
+ <Languages>
+ <Language>
+ <Name>English</Name>
+ <Type>Unknown</Type>
+ </Language>
+ <Language>
+ <Name>English</Name>
+ <Type>Original Language</Type>
+ </Language>
+ <Language>
+ <Name>English</Name>
+ <Type>Published</Type>
+ </Language>
+ </Languages>
+ <ListPrice>
+ <Amount>999</Amount>
+ <CurrencyCode>GBP</CurrencyCode>
+ <FormattedPrice>£9.99</FormattedPrice>
+ </ListPrice>
+ <Manufacturer>O'Reilly Media</Manufacturer>
+ <NumberOfItems>1</NumberOfItems>
+ <NumberOfPages>288</NumberOfPages>
+ <PackageDimensions>
+ <Height Units="hundredths-inches">79</Height>
+ <Length Units="hundredths-inches">709</Length>
+ <Weight Units="hundredths-pounds">44</Weight>
+ <Width Units="hundredths-inches">453</Width>
+ </PackageDimensions>
+ <ProductGroup>Book</ProductGroup>
+ <ProductTypeName>ABIS_BOOK</ProductTypeName>
+ <PublicationDate>2012-04-25</PublicationDate>
+ <Publisher>O'Reilly Media</Publisher>
+ <SKU>ACOUK_book_usedgood_1449316859</SKU>
+ <Studio>O'Reilly Media</Studio>
+ <Title>JavaScript Pocket Reference (Pocket Reference (O'Reilly))</Title>
+ <TradeInValue>
+ <Amount>52</Amount>
+ <CurrencyCode>GBP</CurrencyCode>
+ <FormattedPrice>£0.52</FormattedPrice>
+ </TradeInValue>
+ </ItemAttributes>
+ </Item>
+ </Items>
+</ItemLookupResponse>
diff --git a/discident-glib/discident-ean-amz-glib.c b/discident-glib/discident-ean-amz-glib.c
new file mode 100644
index 0000000..4a36c7b
--- /dev/null
+++ b/discident-glib/discident-ean-amz-glib.c
@@ -0,0 +1,408 @@
+/*
+ Copyright (C) 2010 Bastien Nocera
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA.
+
+ Authors: Bastien Nocera <hadess hadess net>
+
+ */
+
+#include <string.h>
+
+#include <libsoup/soup.h>
+#include <glib/gprintf.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include "discident-ean-glib.h"
+#include "discident-ean-amz-glib.h"
+#include "discident-error.h"
+
+#define RESPONSE_NAMESPACE "http://webservices.amazon.com/AWSECommerceService/2011-08-01"
+
+/* FIXME: Ability to lookup more than DVD? Automatically look up Books for ISBN numbers? UPC support? */
+
+#define SEARCH_URL "https://webservices.amazon.%s/onca/xml?"
+
+#define COMMON_PARAMETERS \
+"AWSAccessKeyId=%s&" \
+"AssociateTag=%s&" \
+"IdType=EAN&" \
+"ItemId=%s&" \
+"Operation=ItemLookup&" \
+"ResponseGroup=ItemAttributes,Images&" \
+"SearchIndex=DVD&" \
+"Service=AWSECommerceService"
+
+static char *
+get_timestamp (void)
+{
+ GTimeVal tv;
+ char *timestamp;
+ char *ts_escaped;
+
+ g_get_current_time (&tv);
+ /* Amazon doesn't want sub-second information */
+ tv.tv_usec = 0;
+ timestamp = g_time_val_to_iso8601 (&tv);
+ ts_escaped = soup_uri_encode (timestamp, NULL);
+ g_free (timestamp);
+
+ return ts_escaped;
+}
+
+static int
+byte_sort (gconstpointer a,
+ gconstpointer b)
+{
+ return memcmp (a, b, MIN (strlen (a), strlen (b)));
+}
+
+/* orig will be freed */
+static char *
+replace (char *orig,
+ const char *string,
+ const char *replacement,
+ gint start)
+{
+ GRegex *regex;
+ char *escaped;
+
+ regex = g_regex_new (string, 0, 0, NULL);
+ escaped = g_regex_replace (regex, orig, -1, start, replacement, 0, NULL);
+ g_free (orig);
+ g_regex_unref (regex);
+
+ return escaped;
+}
+
+static char *
+flatten_list (GList *list)
+{
+ GString *string;
+ GList *l;
+
+ string = g_string_new (NULL);
+ for (l = list; l != NULL; l = l->next) {
+ if (l->prev != NULL)
+ g_string_append_c (string, '&');
+ g_string_append (string, l->data);
+ }
+ return g_string_free (string, FALSE);
+}
+
+#define VERIFY(x, num) if (steps != NULL && steps[num -1] != NULL) g_assert_cmpstr (x, ==, steps[num - 1])
+
+char *
+_sign_query (const char *url,
+ const char *timestamp,
+ const char *private_key,
+ const char **steps)
+{
+ char *with_time, **params, *sorted, *final, *to_sign, *encoded_sig, *sig;
+ SoupURI *uri;
+ guint i;
+ GList *list;
+ GHmac *hmac;
+ guint8 buffer[256];
+ gsize len;
+
+ with_time = g_strdup_printf ("%s&Timestamp=%s", url, timestamp);
+ VERIFY (with_time, 1);
+
+ /* http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/rest-signature.html
+ * step 2 */
+ with_time = replace (with_time, ",", "%2C", 0);
+ with_time = replace (with_time, ":", "%3A", strstr (with_time, "?") - with_time);
+ VERIFY(with_time, 2);
+
+ /* http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/rest-signature.html
+ * step 3 */
+ uri = soup_uri_new (with_time);
+ params = g_strsplit (soup_uri_get_query (uri), "&", -1);
+ list = NULL;
+ for (i = 0; params[i] != NULL; i++)
+ list = g_list_prepend (list, params[i]);
+ g_free (params);
+
+ /* http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/rest-signature.html
+ * step 4 */
+ list = g_list_sort (list, byte_sort);
+
+ /* http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/rest-signature.html
+ * step 5 */
+ sorted = flatten_list (list);
+
+ VERIFY (sorted, 5);
+
+ /* http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/rest-signature.html
+ * step 6, 7 */
+ to_sign = g_strdup_printf ("GET\n%s\n/onca/xml\n%s", soup_uri_get_host (uri), sorted);
+ g_free (sorted);
+
+ VERIFY (to_sign, 7);
+
+ /* http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/rest-signature.html
+ * step 8 */
+ hmac = g_hmac_new (G_CHECKSUM_SHA256, (guchar *) private_key, strlen (private_key));
+ g_hmac_update (hmac, (const guchar *) to_sign, -1);
+ g_free (to_sign);
+ len = sizeof(buffer);
+ g_hmac_get_digest (hmac, buffer, &len);
+ g_hmac_unref (hmac);
+
+ sig = g_base64_encode (buffer, len);
+ VERIFY (sig, 8);
+
+ /* http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/rest-signature.html
+ * step 9 */
+
+ encoded_sig = soup_uri_encode (sig, NULL);
+ g_free (sig);
+ encoded_sig = replace (encoded_sig, "\\+", "%2B", 0);
+ VERIFY (encoded_sig, 9);
+
+ /* http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/rest-signature.html
+ * step 10 */
+
+ sig = g_strdup_printf ("Signature=%s", encoded_sig);
+ list = g_list_prepend (list, sig);
+ list = g_list_sort (list, byte_sort);
+ sorted = flatten_list (list);
+ g_list_free_full (list, g_free);
+
+ final = g_strdup_printf ("%s://%s%s?%s",
+ soup_uri_get_scheme (uri),
+ soup_uri_get_host (uri),
+ soup_uri_get_path (uri),
+ sorted);
+ g_free (sorted);
+ soup_uri_free (uri);
+
+ VERIFY (final, 10);
+
+ return final;
+}
+
+static char *
+get_search_uri (DiscidentEan *ean,
+ const char *barcode)
+{
+ char *uri, *timestamp, *ret;
+
+ uri = g_strdup_printf (SEARCH_URL COMMON_PARAMETERS,
+ ean->priv->service + strlen ("amz-"),
+ ean->priv->access_key,
+ ean->priv->associate_tag, barcode);
+ timestamp = get_timestamp ();
+
+ ret = _sign_query (uri, timestamp, ean->priv->private_key, NULL);
+ g_free (uri);
+ g_free (timestamp);
+
+ return ret;
+}
+
+static char *
+get_value_for_xpath (xmlDocPtr doc,
+ xmlXPathContextPtr xpath_ctx,
+ const char *path)
+{
+ xmlXPathObjectPtr xpath_obj;
+ xmlNodePtr cur;
+ char *ret;
+
+ xpath_obj = xmlXPathEvalExpression (BAD_CAST (path), xpath_ctx);
+ if (xpath_obj == NULL)
+ return NULL;
+ if (xpath_obj->nodesetval == NULL) {
+ xmlXPathFreeObject (xpath_obj);
+ return NULL;
+ }
+ cur = xpath_obj->nodesetval->nodeTab[0];
+ ret = g_strdup ((const char *) xmlNodeListGetString (doc, cur->xmlChildrenNode, 1));
+ xmlXPathFreeObject (xpath_obj);
+
+ return ret;
+}
+
+gboolean
+_amz_parse_lookup_response (const char *response,
+ char **ret_title,
+ char **ret_img_url)
+{
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpath_ctx;
+
+ if (response == NULL)
+ return FALSE;
+
+ doc = xmlParseMemory (response, strlen (response));
+ if (doc == NULL)
+ doc = xmlRecoverMemory (response, strlen (response));
+
+ if(!doc ||
+ !doc->children ||
+ !doc->children->name ||
+ g_ascii_strcasecmp ((char *)doc->children->name, "ItemLookupResponse") != 0) {
+ if (doc != NULL)
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ xpath_ctx = xmlXPathNewContext(doc);
+
+ /* Register namespace */
+ xmlXPathRegisterNs (xpath_ctx, BAD_CAST ("ns"), BAD_CAST (RESPONSE_NAMESPACE));
+
+ *ret_title = get_value_for_xpath (doc, xpath_ctx, "//ns:Title");
+ if (*ret_title == NULL)
+ goto bail;
+
+ *ret_img_url = get_value_for_xpath (doc, xpath_ctx, "//ns:Item/ns:LargeImage/ns:URL");
+
+bail:
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc (doc);
+
+ return TRUE;
+}
+
+static SoupMessage *
+create_query_message (DiscidentEan *ean,
+ const char *barcode)
+{
+ SoupMessage *msg;
+ char *uri;
+
+ uri = get_search_uri (ean, barcode);
+ msg = soup_message_new ("GET", uri);
+ g_free (uri);
+
+ return msg;
+}
+
+gboolean
+discident_ean_amz_lookup_sync (DiscidentEan *ean,
+ const char *barcode,
+ char **title,
+ char **img_url,
+ GError **error)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ char *response;
+ int ret;
+
+ g_return_val_if_fail (DISCIDENT_IS_EAN (ean), FALSE);
+ g_return_val_if_fail (title != NULL, FALSE);
+
+ session = soup_session_new ();
+
+ msg = create_query_message (ean, barcode);
+
+ response = NULL;
+
+ ret = soup_session_send_message (session, msg);
+ if (SOUP_STATUS_IS_SUCCESSFUL (ret) &&
+ msg->response_body != NULL) {
+ response = g_strdup (msg->response_body->data);
+ }
+
+ g_object_unref (msg);
+ g_object_unref (session);
+
+ if (response == NULL) {
+ g_set_error (error, SOUP_HTTP_ERROR, ret, "Could not query EAN service");
+ return FALSE;
+ }
+
+ if (_amz_parse_lookup_response (response, title, img_url) == FALSE) {
+ g_free (response);
+ g_set_error (error, DISCIDENT_ERROR, DISCIDENT_ERROR_PARSE, "Failed to parse response from
EAN service");
+ return FALSE;
+ }
+
+ if (g_strcmp0 (*title, barcode) == 0) {
+ g_free (response);
+ g_set_error (error, DISCIDENT_ERROR, DISCIDENT_ERROR_EMPTY_RESPONSE, "No response for
requested barcode");
+ return FALSE;
+ }
+
+ g_free (response);
+
+ return TRUE;
+}
+
+static void
+got_body_query (SoupMessage *msg,
+ QueryData *data)
+{
+ const char *barcode;
+ char *title = NULL;
+ char *img_url = NULL;
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) ||
+ msg->response_body == NULL) {
+ g_simple_async_result_set_error (data->simple,
+ SOUP_HTTP_ERROR,
+ msg->status_code,
+ "Could not query EAN service: %s",
+ soup_status_get_phrase (msg->status_code));
+ goto out;
+ }
+
+ if (_amz_parse_lookup_response (msg->response_body->data, &title, &img_url) == FALSE) {
+ g_simple_async_result_set_error (data->simple,
+ DISCIDENT_ERROR,
+ DISCIDENT_ERROR_PARSE,
+ "Failed to parse response from EAN service");
+ goto out;
+ }
+
+ barcode = g_object_get_data (G_OBJECT (data->simple), "barcode");
+ if (g_strcmp0 (title, barcode) == 0) {
+ g_simple_async_result_set_error (data->simple,
+ DISCIDENT_ERROR,
+ DISCIDENT_ERROR_EMPTY_RESPONSE,
+ "No response for requested barcode");
+ g_free (title);
+ g_free (img_url);
+ goto out;
+ }
+
+ g_object_set_data_full (G_OBJECT (data->simple), "image-url", img_url, g_free);
+ g_simple_async_result_set_op_res_gpointer (data->simple, title, NULL);
+
+out:
+ g_simple_async_result_complete_in_idle (data->simple);
+ free_query_data (data);
+}
+
+void
+discident_ean_amz_lookup (DiscidentEan *ean,
+ SoupSession *session,
+ QueryData *data)
+{
+ SoupMessage *msg;
+
+ msg = create_query_message (ean, data->barcode);
+ g_signal_connect (G_OBJECT (msg), "got-body",
+ G_CALLBACK (got_body_query), data);
+ soup_session_queue_message (session, msg, NULL, NULL);
+}
diff --git a/discident-glib/discident-ean-amz-glib.h b/discident-glib/discident-ean-amz-glib.h
new file mode 100644
index 0000000..fde9ca1
--- /dev/null
+++ b/discident-glib/discident-ean-amz-glib.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2010 Bastien Nocera
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA.
+
+ Authors: Bastien Nocera <hadess hadess net>
+
+ */
+
+#ifndef DISCIDENT_EAN_AMZ_GLIB_H
+#define DISCIDENT_EAN_AMZ_GLIB_H
+
+#include <glib.h>
+#include "discident-ean-private-glib.h"
+
+G_BEGIN_DECLS
+
+gboolean discident_ean_amz_lookup_sync (DiscidentEan *ean,
+ const char *barcode,
+ char **title,
+ char **img_url,
+ GError **error);
+
+void discident_ean_amz_lookup (DiscidentEan *ean,
+ SoupSession *session,
+ QueryData *data);
+
+G_END_DECLS
+
+#endif /* DISCIDENT_EAN_AMZ_GLIB_H */
diff --git a/discident-glib/discident-ean-glib.c b/discident-glib/discident-ean-glib.c
index ce97d47..61419a5 100644
--- a/discident-glib/discident-ean-glib.c
+++ b/discident-glib/discident-ean-glib.c
@@ -29,6 +29,7 @@
#include "discident-ean-glib.h"
#include "discident-error.h"
+#include "discident-ean-amz-glib.h"
#include "discident-ean-rl-glib.h"
#include "discident-ean-private-glib.h"
@@ -48,10 +49,18 @@ enum {
PROP_PARAMETERS
};
+/* FIXME Add more countries:
+ * http://docs.aws.amazon.com/AWSECommerceService/2011-08-01/DG/AnatomyOfaRESTRequest.html
+ */
const char *rl_services[] = {
"rl-en_US",
"rl-en_GB",
- "rl-fr_FR"
+ "rl-fr_FR",
+ "amz-co.uk",
+ "amz-de",
+ "amz-es",
+ "amz-fr",
+ "amz-it"
};
static gboolean
@@ -66,6 +75,19 @@ service_is_valid (const char *service)
return FALSE;
}
+static gboolean
+parameters_are_valid (DiscidentEan *ean,
+ GError **error)
+{
+ if (ean->priv->access_key != NULL &&
+ ean->priv->private_key != NULL &&
+ ean->priv->associate_tag != NULL)
+ return TRUE;
+
+ g_set_error (error, DISCIDENT_ERROR, DISCIDENT_ERROR_MISSING_PARAMETERS, "Parameters are missing to
access this web service");
+ return FALSE;
+}
+
static void
discident_ean_set_parameters (DiscidentEan *ean,
GHashTable *parameters)
@@ -194,11 +216,23 @@ discident_ean_lookup_sync (DiscidentEan *ean,
char **img_url,
GError **error)
{
- return discident_ean_rl_lookup_sync (ean,
- barcode,
- title,
- img_url,
- error);
+ if (g_str_has_prefix (ean->priv->service, "rl-")) {
+ return discident_ean_rl_lookup_sync (ean,
+ barcode,
+ title,
+ img_url,
+ error);
+ } else if (g_str_has_prefix (ean->priv->service, "amz-")) {
+ if (!parameters_are_valid (ean, error))
+ return FALSE;
+ return discident_ean_amz_lookup_sync (ean,
+ barcode,
+ title,
+ img_url,
+ error);
+ }
+
+ g_assert_not_reached ();
}
void
@@ -234,6 +268,7 @@ discident_ean_lookup (DiscidentEan *ean,
GSimpleAsyncResult *simple;
SoupSession *session;
QueryData *data;
+ GError *error = NULL;
simple = g_simple_async_result_new (G_OBJECT (ean),
callback,
@@ -249,7 +284,16 @@ discident_ean_lookup (DiscidentEan *ean,
data->barcode = g_strdup (barcode);
data->simple = simple;
- discident_ean_rl_lookup (ean, session, data);
+ if (g_str_has_prefix (ean->priv->service, "rl-")) {
+ discident_ean_rl_lookup (ean, session, data);
+ } else if (g_str_has_prefix (ean->priv->service, "amz-")) {
+ if (!parameters_are_valid (ean, &error)) {
+ g_simple_async_result_take_error (simple, error);
+ g_simple_async_result_complete_in_idle (simple);
+ return;
+ }
+ discident_ean_amz_lookup (ean, session, data);
+ }
}
/**
@@ -326,6 +370,8 @@ discident_ean_lookup_get_barcode (DiscidentEan *ean,
DiscidentEan *
discident_ean_new (const char *service)
{
+ g_return_val_if_fail (!service || service_is_valid (service), NULL);
+
if (service)
return g_object_new (DISCIDENT_TYPE_EAN, "service", service, NULL);
diff --git a/discident-glib/discident-ean-private-glib.h b/discident-glib/discident-ean-private-glib.h
index e0320e5..f2a386f 100644
--- a/discident-glib/discident-ean-private-glib.h
+++ b/discident-glib/discident-ean-private-glib.h
@@ -23,6 +23,8 @@
#ifndef DISCIDENT_EAN_PRIVATE_GLIB_H
#define DISCIDENT_EAN_PRIVATE_GLIB_H
+#include <libsoup/soup.h>
+
struct DiscidentEanPrivate {
char *service;
@@ -47,9 +49,16 @@ typedef struct {
} QueryData;
void free_query_data (QueryData *data);
+char *_sign_query (const char *url,
+ const char *timestamp,
+ const char *private_key,
+ const char **steps);
gboolean _rl_parse_lookup_response (const char *response,
char **ret_title,
char **ret_img_url);
+gboolean _amz_parse_lookup_response (const char *response,
+ char **ret_title,
+ char **ret_img_url);
#endif /* DISCIDENT_EAN_PRIVATE_GLIB_H */
diff --git a/discident-glib/discident-glib.symbols b/discident-glib/discident-glib.symbols
index 140646b..f2e93b5 100644
--- a/discident-glib/discident-glib.symbols
+++ b/discident-glib/discident-glib.symbols
@@ -13,5 +13,7 @@ discident_ean_lookup_sync
discident_ean_lookup
discident_ean_lookup_get_barcode
discident_ean_lookup_finish
+_sign_query
_rl_parse_lookup_response
+_amz_parse_lookup_response
discident_error_quark
diff --git a/discident-glib/test-diglib.c b/discident-glib/test-diglib.c
index 0dbcb41..eda0cc4 100644
--- a/discident-glib/test-diglib.c
+++ b/discident-glib/test-diglib.c
@@ -8,6 +8,7 @@
#include <discident-glib.h>
#include <discident-ean-glib.h>
#include <discident-glib-private.h>
+#include <discident-ean-private-glib.h>
static gboolean option_async = FALSE;
static gboolean option_file_list = FALSE;
@@ -101,6 +102,105 @@ test_rl_parse (void)
}
static void
+test_amz_parse (void)
+{
+ char *response;
+ gboolean ret;
+ char *img_url, *title;
+
+ g_file_get_contents (SRCDIR "/amz-response.xml",
+ &response,
+ NULL,
+ NULL);
+ ret = _amz_parse_lookup_response (response,
+ &title,
+ &img_url);
+ g_assert (ret);
+ g_assert_cmpstr (title, ==, "JavaScript Pocket Reference (Pocket Reference (O'Reilly))");
+ g_assert_cmpstr (img_url, ==, "http://ecx.images-amazon.com/images/I/51UL7NX3-6L.jpg");
+}
+
+static void
+test_amz1 (void)
+{
+ char *ret;
+ const char *steps[] = {
+
"http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Operation=ItemLookup&ItemId=0679722769&ResponseGroup=ItemAttributes,Offers,Images,Reviews&Version=2009-01-06&Timestamp=2009-01-01T12:00:00Z",
+
"http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Operation=ItemLookup&ItemId=0679722769&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Version=2009-01-06&Timestamp=2009-01-01T12%3A00%3A00Z",
+ NULL,
+ NULL,
+
"AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06",
+ NULL,
+
"GET\nwebservices.amazon.com\n/onca/xml\nAWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06",
+ "M/y0+EAFFGaUAp4bWv/WEuXYah99pVsxvqtAuC8YN7I=",
+ "M%2Fy0%2BEAFFGaUAp4bWv%2FWEuXYah99pVsxvqtAuC8YN7I%3D",
+ /* Note: this has been modified to reorder the parameters as
+ * per other tests */
+
"http://webservices.amazon.com/onca/xml?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Signature=M%2Fy0%2BEAFFGaUAp4bWv%2FWEuXYah99pVsxvqtAuC8YN7I%3D&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06"
+ };
+
+ ret = _sign_query
("http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Operation=ItemLookup&ItemId=0679722769&ResponseGroup=ItemAttributes,Offers,Images,Reviews&Version=2009-01-06",
"2009-01-01T12:00:00Z", "1234567890", steps);
+ g_free (ret);
+}
+
+static void
+test_amz2 (void)
+{
+ char *ret;
+ const char *steps[10];
+ memset (steps, 0, sizeof(steps));
+ steps[6] =
"GET\necs.amazonaws.co.uk\n/onca/xml\nAWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Actor=Johnny%20Depp&AssociateTag=mytag-20&Operation=ItemSearch&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews%2CVariations&SearchIndex=DVD&Service=AWSECommerceService&Sort=salesrank&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01";
+// steps[9] =
"http://ecs.amazonaws.co.uk/onca/xml?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Actor=Johnny%20Depp&AssociateTag=mytag-20&Operation=ItemSearch&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews%2CVariations&SearchIndex=DVD&Service=AWSECommerceService&Signature=TuM6E5L9u%2FuNqOX09ET03BXVmHLVFfJIna5cxXuHxiU%3D&Sort=salesrank&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01";
+
+ ret = _sign_query
("http://ecs.amazonaws.co.uk/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Operation=ItemSearch&Actor=Johnny%20Depp&ResponseGroup=ItemAttributes,Offers,Images,Reviews,Variations&Version=2009-01-01&SearchIndex=DVD&Sort=salesrank&AssociateTag=mytag-20",
"2009-01-01T12:00:00Z", "1234567890", steps);
+ g_free (ret);
+}
+
+static void
+test_amz3 (void)
+{
+ char *ret;
+ const char *steps[10];
+ memset (steps, 0, sizeof(steps));
+ steps[6] =
"GET\nwebservices.amazon.com\n/onca/xml\nAWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&Item.1.OfferListingId=j8ejq9wxDfSYWf2OCp6XQGDsVrWhl08GSQ9m5j%2Be8MS449BN1XGUC3DfU5Zw4nt%2FFBt87cspLow1QXzfvZpvzg%3D%3D&Item.1.Quantity=3&Operation=CartCreate&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01";
+// steps[9] =
"http://webservices.amazon.com/onca/xml?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&Item.1.OfferListingId=j8ejq9wxDfSYWf2OCp6XQGDsVrWhl08GSQ9m5j%2Be8MS449BN1XGUC3DfU5Zw4nt%2FFBt87cspLow1QXzfvZpvzg%3D%3D&Item.1.Quantity=3&Operation=CartCreate&Service=AWSECommerceService&Signature=cF3UtjbJb1%2BxDh387C%2FEmS1BCtS%2FZ01taykBCGemvUU%3D&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01";
+
+ ret = _sign_query
("http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Operation=CartCreate&Version=2009-01-01&Item.1.OfferListingId=j8ejq9wxDfSYWf2OCp6XQGDsVrWhl08GSQ9m5j%2Be8MS449BN1XGUC3DfU5Zw4nt%2FFBt87cspLow1QXzfvZpvzg%3D%3D&Item.1.Quantity=3&AssociateTag=mytag-20",
"2009-01-01T12:00:00Z", "1234567890", steps);
+ g_free (ret);
+}
+
+static void
+test_amz4 (void)
+{
+ char *ret;
+ const char *steps[10];
+ memset (steps, 0, sizeof(steps));
+ steps[6] =
"GET\nwebservices.amazon.com\n/onca/xml\nAWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&BrowseNodeId=465600&Operation=BrowseNodeLookup&ResponseGroup=BrowseNodeInfo%2CTopSellers%2CNewReleases%2CMostWishedFor%2CMostGifted&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01";
+// steps[9] =
"http://webservices.amazon.com/onca/xml?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&BrowseNodeId=465600&Operation=BrowseNodeLookup&ResponseGroup=BrowseNodeInfo%2CTopSellers%2CNewReleases%2CMostWishedFor%2CMostGifted&Service=AWSECommerceService&Signature=kEXxAIqhh6eBhLhrVMz2gt3ocMaH%2FOBVPbjvc9TG8ao%3D&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01";
+
+ ret = _sign_query
("http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Operation=BrowseNodeLookup&Version=2009-01-01&BrowseNodeId=465600&AssociateTag=mytag-20&ResponseGroup=BrowseNodeInfo,TopSellers,NewReleases,MostWishedFor,MostGifted",
"2009-01-01T12:00:00Z", "1234567890", steps);
+ g_free (ret);
+}
+
+static void
+test_amz5 (void)
+{
+ /* This is completely busted,
+ * there's a ";" that somebody typoed into
+ * the GET request example */
+#if 0
+ char *ret;
+ const char *steps[10];
+ memset (steps, 0, sizeof(steps));
+ steps[6] =
"GET\nwebservices.amazon.com\n/onca/xml\nAWSAccessKeyId=AKIAIOSFODNN7EXAMPLE;&AssociateTag=mytag-20&Condition=New&ItemId=B0011ZK6PC%2CB000NK8EWI&Merchant=Amazon&Operation=SimilarityLookup&ResponseGroup=Offers%2CItemAttributes&Service=AWSECommerceService&SimilarityType=Intersection&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01";
+ steps[9] =
"http://webservices.amazon.com/onca/xml?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&Condition=New&ItemId=B0011ZK6PC%2CB000NK8EWI&Merchant=Amazon&Operation=SimilarityLookup&ResponseGroup=Offers%2CItemAttributes&Service=AWSECommerceService&Signature=I2pbqxuS%2FmZK6Apwz0oLBxJn2wDL5n4kFQhgYWgLM7I%3D&SimilarityType=Intersection&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-01";
+
+ ret = _sign_query
("http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Operation=SimilarityLookup&ItemId=B0011ZK6PC,B000NK8EWI&Version=2009-01-01&AssociateTag=mytag-20&ResponseGroup=Offers,ItemAttributes&SimilarityType=Intersection&Condition=New&Merchant=Amazon",
"2009-01-01T12:00:00Z", "1234567890", steps);
+ g_free (ret);
+#endif
+}
+
+static void
test_ean (void)
{
DiscidentEan *ean;
@@ -347,6 +447,7 @@ int main (int argc, char **argv)
g_test_add_func ("/discident/ean-amz-browsenodelookup", test_amz4);
g_test_add_func ("/discident/ean-amz-similaritylookup", test_amz5);
g_test_add_func ("/discident/rl-parse", test_rl_parse);
+ g_test_add_func ("/discident/amz-parse", test_amz_parse);
g_test_add_func ("/discident/ean", test_ean);
g_test_add_func ("/discident/ean_fail", test_ean_fail);
g_test_add_func ("/discident/hash", test_hash);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]