LCOV - code coverage report
Current view: top level - util - iban.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 39 55 70.9 %
Date: 2022-08-25 06:15:09 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          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 "platform.h"
      22             : #include "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          27 : cmp_country_code (const void *ptr1,
     205             :                   const void *ptr2)
     206             : {
     207          27 :   const struct CountryTableEntry *cc1 = ptr1;
     208          27 :   const struct CountryTableEntry *cc2 = ptr2;
     209             : 
     210          54 :   return memcmp (cc1->code,
     211          27 :                  cc2->code,
     212             :                  2);
     213             : }
     214             : 
     215             : 
     216             : char *
     217           5 : 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 i;
     227             :   unsigned int j;
     228             : 
     229           5 :   if (NULL == iban)
     230           0 :     return GNUNET_strdup ("(null) is not a valid IBAN");
     231           5 :   len = strlen (iban);
     232           5 :   if (len < 4)
     233           0 :     return GNUNET_strdup ("IBAN number too short to be valid");
     234           5 :   if (len > 34)
     235           0 :     return GNUNET_strdup ("IBAN number too long to be valid");
     236           5 :   memcpy (cc, iban, 2);
     237           5 :   memcpy (ibancpy, iban + 4, len - 4);
     238           5 :   memcpy (ibancpy + len - 4, iban, 4);
     239           5 :   ibancpy[len] = '\0';
     240           5 :   cc_entry.code = cc;
     241           5 :   cc_entry.english = NULL;
     242           5 :   if (NULL ==
     243           5 :       bsearch (&cc_entry,
     244             :                country_table,
     245             :                sizeof (country_table) / sizeof (struct CountryTableEntry),
     246             :                sizeof (struct CountryTableEntry),
     247             :                &cmp_country_code))
     248             :   {
     249             :     char *msg;
     250             : 
     251           0 :     GNUNET_asprintf (&msg,
     252             :                      "Country code `%c%c' not supported\n",
     253           0 :                      cc[0],
     254           0 :                      cc[1]);
     255           0 :     return msg;
     256             :   }
     257           5 :   nbuf = GNUNET_malloc ((len * 2) + 1);
     258         120 :   for (i = 0, j = 0; i < len; i++)
     259             :   {
     260         115 :     if (isalpha ((unsigned char) ibancpy[i]))
     261             :     {
     262          11 :       if (2 != snprintf (&nbuf[j],
     263             :                          3,
     264             :                          "%2u",
     265          11 :                          (ibancpy[i] - 'A' + 10)))
     266             :       {
     267           0 :         GNUNET_break (0);
     268           0 :         return GNUNET_strdup ("internal invariant violation");
     269             :       }
     270          11 :       j += 2;
     271          11 :       continue;
     272             :     }
     273         104 :     nbuf[j] = ibancpy[i];
     274         104 :     j++;
     275             :   }
     276         131 :   for (j = 0; '\0' != nbuf[j]; j++)
     277             :   {
     278         126 :     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           5 :   remainder = 0;
     291          15 :   for (unsigned int i = 0; i<j; i += 16)
     292             :   {
     293             :     int nread;
     294             : 
     295          10 :     if (1 !=
     296          10 :         sscanf (&nbuf[i],
     297             :                 "%16llu %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          10 :     if (0 != remainder)
     310           5 :       dividend += remainder * (pow (10, nread));
     311          10 :     remainder = dividend % 97;
     312             :   }
     313           5 :   GNUNET_free (nbuf);
     314           5 :   if (1 != remainder)
     315           0 :     return GNUNET_strdup ("IBAN checksum is wrong");
     316           5 :   return NULL;
     317             : }

Generated by: LCOV version 1.14