]> git.ipfire.org Git - people/ms/libloc.git/blame - src/country.c
country: Refactor storing country code and continent code
[people/ms/libloc.git] / src / country.c
CommitLineData
ec684c1a
MT
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
c12a1385
MT
21#include <libloc/libloc.h>
22#include <libloc/compat.h>
23#include <libloc/country.h>
3eb1eed6 24#include <libloc/network.h>
c12a1385 25#include <libloc/private.h>
ec684c1a 26
3eb1eed6
MT
27static 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
32b16159
MT
38#define CC_LEN 3
39
ec684c1a
MT
40struct loc_country {
41 struct loc_ctx* ctx;
42 int refcount;
43
32b16159
MT
44 // Store the country code in a 3 byte buffer. Two bytes for the code, and NULL so
45 // that we can use strcmp() and return a pointer.
46 char code[3];
47 char continent_code[3];
ec684c1a
MT
48
49 char* name;
50};
51
52LOC_EXPORT int loc_country_new(struct loc_ctx* ctx, struct loc_country** country, const char* country_code) {
e73b7709
MT
53 // Check of the country code is valid
54 if (!loc_country_code_is_valid(country_code)) {
55 errno = EINVAL;
56 return 1;
57 }
e646a8f3 58
ec684c1a
MT
59 struct loc_country* c = calloc(1, sizeof(*c));
60 if (!c)
198e382c 61 return 1;
ec684c1a
MT
62
63 c->ctx = loc_ref(ctx);
64 c->refcount = 1;
65
32b16159
MT
66 // Set the country code
67 loc_country_code_copy(c->code, country_code);
ec684c1a
MT
68
69 DEBUG(c->ctx, "Country %s allocated at %p\n", c->code, c);
70 *country = c;
71
72 return 0;
73}
74
75LOC_EXPORT struct loc_country* loc_country_ref(struct loc_country* country) {
76 country->refcount++;
77
78 return country;
79}
80
81static void loc_country_free(struct loc_country* country) {
82 DEBUG(country->ctx, "Releasing country %s %p\n", country->code, country);
83
ec684c1a
MT
84 if (country->name)
85 free(country->name);
86
87 loc_unref(country->ctx);
88 free(country);
89}
90
91LOC_EXPORT struct loc_country* loc_country_unref(struct loc_country* country) {
92 if (--country->refcount > 0)
93 return NULL;
94
95 loc_country_free(country);
ec684c1a
MT
96 return NULL;
97}
98
99LOC_EXPORT const char* loc_country_get_code(struct loc_country* country) {
100 return country->code;
101}
102
103LOC_EXPORT const char* loc_country_get_continent_code(struct loc_country* country) {
104 return country->continent_code;
105}
106
107LOC_EXPORT int loc_country_set_continent_code(struct loc_country* country, const char* continent_code) {
32b16159
MT
108 // Check for valid input
109 if (!continent_code || strlen(continent_code) != 2) {
110 errno = EINVAL;
111 return 1;
112 }
ec684c1a 113
32b16159
MT
114 // Store the code
115 loc_country_code_copy(country->continent_code, continent_code);
ec684c1a
MT
116
117 return 0;
118}
119
120LOC_EXPORT const char* loc_country_get_name(struct loc_country* country) {
121 return country->name;
122}
123
124LOC_EXPORT int loc_country_set_name(struct loc_country* country, const char* name) {
125 if (country->name)
126 free(country->name);
127
32b16159 128 if (name) {
ec684c1a
MT
129 country->name = strdup(name);
130
32b16159
MT
131 // Report error if we could not copy the string
132 if (!country->name)
133 return 1;
134 }
135
ec684c1a
MT
136 return 0;
137}
138
af208e26 139LOC_EXPORT int loc_country_cmp(struct loc_country* country1, struct loc_country* country2) {
32b16159 140 return strncmp(country1->code, country2->code, 2);
ec684c1a
MT
141}
142
b904896a
MT
143int loc_country_new_from_database_v1(struct loc_ctx* ctx, struct loc_stringpool* pool,
144 struct loc_country** country, const struct loc_database_country_v1* dbobj) {
32b16159 145 char buffer[3] = "XX";
ec684c1a
MT
146
147 // Read country code
a7a3d958 148 loc_country_code_copy(buffer, dbobj->code);
ec684c1a
MT
149
150 // Create a new country object
151 int r = loc_country_new(ctx, country, buffer);
152 if (r)
153 return r;
154
32b16159
MT
155 // Copy continent code
156 if (*dbobj->continent_code)
157 loc_country_code_copy((*country)->continent_code, dbobj->continent_code);
ec684c1a
MT
158
159 // Set name
ec684c1a
MT
160 const char* name = loc_stringpool_get(pool, be32toh(dbobj->name));
161 if (name) {
162 r = loc_country_set_name(*country, name);
163 if (r)
164 goto FAIL;
165 }
ec684c1a
MT
166
167 return 0;
168
169FAIL:
170 loc_country_unref(*country);
171 return r;
172}
173
b904896a
MT
174int loc_country_to_database_v1(struct loc_country* country,
175 struct loc_stringpool* pool, struct loc_database_country_v1* dbobj) {
32b16159
MT
176 off_t name = 0;
177
ec684c1a 178 // Add country code
32b16159
MT
179 if (*country->code)
180 loc_country_code_copy(dbobj->code, country->code);
ec684c1a
MT
181
182 // Add continent code
32b16159
MT
183 if (*country->continent_code)
184 loc_country_code_copy(dbobj->continent_code, country->continent_code);
ec684c1a
MT
185
186 // Save the name string in the string pool
32b16159
MT
187 if (country->name)
188 name = loc_stringpool_add(pool, country->name);
189
ec684c1a
MT
190 dbobj->name = htobe32(name);
191
192 return 0;
193}
0f0829ef
MT
194
195LOC_EXPORT int loc_country_code_is_valid(const char* cc) {
196 // It cannot be NULL
197 if (!cc || !*cc)
198 return 0;
199
200 // It must be 2 characters long
201 if (strlen(cc) != 2)
202 return 0;
203
204 // It must only contain A-Z
205 for (unsigned int i = 0; i < 2; i++) {
206 if (cc[i] < 'A' || cc[i] > 'Z')
207 return 0;
208 }
209
e8ebd079
MT
210 // The code cannot begin with an X (those are reserved for private use)
211 if (*cc == 'X')
212 return 0;
213
0f0829ef
MT
214 // Looks valid
215 return 1;
216}
3eb1eed6
MT
217
218LOC_EXPORT int loc_country_special_code_to_flag(const char* cc) {
219 // Check if we got some input
220 if (!cc || !*cc) {
221 errno = EINVAL;
222 return -1;
223 }
224
225 // Return flags for any known special country
226 for (const struct loc_special_country* country = loc_special_countries;
227 country->flags; country++) {
32b16159 228 if (strncmp(country->code, cc, 2) == 0)
3eb1eed6
MT
229 return country->flags;
230 }
231
232 return 0;
233}