1 /*
2 * Copyright (c) 2015-2021 Free Software Foundation, Inc.
3 *
4 * This file is part of libwget.
5 *
6 * Libwget is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser 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 * Libwget is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with libwget. If not, see <https://www.gnu.org/licenses/>.
18 *
19 *
20 * .netrc routines
21 *
22 * Changelog
23 * 01.11.2015 Tim Ruehsen created
24 *
25 */
26
27 #include <config.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <time.h>
34 #include <errno.h>
35
36 #include <wget.h>
37 #include "private.h"
38
39 struct wget_netrc_db_st {
40 wget_hashmap *
41 machines;
42 };
43
44 #ifdef __clang__
45 __attribute__((no_sanitize("integer")))
46 #endif
47 WGET_GCC_PURE
hash_netrc(const wget_netrc * netrc)48 static unsigned int hash_netrc(const wget_netrc *netrc)
49 {
50 unsigned int hash = 0;
51 const unsigned char *p;
52
53 for (p = (unsigned char *)netrc->host; *p; p++)
54 hash = hash * 101 + *p;
55
56 return hash;
57 }
58
59 WGET_GCC_NONNULL_ALL WGET_GCC_PURE
compare_netrc(const wget_netrc * h1,const wget_netrc * h2)60 static int compare_netrc(const wget_netrc *h1, const wget_netrc *h2)
61 {
62 return wget_strcmp(h1->host, h2->host);
63 }
64
wget_netrc_init(wget_netrc * netrc)65 wget_netrc *wget_netrc_init(wget_netrc *netrc)
66 {
67 if (!netrc) {
68 if (!(netrc = wget_calloc(1, sizeof(wget_netrc))))
69 return NULL;
70 } else
71 memset(netrc, 0, sizeof(*netrc));
72
73 return netrc;
74 }
75
wget_netrc_deinit(wget_netrc * netrc)76 void wget_netrc_deinit(wget_netrc *netrc)
77 {
78 if (netrc) {
79 xfree(netrc->host);
80 xfree(netrc->login);
81 xfree(netrc->password);
82 }
83 }
84
wget_netrc_free(wget_netrc * netrc)85 void wget_netrc_free(wget_netrc *netrc)
86 {
87 if (netrc) {
88 wget_netrc_deinit(netrc);
89 xfree(netrc);
90 }
91 }
92
wget_netrc_new(const char * machine,const char * login,const char * password)93 wget_netrc *wget_netrc_new(const char *machine, const char *login, const char *password)
94 {
95 wget_netrc *netrc = wget_netrc_init(NULL);
96
97 if (netrc) {
98 netrc->host = wget_strdup(machine);
99 netrc->login = wget_strdup(login);
100 netrc->password = wget_strdup(password);
101 }
102
103 return netrc;
104 }
105
wget_netrc_get(const wget_netrc_db * netrc_db,const char * host)106 wget_netrc *wget_netrc_get(const wget_netrc_db *netrc_db, const char *host)
107 {
108 if (netrc_db) {
109 wget_netrc netrc, *netrcp;
110
111 // look for an exact match
112 netrc.host = host;
113
114 if (wget_hashmap_get(netrc_db->machines, &netrc, &netrcp))
115 return netrcp;
116 }
117
118 return NULL;
119 }
120
wget_netrc_db_init(wget_netrc_db * netrc_db)121 wget_netrc_db *wget_netrc_db_init(wget_netrc_db *netrc_db)
122 {
123 wget_hashmap *machines = wget_hashmap_create(16, (wget_hashmap_hash_fn *) hash_netrc, (wget_hashmap_compare_fn *) compare_netrc);
124
125 if (!machines)
126 return NULL;
127
128 if (!netrc_db) {
129 if (!(netrc_db = wget_calloc(1, sizeof(wget_netrc_db)))) {
130 wget_hashmap_free(&machines);
131 return NULL;
132 }
133 } else
134 memset(netrc_db, 0, sizeof(*netrc_db));
135
136 wget_hashmap_set_key_destructor(machines, (wget_hashmap_key_destructor *) wget_netrc_free);
137 wget_hashmap_set_value_destructor(machines, (wget_hashmap_value_destructor *) wget_netrc_free);
138 netrc_db->machines = machines;
139
140 return netrc_db;
141 }
142
wget_netrc_db_deinit(wget_netrc_db * netrc_db)143 void wget_netrc_db_deinit(wget_netrc_db *netrc_db)
144 {
145 if (netrc_db) {
146 wget_hashmap_free(&netrc_db->machines);
147 }
148 }
149
wget_netrc_db_free(wget_netrc_db ** netrc_db)150 void wget_netrc_db_free(wget_netrc_db **netrc_db)
151 {
152 if (netrc_db) {
153 wget_netrc_db_deinit(*netrc_db);
154 xfree(*netrc_db);
155 }
156 }
157
wget_netrc_db_add(wget_netrc_db * netrc_db,wget_netrc * netrc)158 void wget_netrc_db_add(wget_netrc_db *netrc_db, wget_netrc *netrc)
159 {
160 if (!netrc)
161 return;
162
163 if (!netrc_db) {
164 wget_netrc_free(netrc);
165 return;
166 }
167
168 // key and value are the same to make wget_hashmap_get() return old 'netrc'
169 debug_printf("add .netrc %s (login=%s, password=*)\n", netrc->host, netrc->login);
170 wget_hashmap_put(netrc_db->machines, netrc, netrc);
171 // no need to free anything here
172 }
173
174 // load the .netrc file
175 // not thread-save
176
wget_netrc_db_load(wget_netrc_db * netrc_db,const char * fname)177 int wget_netrc_db_load(wget_netrc_db *netrc_db, const char *fname)
178 {
179 FILE *fp;
180
181 if (!netrc_db || !fname || !*fname)
182 return WGET_E_INVALID;
183
184 if (!(fp = fopen(fname, "r")))
185 return WGET_E_OPEN;
186
187 wget_netrc netrc;
188 char *buf = NULL, *linep, *p, *key = NULL;
189 size_t bufsize = 0;
190 ssize_t buflen;
191 int in_macdef = 0, in_machine = 0, nentries = 0;
192
193 while ((buflen = wget_getline(&buf, &bufsize, fp)) >= 0) {
194 linep = buf;
195
196 while (isspace(*linep)) linep++; // ignore leading whitespace
197
198 if (*linep == '#')
199 continue; // skip comments
200
201 // strip off \r\n
202 while (buflen > 0 && (buf[buflen] == '\n' || buf[buflen] == '\r'))
203 buf[--buflen] = 0;
204
205 if (!*linep) {
206 // empty lines reset macro processing
207 in_macdef = 0;
208 continue;
209 } else if (in_macdef)
210 continue; // still processing 'macdef' macro
211
212 // now we expect key value pairs, e.g.: machine example.com
213 do {
214 xfree(key);
215 while (isspace(*linep)) linep++;
216 for (p = linep; *linep && !isspace(*linep);) linep++;
217
218 if (!(key = wget_strmemdup(p, linep - p))) {
219 xfree(buf);
220 fclose(fp);
221 return WGET_E_MEMORY;
222 }
223
224 if (!strcmp(key, "machine") || !strcmp(key, "default")) {
225 if (in_machine)
226 wget_netrc_db_add(netrc_db, wget_memdup(&netrc, sizeof(netrc)));
227
228 wget_netrc_init(&netrc);
229 in_machine = 1;
230
231 if (!strcmp(key, "default")) {
232 netrc.host = wget_strdup("default");
233 continue;
234 }
235 } else if (!in_machine)
236 continue; // token outside of machine or default
237
238 while (isspace(*linep)) linep++;
239 for (p = linep; *linep && !isspace(*linep);) linep++;
240
241 if (!strcmp(key, "machine")) {
242 if (!netrc.host)
243 netrc.host = wget_strmemdup(p, linep - p);
244 } else if (!strcmp(key, "login")) {
245 if (!netrc.login)
246 netrc.login = wget_strmemdup(p, linep - p);
247 } else if (!strcmp(key, "password")) {
248 if (!netrc.password)
249 netrc.password = wget_strmemdup(p, linep - p);
250 } else if (!strcmp(key, "port")) { // GNU extension
251 netrc.port = (uint16_t) atoi(p);
252 } else if (!strcmp(key, "force")) { // GNU extension
253 netrc.force = !wget_strncasecmp_ascii("yes", p, 3);
254 } else if (!strcmp(key, "macdef")) {
255 in_macdef = 1; // the above code skips until next empty line
256 }
257 } while (*linep);
258
259 xfree(key);
260 }
261
262 if (in_machine)
263 wget_netrc_db_add(netrc_db, wget_memdup(&netrc, sizeof(netrc)));
264
265 xfree(buf);
266 fclose(fp);
267
268 nentries = wget_hashmap_size(netrc_db->machines);
269
270 debug_printf("loaded %d .netrc %s\n", nentries, nentries != 1 ? "entries" : "entry");
271
272 return nentries;
273 }
274