New utility yaz_url: fetches HTTP content
[yaz-moved-to-github.git] / src / url.c
diff --git a/src/url.c b/src/url.c
new file mode 100644 (file)
index 0000000..eb0ed58
--- /dev/null
+++ b/src/url.c
@@ -0,0 +1,156 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2011 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file url.c
+ * \brief URL fetch utility
+ */
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <yaz/url.h>
+#include <yaz/comstack.h>
+#include <yaz/log.h>
+
+struct yaz_url {
+    ODR odr_in;
+    ODR odr_out;
+    char *proxy;
+};
+
+yaz_url_t yaz_url_create(void)
+{
+    yaz_url_t p = xmalloc(sizeof(*p));
+    p->odr_in = odr_createmem(ODR_DECODE);
+    p->odr_out = odr_createmem(ODR_ENCODE);
+    p->proxy = 0;
+    return p;
+}
+
+void yaz_url_destroy(yaz_url_t p)
+{
+    if (p)
+    {
+        odr_destroy(p->odr_in);
+        odr_destroy(p->odr_out);
+        xfree(p->proxy);
+        xfree(p);
+    }
+}
+
+void yaz_url_set_proxy(yaz_url_t p, const char *proxy)
+{
+    xfree(p->proxy);
+    p->proxy = 0;
+    if (proxy && *proxy)
+        p->proxy = xstrdup(proxy);
+}
+
+Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri,
+                              const char *method,
+                              Z_HTTP_Header *headers,
+                              const char *buf, size_t len)
+{
+    Z_HTTP_Response *res = 0;
+    int number_of_redirects = 0;
+
+    while (1)
+    {
+        void *add;
+        COMSTACK conn = 0;
+        int code;
+        struct Z_HTTP_Header **last_header_entry;
+        const char *location = 0;
+        Z_GDU *gdu = z_get_HTTP_Request_uri(p->odr_out, uri, 0,
+                                            p->proxy ? 1 : 0);
+        gdu->u.HTTP_Request->method = odr_strdup(p->odr_out, method);
+
+        res = 0;
+        last_header_entry = &gdu->u.HTTP_Request->headers;
+        while (*last_header_entry)
+            last_header_entry = &(*last_header_entry)->next;
+        *last_header_entry = headers; /* attach user headers */
+
+        if (buf && len)
+        {
+            gdu->u.HTTP_Request->content_buf = (char *) buf;
+            gdu->u.HTTP_Request->content_len = len;
+        }
+        if (!z_GDU(p->odr_out, &gdu, 0, 0))
+        {
+            yaz_log(YLOG_WARN, "Can not encode HTTP request URL:%s", uri);
+            return 0;
+        }
+        conn = cs_create_host_proxy(uri, 1, &add, p->proxy);
+        if (!conn)
+        {
+            yaz_log(YLOG_WARN, "Bad address for URL:%s", uri);
+        }
+        else if (cs_connect(conn, add) < 0)
+        {
+            yaz_log(YLOG_WARN, "Can not connect to URL:%s", uri);
+        }
+        else
+        {
+            int len;
+            char *buf = odr_getbuf(p->odr_out, &len, 0);
+            
+            if (cs_put(conn, buf, len) < 0)
+                yaz_log(YLOG_WARN, "cs_put failed URL:%s", uri);
+            else
+            {
+                char *netbuffer = 0;
+                int netlen = 0;
+                int cs_res = cs_get(conn, &netbuffer, &netlen);
+                if (cs_res <= 0)
+                {
+                    yaz_log(YLOG_WARN, "cs_get failed URL:%s", uri);
+                }
+                else
+                {
+                    Z_GDU *gdu;
+                    odr_setbuf(p->odr_in, netbuffer, cs_res, 0);
+                    if (!z_GDU(p->odr_in, &gdu, 0, 0)
+                        || gdu->which != Z_GDU_HTTP_Response)
+                    {
+                        yaz_log(YLOG_WARN, "HTTP decoding failed "
+                                "URL:%s", uri);
+                    }
+                    else
+                    {
+                        res = gdu->u.HTTP_Response;
+                    }
+                }
+                xfree(netbuffer);
+            }
+        }
+        if (conn)
+            cs_close(conn);
+        if (!res)
+            break;
+        code = res->code;
+        location = z_HTTP_header_lookup(res->headers, "Location");
+        if (++number_of_redirects < 10 &&
+            location && (code == 301 || code == 302 || code == 307))
+        {
+            odr_reset(p->odr_out);
+            uri = odr_strdup(p->odr_out, location);
+            odr_reset(p->odr_in);
+        }
+        else
+            break;
+    }
+    return res;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+