]> git.ipfire.org Git - people/ms/libloc.git/blob - src/country.c
country: Add function that returns flags for special country
[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 char* code;
43 char* continent_code;
44
45 char* name;
46 };
47
48 LOC_EXPORT int loc_country_new(struct loc_ctx* ctx, struct loc_country** country, const char* country_code) {
49 // Check of the country code is valid
50 if (!loc_country_code_is_valid(country_code)) {
51 errno = EINVAL;
52 return 1;
53 }
54
55 struct loc_country* c = calloc(1, sizeof(*c));
56 if (!c)
57 return -ENOMEM;
58
59 c->ctx = loc_ref(ctx);
60 c->refcount = 1;
61
62 c->code = strdup(country_code);
63
64 DEBUG(c->ctx, "Country %s allocated at %p\n", c->code, c);
65 *country = c;
66
67 return 0;
68 }
69
70 LOC_EXPORT struct loc_country* loc_country_ref(struct loc_country* country) {
71 country->refcount++;
72
73 return country;
74 }
75
76 static void loc_country_free(struct loc_country* country) {
77 DEBUG(country->ctx, "Releasing country %s %p\n", country->code, country);
78
79 if (country->code)
80 free(country->code);
81
82 if (country->continent_code)
83 free(country->continent_code);
84
85 if (country->name)
86 free(country->name);
87
88 loc_unref(country->ctx);
89 free(country);
90 }
91
92 LOC_EXPORT struct loc_country* loc_country_unref(struct loc_country* country) {
93 if (--country->refcount > 0)
94 return NULL;
95
96 loc_country_free(country);
97
98 return NULL;
99 }
100
101 LOC_EXPORT const char* loc_country_get_code(struct loc_country* country) {
102 return country->code;
103 }
104
105 LOC_EXPORT const char* loc_country_get_continent_code(struct loc_country* country) {
106 return country->continent_code;
107 }
108
109 LOC_EXPORT int loc_country_set_continent_code(struct loc_country* country, const char* continent_code) {
110 // XXX validate input
111
112 // Free previous value
113 if (country->continent_code)
114 free(country->continent_code);
115
116 country->continent_code = strdup(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 return 0;
133 }
134
135 LOC_EXPORT int loc_country_cmp(struct loc_country* country1, struct loc_country* country2) {
136 return strcmp(country1->code, country2->code);
137 }
138
139 int loc_country_new_from_database_v1(struct loc_ctx* ctx, struct loc_stringpool* pool,
140 struct loc_country** country, const struct loc_database_country_v1* dbobj) {
141 char buffer[3];
142
143 // Read country code
144 loc_country_code_copy(buffer, dbobj->code);
145
146 // Terminate buffer
147 buffer[2] = '\0';
148
149 // Create a new country object
150 int r = loc_country_new(ctx, country, buffer);
151 if (r)
152 return r;
153
154 // Continent Code
155 loc_country_code_copy(buffer, dbobj->continent_code);
156
157 r = loc_country_set_continent_code(*country, buffer);
158 if (r)
159 goto FAIL;
160
161 // Set name
162 const char* name = loc_stringpool_get(pool, be32toh(dbobj->name));
163 if (name) {
164 r = loc_country_set_name(*country, name);
165 if (r)
166 goto FAIL;
167 }
168
169 return 0;
170
171 FAIL:
172 loc_country_unref(*country);
173 return r;
174 }
175
176 int loc_country_to_database_v1(struct loc_country* country,
177 struct loc_stringpool* pool, struct loc_database_country_v1* dbobj) {
178 // Add country code
179 for (unsigned int i = 0; i < 2; i++) {
180 dbobj->code[i] = country->code[i] ? country->code[i] : '\0';
181 }
182
183 // Add continent code
184 if (country->continent_code) {
185 for (unsigned int i = 0; i < 2; i++) {
186 dbobj->continent_code[i] = country->continent_code[i] ? country->continent_code[i] : '\0';
187 }
188 }
189
190 // Save the name string in the string pool
191 off_t name = loc_stringpool_add(pool, country->name ? country->name : "");
192 dbobj->name = htobe32(name);
193
194 return 0;
195 }
196
197 LOC_EXPORT int loc_country_code_is_valid(const char* cc) {
198 // It cannot be NULL
199 if (!cc || !*cc)
200 return 0;
201
202 // It must be 2 characters long
203 if (strlen(cc) != 2)
204 return 0;
205
206 // It must only contain A-Z
207 for (unsigned int i = 0; i < 2; i++) {
208 if (cc[i] < 'A' || cc[i] > 'Z')
209 return 0;
210 }
211
212 // Looks valid
213 return 1;
214 }
215
216 LOC_EXPORT int loc_country_special_code_to_flag(const char* cc) {
217 // Check if we got some input
218 if (!cc || !*cc) {
219 errno = EINVAL;
220 return -1;
221 }
222
223 // Return flags for any known special country
224 for (const struct loc_special_country* country = loc_special_countries;
225 country->flags; country++) {
226 if (strcmp(country->code, cc) == 0)
227 return country->flags;
228 }
229
230 return 0;
231 }