Fix change in source repo
[yaz-moved-to-github.git] / src / url.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file url.c
7  * \brief URL fetch utility
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <yaz/url.h>
14 #include <yaz/comstack.h>
15 #include <yaz/log.h>
16
17 struct yaz_url {
18     ODR odr_in;
19     ODR odr_out;
20     char *proxy;
21 };
22
23 yaz_url_t yaz_url_create(void)
24 {
25     yaz_url_t p = xmalloc(sizeof(*p));
26     p->odr_in = odr_createmem(ODR_DECODE);
27     p->odr_out = odr_createmem(ODR_ENCODE);
28     p->proxy = 0;
29     return p;
30 }
31
32 void yaz_url_destroy(yaz_url_t p)
33 {
34     if (p)
35     {
36         odr_destroy(p->odr_in);
37         odr_destroy(p->odr_out);
38         xfree(p->proxy);
39         xfree(p);
40     }
41 }
42
43 void yaz_url_set_proxy(yaz_url_t p, const char *proxy)
44 {
45     xfree(p->proxy);
46     p->proxy = 0;
47     if (proxy && *proxy)
48         p->proxy = xstrdup(proxy);
49 }
50
51 static void extract_user_pass(NMEM nmem,
52                               const char *uri,
53                               char **uri_lean, char **http_user,
54                               char **http_pass)
55 {
56     const char *cp1 = strchr(uri, '/');
57     *uri_lean = 0;
58     *http_user = 0;
59     *http_pass = 0;
60     if (cp1 && cp1 > uri)
61     {
62         cp1--;
63
64         if (!strncmp(cp1, "://", 3))
65         {
66             const char *cp3 = 0;
67             const char *cp2 = cp1 + 3;
68             while (*cp2 && *cp2 != '/' && *cp2 != '@')
69             {
70                 if (*cp2 == ':')
71                     cp3 = cp2;
72                 cp2++;
73             }
74             if (*cp2 == '@' && cp3)
75             {
76                 *uri_lean = nmem_malloc(nmem, strlen(uri) + 1);
77                 memcpy(*uri_lean, uri, cp1 + 3 - uri);
78                 strcpy(*uri_lean + (cp1 + 3 - uri), cp2 + 1);
79
80                 *http_user = nmem_strdupn(nmem, cp1 + 3, cp3 - (cp1 + 3));
81                 *http_pass = nmem_strdupn(nmem, cp3 + 1, cp2 - (cp3 + 1));
82             }
83         }
84     }
85     if (*uri_lean == 0)
86         *uri_lean = nmem_strdup(nmem, uri);
87 }
88
89 Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri,
90                               const char *method,
91                               Z_HTTP_Header *user_headers,
92                               const char *buf, size_t len)
93 {
94     Z_HTTP_Response *res = 0;
95     int number_of_redirects = 0;
96
97     while (1)
98     {
99         void *add;
100         COMSTACK conn = 0;
101         int code;
102         const char *location = 0;
103         char *http_user = 0;
104         char *http_pass = 0;
105         char *uri_lean = 0;
106         Z_GDU *gdu;
107
108         extract_user_pass(p->odr_out->mem, uri, &uri_lean,
109                           &http_user, &http_pass);
110
111         gdu = z_get_HTTP_Request_uri(p->odr_out, uri_lean, 0, p->proxy ? 1 : 0);
112         gdu->u.HTTP_Request->method = odr_strdup(p->odr_out, method);
113
114         for ( ; user_headers; user_headers = user_headers->next)
115         {
116             /* prefer new Host over user-supplied Host */
117             if (!strcmp(user_headers->name, "Host"))
118                 ;
119             /* prefer user-supplied User-Agent over YAZ' own */
120             else if (!strcmp(user_headers->name, "User-Agent"))
121                 z_HTTP_header_set(p->odr_out, &gdu->u.HTTP_Request->headers,
122                                   user_headers->name, user_headers->value);
123             else
124                 z_HTTP_header_add(p->odr_out, &gdu->u.HTTP_Request->headers,
125                                   user_headers->name, user_headers->value);
126         }
127         if (http_user && http_pass)
128             z_HTTP_header_add_basic_auth(p->odr_out,
129                                          &gdu->u.HTTP_Request->headers,
130                                          http_user, http_pass);
131
132         res = 0;
133         if (buf && len)
134         {
135             gdu->u.HTTP_Request->content_buf = (char *) buf;
136             gdu->u.HTTP_Request->content_len = len;
137         }
138         if (!z_GDU(p->odr_out, &gdu, 0, 0))
139         {
140             yaz_log(YLOG_WARN, "Can not encode HTTP request URL:%s", uri);
141             return 0;
142         }
143         conn = cs_create_host_proxy(uri_lean, 1, &add, p->proxy);
144         if (!conn)
145         {
146             yaz_log(YLOG_WARN, "Could not resolve URL: %s", uri);
147         }
148         else if (cs_connect(conn, add) < 0)
149         {
150             yaz_log(YLOG_WARN, "Can not connect to URL: %s", uri);
151         }
152         else
153         {
154             int len;
155             char *buf = odr_getbuf(p->odr_out, &len, 0);
156
157             if (cs_put(conn, buf, len) < 0)
158                 yaz_log(YLOG_WARN, "cs_put failed URL: %s", uri);
159             else
160             {
161                 char *netbuffer = 0;
162                 int netlen = 0;
163                 int cs_res = cs_get(conn, &netbuffer, &netlen);
164                 if (cs_res <= 0)
165                 {
166                     yaz_log(YLOG_WARN, "cs_get failed URL: %s", uri);
167                 }
168                 else
169                 {
170                     Z_GDU *gdu;
171                     odr_setbuf(p->odr_in, netbuffer, cs_res, 0);
172                     if (!z_GDU(p->odr_in, &gdu, 0, 0)
173                         || gdu->which != Z_GDU_HTTP_Response)
174                     {
175                         yaz_log(YLOG_WARN, "HTTP decoding failed "
176                                 "URL:%s", uri);
177                     }
178                     else
179                     {
180                         res = gdu->u.HTTP_Response;
181                     }
182                 }
183                 xfree(netbuffer);
184             }
185         }
186         if (conn)
187             cs_close(conn);
188         if (!res)
189             break;
190         code = res->code;
191         location = z_HTTP_header_lookup(res->headers, "Location");
192         if (++number_of_redirects < 10 &&
193             location && (code == 301 || code == 302 || code == 307))
194         {
195             odr_reset(p->odr_out);
196             uri = odr_strdup(p->odr_out, location);
197             odr_reset(p->odr_in);
198         }
199         else
200             break;
201     }
202     return res;
203 }
204
205 /*
206  * Local variables:
207  * c-basic-offset: 4
208  * c-file-style: "Stroustrup"
209  * indent-tabs-mode: nil
210  * End:
211  * vim: shiftwidth=4 tabstop=8 expandtab
212  */
213