LCOV - code coverage report
Current view: top level - util - iban.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 71.4 % 56 40
Test Date: 2026-01-04 22:17:00 Functions: 100.0 % 2 2

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2019-2021 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file iban.c
      18              :  * @brief Common utility function for dealing with IBAN numbers
      19              :  * @author Florian Dold
      20              :  */
      21              : #include "taler/platform.h"
      22              : #include "taler/taler_util.h"
      23              : 
      24              : 
      25              : /* Country table taken from GNU gettext */
      26              : 
      27              : /**
      28              :  * Entry in the country table.
      29              :  */
      30              : struct CountryTableEntry
      31              : {
      32              :   /**
      33              :    * 2-Character international country code.
      34              :    */
      35              :   const char *code;
      36              : 
      37              :   /**
      38              :    * Long English name of the country.
      39              :    */
      40              :   const char *english;
      41              : };
      42              : 
      43              : 
      44              : /* Keep the following table in sync with gettext.
      45              :    WARNING: the entries should stay sorted according to the code */
      46              : /**
      47              :  * List of country codes.
      48              :  */
      49              : static const struct CountryTableEntry country_table[] = {
      50              :   { "AE", "U.A.E." },
      51              :   { "AF", "Afghanistan" },
      52              :   { "AL", "Albania" },
      53              :   { "AM", "Armenia" },
      54              :   { "AN", "Netherlands Antilles" },
      55              :   { "AR", "Argentina" },
      56              :   { "AT", "Austria" },
      57              :   { "AU", "Australia" },
      58              :   { "AZ", "Azerbaijan" },
      59              :   { "BA", "Bosnia and Herzegovina" },
      60              :   { "BD", "Bangladesh" },
      61              :   { "BE", "Belgium" },
      62              :   { "BG", "Bulgaria" },
      63              :   { "BH", "Bahrain" },
      64              :   { "BN", "Brunei Darussalam" },
      65              :   { "BO", "Bolivia" },
      66              :   { "BR", "Brazil" },
      67              :   { "BT", "Bhutan" },
      68              :   { "BY", "Belarus" },
      69              :   { "BZ", "Belize" },
      70              :   { "CA", "Canada" },
      71              :   { "CG", "Congo" },
      72              :   { "CH", "Switzerland" },
      73              :   { "CI", "Cote d'Ivoire" },
      74              :   { "CL", "Chile" },
      75              :   { "CM", "Cameroon" },
      76              :   { "CN", "People's Republic of China" },
      77              :   { "CO", "Colombia" },
      78              :   { "CR", "Costa Rica" },
      79              :   { "CS", "Serbia and Montenegro" },
      80              :   { "CZ", "Czech Republic" },
      81              :   { "DE", "Germany" },
      82              :   { "DK", "Denmark" },
      83              :   { "DO", "Dominican Republic" },
      84              :   { "DZ", "Algeria" },
      85              :   { "EC", "Ecuador" },
      86              :   { "EE", "Estonia" },
      87              :   { "EG", "Egypt" },
      88              :   { "ER", "Eritrea" },
      89              :   { "ES", "Spain" },
      90              :   { "ET", "Ethiopia" },
      91              :   { "FI", "Finland" },
      92              :   { "FO", "Faroe Islands" },
      93              :   { "FR", "France" },
      94              :   { "GB", "United Kingdom" },
      95              :   { "GD", "Caribbean" },
      96              :   { "GE", "Georgia" },
      97              :   { "GL", "Greenland" },
      98              :   { "GR", "Greece" },
      99              :   { "GT", "Guatemala" },
     100              :   { "HK", "Hong Kong" },
     101              :   { "HK", "Hong Kong S.A.R." },
     102              :   { "HN", "Honduras" },
     103              :   { "HR", "Croatia" },
     104              :   { "HT", "Haiti" },
     105              :   { "HU", "Hungary" },
     106              :   { "ID", "Indonesia" },
     107              :   { "IE", "Ireland" },
     108              :   { "IL", "Israel" },
     109              :   { "IN", "India" },
     110              :   { "IQ", "Iraq" },
     111              :   { "IR", "Iran" },
     112              :   { "IS", "Iceland" },
     113              :   { "IT", "Italy" },
     114              :   { "JM", "Jamaica" },
     115              :   { "JO", "Jordan" },
     116              :   { "JP", "Japan" },
     117              :   { "KE", "Kenya" },
     118              :   { "KG", "Kyrgyzstan" },
     119              :   { "KH", "Cambodia" },
     120              :   { "KR", "South Korea" },
     121              :   { "KW", "Kuwait" },
     122              :   { "KZ", "Kazakhstan" },
     123              :   { "LA", "Laos" },
     124              :   { "LB", "Lebanon" },
     125              :   { "LI", "Liechtenstein" },
     126              :   { "LK", "Sri Lanka" },
     127              :   { "LT", "Lithuania" },
     128              :   { "LU", "Luxembourg" },
     129              :   { "LV", "Latvia" },
     130              :   { "LY", "Libya" },
     131              :   { "MA", "Morocco" },
     132              :   { "MC", "Principality of Monaco" },
     133              :   { "MD", "Moldava" },
     134              :   { "MD", "Moldova" },
     135              :   { "ME", "Montenegro" },
     136              :   { "MK", "Former Yugoslav Republic of Macedonia" },
     137              :   { "ML", "Mali" },
     138              :   { "MM", "Myanmar" },
     139              :   { "MN", "Mongolia" },
     140              :   { "MO", "Macau S.A.R." },
     141              :   { "MT", "Malta" },
     142              :   { "MV", "Maldives" },
     143              :   { "MX", "Mexico" },
     144              :   { "MY", "Malaysia" },
     145              :   { "NG", "Nigeria" },
     146              :   { "NI", "Nicaragua" },
     147              :   { "NL", "Netherlands" },
     148              :   { "NO", "Norway" },
     149              :   { "NP", "Nepal" },
     150              :   { "NZ", "New Zealand" },
     151              :   { "OM", "Oman" },
     152              :   { "PA", "Panama" },
     153              :   { "PE", "Peru" },
     154              :   { "PH", "Philippines" },
     155              :   { "PK", "Islamic Republic of Pakistan" },
     156              :   { "PL", "Poland" },
     157              :   { "PR", "Puerto Rico" },
     158              :   { "PT", "Portugal" },
     159              :   { "PY", "Paraguay" },
     160              :   { "QA", "Qatar" },
     161              :   { "RE", "Reunion" },
     162              :   { "RO", "Romania" },
     163              :   { "RS", "Serbia" },
     164              :   { "RU", "Russia" },
     165              :   { "RW", "Rwanda" },
     166              :   { "SA", "Saudi Arabia" },
     167              :   { "SE", "Sweden" },
     168              :   { "SG", "Singapore" },
     169              :   { "SI", "Slovenia" },
     170              :   { "SK", "Slovak" },
     171              :   { "SN", "Senegal" },
     172              :   { "SO", "Somalia" },
     173              :   { "SR", "Suriname" },
     174              :   { "SV", "El Salvador" },
     175              :   { "SY", "Syria" },
     176              :   { "TH", "Thailand" },
     177              :   { "TJ", "Tajikistan" },
     178              :   { "TM", "Turkmenistan" },
     179              :   { "TN", "Tunisia" },
     180              :   { "TR", "Turkey" },
     181              :   { "TT", "Trinidad and Tobago" },
     182              :   { "TW", "Taiwan" },
     183              :   { "TZ", "Tanzania" },
     184              :   { "UA", "Ukraine" },
     185              :   { "US", "United States" },
     186              :   { "UY", "Uruguay" },
     187              :   { "VA", "Vatican" },
     188              :   { "VE", "Venezuela" },
     189              :   { "VN", "Viet Nam" },
     190              :   { "YE", "Yemen" },
     191              :   { "ZA", "South Africa" },
     192              :   { "ZW", "Zimbabwe" }
     193              : };
     194              : 
     195              : 
     196              : /**
     197              :  * Country code comparator function, for binary search with bsearch().
     198              :  *
     199              :  * @param ptr1 pointer to a `struct table_entry`
     200              :  * @param ptr2 pointer to a `struct table_entry`
     201              :  * @return result of memcmp()'ing the 2-digit country codes of the entries
     202              :  */
     203              : static int
     204           12 : cmp_country_code (const void *ptr1,
     205              :                   const void *ptr2)
     206              : {
     207           12 :   const struct CountryTableEntry *cc1 = ptr1;
     208           12 :   const struct CountryTableEntry *cc2 = ptr2;
     209              : 
     210           24 :   return memcmp (cc1->code,
     211           12 :                  cc2->code,
     212              :                  2);
     213              : }
     214              : 
     215              : 
     216              : char *
     217            2 : TALER_iban_validate (const char *iban)
     218              : {
     219              :   char cc[2];
     220              :   char ibancpy[35];
     221              :   struct CountryTableEntry cc_entry;
     222              :   unsigned int len;
     223              :   char *nbuf;
     224              :   unsigned long long dividend;
     225              :   unsigned long long remainder;
     226              :   unsigned int j;
     227              : 
     228            2 :   if (NULL == iban)
     229            0 :     return GNUNET_strdup ("(null) is not a valid IBAN");
     230            2 :   len = strlen (iban);
     231            2 :   if (len < 4)
     232            0 :     return GNUNET_strdup ("IBAN number too short to be valid");
     233            2 :   if (len > 34)
     234            0 :     return GNUNET_strdup ("IBAN number too long to be valid");
     235            2 :   GNUNET_memcpy (cc, iban, 2);
     236            2 :   GNUNET_memcpy (ibancpy, iban + 4, len - 4);
     237            2 :   GNUNET_memcpy (ibancpy + len - 4, iban, 4);
     238            2 :   ibancpy[len] = '\0';
     239            2 :   cc_entry.code = cc;
     240            2 :   cc_entry.english = NULL;
     241            2 :   if (NULL ==
     242            2 :       bsearch (&cc_entry,
     243              :                country_table,
     244              :                sizeof (country_table) / sizeof (struct CountryTableEntry),
     245              :                sizeof (struct CountryTableEntry),
     246              :                &cmp_country_code))
     247              :   {
     248              :     char *msg;
     249              : 
     250            0 :     GNUNET_asprintf (&msg,
     251              :                      "Country code `%c%c' not supported\n",
     252            0 :                      cc[0],
     253            0 :                      cc[1]);
     254            0 :     return msg;
     255              :   }
     256            2 :   nbuf = GNUNET_malloc ((len * 2) + 1);
     257            2 :   j = 0;
     258           51 :   for (unsigned int i = 0; i < len; i++)
     259              :   {
     260           49 :     if (isalpha ((unsigned char) ibancpy[i]))
     261              :     {
     262            5 :       if (2 != snprintf (&nbuf[j],
     263              :                          3,
     264              :                          "%2u",
     265            5 :                          (ibancpy[i] - 'A' + 10)))
     266              :       {
     267            0 :         GNUNET_break (0);
     268            0 :         return GNUNET_strdup ("internal invariant violation");
     269              :       }
     270            5 :       j += 2;
     271            5 :       continue;
     272              :     }
     273           44 :     nbuf[j] = ibancpy[i];
     274           44 :     j++;
     275              :   }
     276           56 :   for (j = 0; '\0' != nbuf[j]; j++)
     277              :   {
     278           54 :     if (! isdigit ( (unsigned char) nbuf[j]))
     279              :     {
     280              :       char *msg;
     281              : 
     282            0 :       GNUNET_asprintf (&msg,
     283              :                        "digit expected at `%s'",
     284              :                        &nbuf[j]);
     285            0 :       GNUNET_free (nbuf);
     286            0 :       return msg;
     287              :     }
     288              :   }
     289              :   GNUNET_assert (sizeof(dividend) >= 8);
     290            2 :   remainder = 0;
     291            9 :   for (unsigned int i = 0; i<j; i += 9)
     292              :   {
     293              :     int nread;
     294              : 
     295            7 :     if (1 !=
     296            7 :         sscanf (&nbuf[i],
     297              :                 "%9llu %n",
     298              :                 &dividend,
     299              :                 &nread))
     300              :     {
     301              :       char *msg;
     302              : 
     303            0 :       GNUNET_asprintf (&msg,
     304              :                        "wrong input for checksum calculation at `%s'",
     305              :                        &nbuf[i]);
     306            0 :       GNUNET_free (nbuf);
     307            0 :       return msg;
     308              :     }
     309            7 :     if (0 != remainder)
     310            5 :       dividend += remainder * (pow (10, nread));
     311            7 :     remainder = dividend % 97;
     312              :   }
     313            2 :   GNUNET_free (nbuf);
     314            2 :   if (1 != remainder)
     315            0 :     return GNUNET_strdup ("IBAN checksum is wrong");
     316            2 :   return NULL;
     317              : }
        

Generated by: LCOV version 2.0-1