1 /*
2  * Copyright (C) 2019-2021 Nicola Di Lieto <nicola.dilieto@gmail.com>
3  *
4  * This file is part of uacme.
5  *
6  * uacme is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * uacme is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 #include <err.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "curlwrap.h"
29 
curldata_calloc(void)30 curldata_t *curldata_calloc(void)
31 {
32     curldata_t *c = calloc(1, sizeof(curldata_t));
33     if (!c) {
34         warn("curldata_calloc: calloc failed");
35         return NULL;
36     }
37     c->body = strdup("");
38     if (!c->body) {
39         warn("curldata_calloc: strdup failed");
40         free(c);
41         return NULL;
42     }
43     c->headers = strdup("");
44     if (!c->headers) {
45         warn("curldata_calloc: strdup failed");
46         free(c->body);
47         free(c);
48         return NULL;
49     }
50     return c;
51 }
52 
curldata_free(curldata_t * c)53 void curldata_free(curldata_t *c)
54 {
55     if (!c)
56         return;
57     free(c->body);
58     free(c->headers);
59     free(c);
60 }
61 
curl_hcb(char * buf,size_t size,size_t n,void * userdata)62 static size_t curl_hcb(char *buf, size_t size, size_t n, void *userdata)
63 {
64     curldata_t *c = (curldata_t *)userdata;
65     void *p = realloc(c->headers, c->headers_len + size * n + 1);
66     if (!p) {
67         warn("curl_hcb: realloc failed");
68         return 0;
69     }
70     c->headers = p;
71     memcpy(c->headers + c->headers_len, buf, size * n);
72     c->headers_len += size * n;
73     c->headers[c->headers_len] = 0;
74     return size * n;
75 }
76 
curl_wcb(void * ptr,size_t size,size_t n,void * userdata)77 static size_t curl_wcb(void *ptr, size_t size, size_t n, void *userdata)
78 {
79     curldata_t *c = (curldata_t *)userdata;
80     void *p = realloc(c->body, c->body_len + size * n + 1);
81     if (!p) {
82         warn("curl_wcb: realloc failed");
83         return 0;
84     }
85     c->body = p;
86     memcpy(c->body + c->body_len, ptr, size * n);
87     c->body_len += size * n;
88     c->body[c->body_len] = 0;
89     return size * n;
90 }
91 
curl_get(const char * url)92 curldata_t *curl_get(const char *url)
93 {
94     curldata_t *c = NULL;
95     for (int retry = 0; retry < 3; retry++) {
96         CURL *curl;
97         CURLcode res;
98         curl = curl_easy_init();
99         if (!curl) {
100             warnx("curl_get: curl_easy_init failed");
101             return NULL;
102         }
103         c = curldata_calloc();
104         if (!c) {
105             warnx("curl_get: curldata_calloc failed");
106             curl_easy_cleanup(curl);
107             return NULL;
108         }
109         curl_easy_setopt(curl, CURLOPT_URL, url);
110         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_wcb);
111         curl_easy_setopt(curl, CURLOPT_WRITEDATA, c);
112         curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_hcb);
113         curl_easy_setopt(curl, CURLOPT_HEADERDATA, c);
114         curl_easy_setopt(curl, CURLOPT_USERAGENT,
115                 "uacme/" VERSION " (https://github.com/ndilieto/uacme)");
116         res = curl_easy_perform(curl);
117         if (res != CURLE_OK) {
118             warnx("curl_get: GET %s failed: %s", url,
119                     curl_easy_strerror(res));
120             curldata_free(c);
121             c = NULL;
122         } else {
123             long code = -1;
124             curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
125             c->code = code;
126         }
127         curl_easy_cleanup(curl);
128         if (c)
129             break;
130         else if (retry < 3) {
131             warnx("curl_get: waiting 5 seconds before retrying");
132             sleep(5);
133         }
134     }
135     return c;
136 }
137 
curl_post(const char * url,void * post_data,size_t post_size,const char * header,...)138 curldata_t *curl_post(const char *url, void *post_data, size_t post_size,
139         const char *header, ...)
140 {
141     va_list ap;
142     curldata_t *c = NULL;
143     for (int retry = 0; retry < 3; retry++) {
144         CURL *curl;
145         CURLcode res;
146         struct curl_slist *list = NULL;
147         curl = curl_easy_init();
148         if (!curl) {
149             warnx("curl_post: curl_easy_init failed");
150             return NULL;
151         }
152         c = curldata_calloc();
153         if (!c) {
154             warnx("curl_post: curldata_calloc failed");
155             curl_easy_cleanup(curl);
156             return NULL;
157         }
158         curl_easy_setopt(curl, CURLOPT_URL, url);
159         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_wcb);
160         curl_easy_setopt(curl, CURLOPT_WRITEDATA, c);
161         curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_hcb);
162         curl_easy_setopt(curl, CURLOPT_HEADERDATA, c);
163         curl_easy_setopt(curl, CURLOPT_USERAGENT,
164                 "uacme/" VERSION " (https://github.com/ndilieto/uacme)");
165         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
166         curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_size);
167         va_start(ap, header);
168         while (header) {
169             list = curl_slist_append(list, header);
170             header = va_arg(ap, const char *);
171         }
172         va_end(ap);
173         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
174         res = curl_easy_perform(curl);
175         curl_slist_free_all(list);
176         if (res != CURLE_OK) {
177             warnx("curl_post: POST %s failed: %s", url,
178                     curl_easy_strerror(res));
179             curldata_free(c);
180             c = NULL;
181         } else {
182             long code = -1;
183             curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
184             c->code = code;
185         }
186         curl_easy_cleanup(curl);
187         if (c)
188             break;
189         else if (retry < 3) {
190             warnx("curl_post: waiting 5 seconds before retrying");
191             sleep(5);
192         }
193     }
194     return c;
195 }
196