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