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