]> git.ipfire.org Git - location/debian/libloc.git/blame - src/country.c
New upstream version 0.9.15
[location/debian/libloc.git] / src / country.c
CommitLineData
1f2c3ccb
JS
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
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
38struct loc_country {
39 struct loc_ctx* ctx;
40 int refcount;
41
aa9346d8
JS
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];
1f2c3ccb
JS
46
47 char* name;
48};
49
50LOC_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)
aa9346d8 59 return 1;
1f2c3ccb
JS
60
61 c->ctx = loc_ref(ctx);
62 c->refcount = 1;
63
aa9346d8
JS
64 // Set the country code
65 loc_country_code_copy(c->code, country_code);
1f2c3ccb
JS
66
67 DEBUG(c->ctx, "Country %s allocated at %p\n", c->code, c);
68 *country = c;
69
70 return 0;
71}
72
73LOC_EXPORT struct loc_country* loc_country_ref(struct loc_country* country) {
74 country->refcount++;
75
76 return country;
77}
78
79static void loc_country_free(struct loc_country* country) {
80 DEBUG(country->ctx, "Releasing country %s %p\n", country->code, country);
81
1f2c3ccb
JS
82 if (country->name)
83 free(country->name);
84
85 loc_unref(country->ctx);
86 free(country);
87}
88
89LOC_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);
1f2c3ccb
JS
94 return NULL;
95}
96
97LOC_EXPORT const char* loc_country_get_code(struct loc_country* country) {
98 return country->code;
99}
100
101LOC_EXPORT const char* loc_country_get_continent_code(struct loc_country* country) {
102 return country->continent_code;
103}
104
105LOC_EXPORT int loc_country_set_continent_code(struct loc_country* country, const char* continent_code) {
aa9346d8
JS
106 // Check for valid input
107 if (!continent_code || strlen(continent_code) != 2) {
108 errno = EINVAL;
109 return 1;
110 }
1f2c3ccb 111
aa9346d8
JS
112 // Store the code
113 loc_country_code_copy(country->continent_code, continent_code);
1f2c3ccb
JS
114
115 return 0;
116}
117
118LOC_EXPORT const char* loc_country_get_name(struct loc_country* country) {
119 return country->name;
120}
121
122LOC_EXPORT int loc_country_set_name(struct loc_country* country, const char* name) {
123 if (country->name)
124 free(country->name);
125
aa9346d8 126 if (name) {
1f2c3ccb
JS
127 country->name = strdup(name);
128
aa9346d8
JS
129 // Report error if we could not copy the string
130 if (!country->name)
131 return 1;
132 }
133
1f2c3ccb
JS
134 return 0;
135}
136
137LOC_EXPORT int loc_country_cmp(struct loc_country* country1, struct loc_country* country2) {
aa9346d8 138 return strncmp(country1->code, country2->code, 2);
1f2c3ccb
JS
139}
140
141int loc_country_new_from_database_v1(struct loc_ctx* ctx, struct loc_stringpool* pool,
142 struct loc_country** country, const struct loc_database_country_v1* dbobj) {
aa9346d8 143 char buffer[3] = "XX";
1f2c3ccb
JS
144
145 // Read country code
146 loc_country_code_copy(buffer, dbobj->code);
147
1f2c3ccb
JS
148 // Create a new country object
149 int r = loc_country_new(ctx, country, buffer);
150 if (r)
151 return r;
152
aa9346d8
JS
153 // Copy continent code
154 if (*dbobj->continent_code)
155 loc_country_code_copy((*country)->continent_code, dbobj->continent_code);
1f2c3ccb
JS
156
157 // Set name
158 const char* name = loc_stringpool_get(pool, be32toh(dbobj->name));
159 if (name) {
160 r = loc_country_set_name(*country, name);
161 if (r)
162 goto FAIL;
163 }
164
165 return 0;
166
167FAIL:
168 loc_country_unref(*country);
169 return r;
170}
171
172int loc_country_to_database_v1(struct loc_country* country,
173 struct loc_stringpool* pool, struct loc_database_country_v1* dbobj) {
aa9346d8
JS
174 off_t name = 0;
175
1f2c3ccb 176 // Add country code
aa9346d8
JS
177 if (*country->code)
178 loc_country_code_copy(dbobj->code, country->code);
1f2c3ccb
JS
179
180 // Add continent code
aa9346d8
JS
181 if (*country->continent_code)
182 loc_country_code_copy(dbobj->continent_code, country->continent_code);
1f2c3ccb
JS
183
184 // Save the name string in the string pool
aa9346d8
JS
185 if (country->name)
186 name = loc_stringpool_add(pool, country->name);
187
1f2c3ccb
JS
188 dbobj->name = htobe32(name);
189
190 return 0;
191}
192
193LOC_EXPORT int loc_country_code_is_valid(const char* cc) {
194 // It cannot be NULL
195 if (!cc || !*cc)
196 return 0;
197
198 // It must be 2 characters long
199 if (strlen(cc) != 2)
200 return 0;
201
202 // It must only contain A-Z
203 for (unsigned int i = 0; i < 2; i++) {
204 if (cc[i] < 'A' || cc[i] > 'Z')
205 return 0;
206 }
207
208 // The code cannot begin with an X (those are reserved for private use)
209 if (*cc == 'X')
210 return 0;
211
212 // Looks valid
213 return 1;
214}
215
216LOC_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++) {
aa9346d8 226 if (strncmp(country->code, cc, 2) == 0)
1f2c3ccb
JS
227 return country->flags;
228 }
229
230 return 0;
231}