]> git.ipfire.org Git - people/ms/libloc.git/blob - src/country.c
database: Create a connection pool for async operation
[people/ms/libloc.git] / src / country.c
1 /*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2019 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library 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 GNU
14 Lesser General Public License for more details.
15 */
16
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include <libloc/libloc.h>
22 #include <libloc/compat.h>
23 #include <libloc/country.h>
24 #include <libloc/network.h>
25 #include <libloc/private.h>
26
27 static const struct loc_special_country {
28 const char code[3];
29 enum loc_network_flags flags;
30 } loc_special_countries[] = {
31 { "A1", LOC_NETWORK_FLAG_ANONYMOUS_PROXY },
32 { "A2", LOC_NETWORK_FLAG_SATELLITE_PROVIDER },
33 { "A3", LOC_NETWORK_FLAG_ANYCAST },
34 { "XD", LOC_NETWORK_FLAG_DROP },
35 { "", 0 },
36 };
37
38 struct loc_country {
39 struct loc_ctx* ctx;
40 int refcount;
41
42 // Store the country code in a 3 byte buffer. Two bytes for the code, and NULL so
43 // that we can use strcmp() and return a pointer.
44 char code[3];
45 char continent_code[3];
46
47 char* name;
48 };
49
50 LOC_EXPORT int loc_country_new(struct loc_ctx* ctx, struct loc_country** country, const char* country_code) {
51 // Check of the country code is valid
52 if (!loc_country_code_is_valid(country_code)) {
53 errno = EINVAL;
54 return 1;
55 }
56
57 struct loc_country* c = calloc(1, sizeof(*c));
58 if (!c)
59 return 1;
60
61 c->ctx = loc_ref(ctx);
62 c->refcount = 1;
63
64 // Set the country code
65 loc_country_code_copy(c->code, country_code);
66
67 DEBUG(c->ctx, "Country %s allocated at %p\n", c->code, c);
68 *country = c;
69
70 return 0;
71 }
72
73 LOC_EXPORT struct loc_country* loc_country_ref(struct loc_country* country) {
74 country->refcount++;
75
76 return country;
77 }
78
79 static void loc_country_free(struct loc_country* country) {
80 DEBUG(country->ctx, "Releasing country %s %p\n", country->code, country);
81
82 if (country->name)
83 free(country->name);
84
85 loc_unref(country->ctx);
86 free(country);
87 }
88
89 LOC_EXPORT struct loc_country* loc_country_unref(struct loc_country* country) {
90 if (--country->refcount > 0)
91 return NULL;
92
93 loc_country_free(country);
94 return NULL;
95 }
96
97 LOC_EXPORT const char* loc_country_get_code(struct loc_country* country) {
98 return country->code;
99 }
100
101 LOC_EXPORT const char* loc_country_get_continent_code(struct loc_country* country) {
102 if (!*country->continent_code)
103 return NULL;
104
105 return country->continent_code;
106 }
107
108 LOC_EXPORT int loc_country_set_continent_code(struct loc_country* country, const char* continent_code) {
109 // Check for valid input
110 if (!continent_code || strlen(continent_code) != 2) {
111 errno = EINVAL;
112 return 1;
113 }
114
115 // Store the code
116 loc_country_code_copy(country->continent_code, continent_code);
117
118 return 0;
119 }
120
121 LOC_EXPORT const char* loc_country_get_name(struct loc_country* country) {
122 return country->name;
123 }
124
125 LOC_EXPORT int loc_country_set_name(struct loc_country* country, const char* name) {
126 if (country->name)
127 free(country->name);
128
129 if (name) {
130 country->name = strdup(name);
131
132 // Report error if we could not copy the string
133 if (!country->name)
134 return 1;
135 }
136
137 return 0;
138 }
139
140 LOC_EXPORT int loc_country_cmp(struct loc_country* country1, struct loc_country* country2) {
141 return strncmp(country1->code, country2->code, 2);
142 }
143
144 int loc_country_new_from_database_v1(struct loc_ctx* ctx, struct loc_stringpool* pool,
145 struct loc_country** country, const struct loc_database_country_v1* dbobj) {
146 char buffer[3] = "XX";
147
148 // Read country code
149 loc_country_code_copy(buffer, dbobj->code);
150
151 // Create a new country object
152 int r = loc_country_new(ctx, country, buffer);
153 if (r)
154 return r;
155
156 // Copy continent code
157 if (*dbobj->continent_code)
158 loc_country_code_copy((*country)->continent_code, dbobj->continent_code);
159
160 // Set name
161 const char* name = loc_stringpool_get(pool, be32toh(dbobj->name));
162 if (name) {
163 r = loc_country_set_name(*country, name);
164 if (r)
165 goto FAIL;
166 }
167
168 return 0;
169
170 FAIL:
171 loc_country_unref(*country);
172 return r;
173 }
174
175 int loc_country_to_database_v1(struct loc_country* country,
176 struct loc_stringpool* pool, struct loc_database_country_v1* dbobj) {
177 off_t name = 0;
178
179 // Add country code
180 if (*country->code)
181 loc_country_code_copy(dbobj->code, country->code);
182
183 // Add continent code
184 if (*country->continent_code)
185 loc_country_code_copy(dbobj->continent_code, country->continent_code);
186
187 // Save the name string in the string pool
188 if (country->name)
189 name = loc_stringpool_add(pool, country->name);
190
191 dbobj->name = htobe32(name);
192
193 return 0;
194 }
195
196 LOC_EXPORT int loc_country_code_is_valid(const char* cc) {
197 // It cannot be NULL
198 if (!cc || !*cc)
199 return 0;
200
201 // It must be 2 characters long
202 if (strlen(cc) != 2)
203 return 0;
204
205 // It must only contain A-Z
206 for (unsigned int i = 0; i < 2; i++) {
207 if (cc[i] < 'A' || cc[i] > 'Z')
208 return 0;
209 }
210
211 // The code cannot begin with an X (those are reserved for private use)
212 if (*cc == 'X')
213 return 0;
214
215 // Looks valid
216 return 1;
217 }
218
219 LOC_EXPORT int loc_country_special_code_to_flag(const char* cc) {
220 // Check if we got some input
221 if (!cc || !*cc) {
222 errno = EINVAL;
223 return -1;
224 }
225
226 // Return flags for any known special country
227 for (const struct loc_special_country* country = loc_special_countries;
228 country->flags; country++) {
229 if (strncmp(country->code, cc, 2) == 0)
230 return country->flags;
231 }
232
233 return 0;
234 }