]> git.ipfire.org Git - people/ms/libloc.git/blobdiff - src/country.c
database: Re-open the file handle in r+ mode
[people/ms/libloc.git] / src / country.c
index 6e9bfaaa98a8bdf63694a30f63eda33483793ddd..8152a89752d2ca9cb1eb61f59b762c31558fd494 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
-#include <loc/libloc.h>
-#include <loc/country.h>
-#include <loc/private.h>
+#include <libloc/libloc.h>
+#include <libloc/compat.h>
+#include <libloc/country.h>
+#include <libloc/network.h>
+#include <libloc/private.h>
+
+static const struct loc_special_country {
+       const char code[3];
+       enum loc_network_flags flags;
+} loc_special_countries[] = {
+       { "A1", LOC_NETWORK_FLAG_ANONYMOUS_PROXY },
+       { "A2", LOC_NETWORK_FLAG_SATELLITE_PROVIDER },
+       { "A3", LOC_NETWORK_FLAG_ANYCAST },
+       { "XD", LOC_NETWORK_FLAG_DROP },
+       { "", 0 },
+};
 
 struct loc_country {
        struct loc_ctx* ctx;
        int refcount;
 
-       char* code;
-       char* continent_code;
+       // Store the country code in a 3 byte buffer. Two bytes for the code, and NULL so
+       // that we can use strcmp() and return a pointer.
+       char code[3];
+       char continent_code[3];
 
        char* name;
 };
 
 LOC_EXPORT int loc_country_new(struct loc_ctx* ctx, struct loc_country** country, const char* country_code) {
+       // Check of the country code is valid
+       if (!loc_country_code_is_valid(country_code)) {
+               errno = EINVAL;
+               return 1;
+       }
+
        struct loc_country* c = calloc(1, sizeof(*c));
        if (!c)
-               return -ENOMEM;
+               return 1;
 
        c->ctx = loc_ref(ctx);
        c->refcount = 1;
 
-       c->code = strdup(country_code);
+       // Set the country code
+       loc_country_code_copy(c->code, country_code);
 
        DEBUG(c->ctx, "Country %s allocated at %p\n", c->code, c);
        *country = c;
@@ -57,12 +79,6 @@ LOC_EXPORT struct loc_country* loc_country_ref(struct loc_country* country) {
 static void loc_country_free(struct loc_country* country) {
        DEBUG(country->ctx, "Releasing country %s %p\n", country->code, country);
 
-       if (country->code)
-               free(country->code);
-
-       if (country->continent_code)
-               free(country->continent_code);
-
        if (country->name)
                free(country->name);
 
@@ -75,7 +91,6 @@ LOC_EXPORT struct loc_country* loc_country_unref(struct loc_country* country) {
                return NULL;
 
        loc_country_free(country);
-
        return NULL;
 }
 
@@ -84,17 +99,21 @@ LOC_EXPORT const char* loc_country_get_code(struct loc_country* country) {
 }
 
 LOC_EXPORT const char* loc_country_get_continent_code(struct loc_country* country) {
+       if (!*country->continent_code)
+               return NULL;
+
        return country->continent_code;
 }
 
 LOC_EXPORT int loc_country_set_continent_code(struct loc_country* country, const char* continent_code) {
-       // XXX validate input
-
-       // Free previous value
-       if (country->continent_code)
-               free(country->continent_code);
+       // Check for valid input
+       if (!continent_code || strlen(continent_code) != 2) {
+               errno = EINVAL;
+               return 1;
+       }
 
-       country->continent_code = strdup(continent_code);
+       // Store the code
+       loc_country_code_copy(country->continent_code, continent_code);
 
        return 0;
 }
@@ -107,34 +126,36 @@ LOC_EXPORT int loc_country_set_name(struct loc_country* country, const char* nam
        if (country->name)
                free(country->name);
 
-       if (name)
+       if (name) {
                country->name = strdup(name);
 
+               // Report error if we could not copy the string
+               if (!country->name)
+                       return 1;
+       }
+
        return 0;
 }
 
-int loc_country_cmp(struct loc_country* country1, struct loc_country* country2) {
-       return strcmp(country1->code, country2->code);
+LOC_EXPORT int loc_country_cmp(struct loc_country* country1, struct loc_country* country2) {
+       return strncmp(country1->code, country2->code, 2);
 }
 
-int loc_country_new_from_database_v0(struct loc_ctx* ctx, struct loc_stringpool* pool,
-               struct loc_country** country, const struct loc_database_country_v0* dbobj) {
-       char buffer[3];
+int loc_country_new_from_database_v1(struct loc_ctx* ctx, struct loc_stringpool* pool,
+               struct loc_country** country, const struct loc_database_country_v1* dbobj) {
+       char buffer[3] = "XX";
 
        // Read country code
-       loc_country_copy_code(buffer, dbobj->code);
+       loc_country_code_copy(buffer, dbobj->code);
 
        // Create a new country object
        int r = loc_country_new(ctx, country, buffer);
        if (r)
                return r;
 
-       // Continent Code
-       loc_country_copy_code(buffer, dbobj->continent_code);
-
-       r = loc_country_set_continent_code(*country, buffer);
-       if (r)
-               goto FAIL;
+       // Copy continent code
+       if (*dbobj->continent_code)
+               loc_country_code_copy((*country)->continent_code, dbobj->continent_code);
 
        // Set name
        const char* name = loc_stringpool_get(pool, be32toh(dbobj->name));
@@ -151,21 +172,63 @@ FAIL:
        return r;
 }
 
-int loc_country_to_database_v0(struct loc_country* country,
-               struct loc_stringpool* pool, struct loc_database_country_v0* dbobj) {
+int loc_country_to_database_v1(struct loc_country* country,
+               struct loc_stringpool* pool, struct loc_database_country_v1* dbobj) {
+       off_t name = 0;
+
        // Add country code
-       for (unsigned int i = 0; i < 2; i++) {
-               dbobj->code[i] = country->code[i] ? country->code[i] : '\0';
-       }
+       if (*country->code)
+               loc_country_code_copy(dbobj->code, country->code);
 
        // Add continent code
-       for (unsigned int i = 0; i < 2; i++) {
-               dbobj->continent_code[i] = country->continent_code[i] ? country->continent_code[i] : '\0';
-       }
+       if (*country->continent_code)
+               loc_country_code_copy(dbobj->continent_code, country->continent_code);
 
        // Save the name string in the string pool
-       off_t name = loc_stringpool_add(pool, country->name ? country->name : "");
+       if (country->name)
+               name = loc_stringpool_add(pool, country->name);
+
        dbobj->name = htobe32(name);
 
        return 0;
 }
+
+LOC_EXPORT int loc_country_code_is_valid(const char* cc) {
+       // It cannot be NULL
+       if (!cc || !*cc)
+               return 0;
+
+       // It must be 2 characters long
+       if (strlen(cc) != 2)
+               return 0;
+
+       // It must only contain A-Z
+       for (unsigned int i = 0; i < 2; i++) {
+               if (cc[i] < 'A' || cc[i] > 'Z')
+                       return 0;
+       }
+
+       // The code cannot begin with an X (those are reserved for private use)
+       if (*cc == 'X')
+               return 0;
+
+       // Looks valid
+       return 1;
+}
+
+LOC_EXPORT int loc_country_special_code_to_flag(const char* cc) {
+       // Check if we got some input
+       if (!cc || !*cc) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       // Return flags for any known special country
+       for (const struct loc_special_country* country = loc_special_countries;
+                       country->flags; country++) {
+               if (strncmp(country->code, cc, 2) == 0)
+                       return country->flags;
+       }
+
+       return 0;
+}