add a utility function to detect redirects and handle them
[project/uclient.git] / uclient.c
1 #include <libubox/ustream-ssl.h>
2 #include "uclient.h"
3 #include "uclient-utils.h"
4 #include "uclient-backend.h"
5
6 struct uclient_url * __hidden
7 uclient_get_url(const char *url_str, const char *auth_str)
8 {
9 static const struct uclient_backend *backends[] = {
10 &uclient_backend_http,
11 };
12
13 const struct uclient_backend *backend;
14 const char * const *prefix = NULL;
15 struct uclient_url *url;
16 const char *location;
17 char *host_buf, *uri_buf, *auth_buf, *next;
18 int i, host_len;
19
20 for (i = 0; i < ARRAY_SIZE(backends); i++) {
21 int prefix_len = 0;
22
23 for (prefix = backends[i]->prefix; *prefix; prefix++) {
24 prefix_len = strlen(*prefix);
25
26 if (!strncmp(url_str, *prefix, prefix_len))
27 break;
28 }
29
30 if (!*prefix)
31 continue;
32
33 url_str += prefix_len;
34 backend = backends[i];
35 break;
36 }
37
38 if (!*prefix)
39 return NULL;
40
41 next = strchr(url_str, '/');
42 if (next) {
43 location = next;
44 host_len = next - url_str;
45 } else {
46 location = "/";
47 host_len = strlen(url_str);
48 }
49
50 url = calloc_a(sizeof(*url),
51 &host_buf, host_len + 1,
52 &uri_buf, strlen(location) + 1,
53 &auth_buf, auth_str ? strlen(auth_str) + 1 : 0);
54
55 url->backend = backend;
56 url->location = strcpy(uri_buf, location);
57
58 url->host = strncpy(host_buf, url_str, host_len);
59
60 next = strchr(host_buf, '@');
61 if (next) {
62 *next = 0;
63 url->host = next + 1;
64
65 if (uclient_urldecode(host_buf, host_buf, false) < 0)
66 goto free;
67
68 url->auth = host_buf;
69 }
70
71 if (!url->auth && auth_str)
72 url->auth = strcpy(auth_buf, auth_str);
73
74 /* Literal IPv6 address */
75 if (*url->host == '[') {
76 url->host++;
77 next = strrchr(url->host, ']');
78 if (!next)
79 goto free;
80
81 *(next++) = 0;
82 if (*next == ':')
83 url->port = next + 1;
84 } else {
85 next = strrchr(url->host, ':');
86 if (next)
87 url->port = next + 1;
88 }
89
90 return url;
91
92 free:
93 free(url);
94 return NULL;
95 }
96
97 struct uclient *uclient_new(const char *url_str, const struct uclient_cb *cb)
98 {
99 struct uclient *cl;
100 struct uclient_url *url;
101
102 url = uclient_get_url(url_str, NULL);
103 if (!url)
104 return NULL;
105
106 cl = url->backend->alloc();
107 if (!cl)
108 return NULL;
109
110 cl->backend = url->backend;
111 cl->cb = cb;
112 cl->url = url;
113
114 return cl;
115 }
116
117 int uclient_connect_url(struct uclient *cl, const char *url_str)
118 {
119 struct uclient_url *url = cl->url;
120 const struct uclient_backend *backend = cl->backend;
121
122 if (url_str) {
123 url = uclient_get_url(url_str, NULL);
124 if (!url)
125 return -1;
126
127 if (url->backend != cl->backend)
128 return -1;
129
130 free(cl->url);
131 cl->url = url;
132
133 if (backend->update_url)
134 backend->update_url(cl);
135 }
136
137 return backend->connect(cl);
138 }
139
140 void uclient_free(struct uclient *cl)
141 {
142 struct uclient_url *url = cl->url;
143
144 if (cl->backend->free)
145 cl->backend->free(cl);
146 else
147 free(cl);
148
149 free(url);
150 }
151
152 int uclient_write(struct uclient *cl, char *buf, int len)
153 {
154 if (!cl->backend->write)
155 return -1;
156
157 return cl->backend->write(cl, buf, len);
158 }
159
160 int uclient_request(struct uclient *cl)
161 {
162 if (!cl->backend->request)
163 return -1;
164
165 return cl->backend->request(cl);
166 }
167
168 int uclient_read(struct uclient *cl, char *buf, int len)
169 {
170 if (!cl->backend->read)
171 return -1;
172
173 return cl->backend->read(cl, buf, len);
174 }
175
176 static void __uclient_backend_change_state(struct uloop_timeout *timeout)
177 {
178 struct uclient *cl = container_of(timeout, struct uclient, timeout);
179
180 if (cl->error && cl->cb->error)
181 cl->cb->error(cl);
182 else if (cl->eof && cl->cb->data_eof)
183 cl->cb->data_eof(cl);
184 }
185
186 static void uclient_backend_change_state(struct uclient *cl)
187 {
188 cl->timeout.cb = __uclient_backend_change_state;
189 uloop_timeout_set(&cl->timeout, 1);
190 }
191
192 void __hidden uclient_backend_set_error(struct uclient *cl)
193 {
194 if (cl->error)
195 return;
196
197 cl->error = true;
198 uclient_backend_change_state(cl);
199 }
200
201 void __hidden uclient_backend_set_eof(struct uclient *cl)
202 {
203 if (cl->eof || cl->error)
204 return;
205
206 cl->eof = true;
207 uclient_backend_change_state(cl);
208 }
209
210 void __hidden uclient_backend_reset_state(struct uclient *cl)
211 {
212 cl->error = false;
213 cl->eof = false;
214 uloop_timeout_cancel(&cl->timeout);
215 }