LCOV - code coverage report
Current view: top level - common - mbox-util.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 89 94 94.7 %
Date: 2016-11-29 15:00:56 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* mbox-util.c - Mail address helper functions
       2             :  * Copyright (C) 1998-2010 Free Software Foundation, Inc.
       3             :  * Copyright (C) 1998-2015 Werner Koch
       4             :  *
       5             :  * This file is part of GnuPG.
       6             :  *
       7             :  * This file is free software; you can redistribute it and/or modify
       8             :  * it under the terms of the GNU Lesser General Public License as
       9             :  * published by the Free Software Foundation; either version 2.1 of
      10             :  * the License, or (at your option) any later version.
      11             :  *
      12             :  * This file is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :  * GNU General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU Lesser General Public License
      18             :  * along with this program; if not, see <https://www.gnu.org/licenses/>.
      19             :  */
      20             : 
      21             : /* NB: GPGME uses the same code to reflect our idea on how to extract
      22             :  * a mail address from a user id.
      23             :  */
      24             : 
      25             : #include <config.h>
      26             : #include <stdio.h>
      27             : #include <stdlib.h>
      28             : #include <string.h>
      29             : #include <unistd.h>
      30             : #include <errno.h>
      31             : 
      32             : #include "util.h"
      33             : #include "mbox-util.h"
      34             : 
      35             : 
      36             : static int
      37         424 : string_count_chr (const char *string, int c)
      38             : {
      39             :   int count;
      40             : 
      41        5400 :   for (count=0; *string; string++ )
      42        4976 :     if ( *string == c )
      43         426 :       count++;
      44         424 :   return count;
      45             : }
      46             : 
      47             : static int
      48         558 : mem_count_chr (const void *buffer, int c, size_t length)
      49             : {
      50         558 :   const char *s = buffer;
      51             :   int count;
      52             : 
      53        7793 :   for (count=0; length; length--, s++)
      54        7235 :     if (*s == c)
      55         314 :       count++;
      56         558 :   return count;
      57             : }
      58             : 
      59             : 
      60             : /* This is a case-sensitive version of our memistr.  I wonder why no
      61             :    standard function memstr exists but I better do not use the name
      62             :    memstr to avoid future conflicts.  */
      63             : static const char *
      64         313 : my_memstr (const void *buffer, size_t buflen, const char *sub)
      65             : {
      66         313 :   const unsigned char *buf = buffer;
      67         313 :   const unsigned char *t = (const unsigned char *)buf;
      68         313 :   const unsigned char *s = (const unsigned char *)sub;
      69         313 :   size_t n = buflen;
      70             : 
      71        5191 :   for ( ; n ; t++, n-- )
      72             :     {
      73        4878 :       if (*t == *s)
      74             :         {
      75         313 :           for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
      76             :             ;
      77         313 :           if (!*s)
      78           0 :             return (const char*)buf;
      79         313 :           t = (const unsigned char *)buf;
      80         313 :           s = (const unsigned char *)sub ;
      81         313 :           n = buflen;
      82             :         }
      83             :     }
      84         313 :   return NULL;
      85             : }
      86             : 
      87             : 
      88             : 
      89             : static int
      90         419 : string_has_ctrl_or_space (const char *string)
      91             : {
      92        5329 :   for (; *string; string++ )
      93        4911 :     if (!(*string & 0x80) && *string <= 0x20)
      94           1 :       return 1;
      95         418 :   return 0;
      96             : }
      97             : 
      98             : 
      99             : /* Return true if STRING has two consecutive '.' after an '@'
     100             :    sign.  */
     101             : static int
     102         418 : has_dotdot_after_at (const char *string)
     103             : {
     104         418 :   string = strchr (string, '@');
     105         418 :   if (!string)
     106           0 :     return 0; /* No at-sign.  */
     107         418 :   string++;
     108         418 :   return !!strstr (string, "..");
     109             : }
     110             : 
     111             : 
     112             : /* Check whether BUFFER has characters not valid in an RFC-822
     113             :    address.  LENGTH gives the length of BUFFER.
     114             : 
     115             :    To cope with OpenPGP we ignore non-ascii characters so that for
     116             :    example umlauts are legal in an email address.  An OpenPGP user ID
     117             :    must be utf-8 encoded but there is no strict requirement for
     118             :    RFC-822.  Thus to avoid IDNA encoding we put the address verbatim
     119             :    as utf-8 into the user ID under the assumption that mail programs
     120             :    handle IDNA at a lower level and take OpenPGP user IDs as utf-8.
     121             :    Note that we can't do an utf-8 encoding checking here because in
     122             :    keygen.c this function is called with the native encoding and
     123             :    native to utf-8 encoding is only done later.  */
     124             : int
     125         764 : has_invalid_email_chars (const void *buffer, size_t length)
     126             : {
     127         764 :   const unsigned char *s = buffer;
     128         764 :   int at_seen=0;
     129         764 :   const char *valid_chars=
     130             :     "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
     131             : 
     132        9434 :   for ( ; length && *s; length--, s++ )
     133             :     {
     134        8876 :       if ((*s & 0x80))
     135           0 :         continue; /* We only care about ASCII.  */
     136        8876 :       if (*s == '@')
     137         315 :         at_seen=1;
     138        8766 :       else if (!at_seen && !(strchr (valid_chars, *s)
     139         205 :                              || strchr ("!#$%&'*+/=?^`{|}~", *s)))
     140         205 :         return 1;
     141        8356 :       else if (at_seen && !strchr (valid_chars, *s))
     142           1 :         return 1;
     143             :     }
     144         558 :   return 0;
     145             : }
     146             : 
     147             : 
     148             : /* Same as is_valid_mailbox (see below) but operates on non-nul
     149             :    terminated buffer.  */
     150             : int
     151         765 : is_valid_mailbox_mem (const void *name_arg, size_t namelen)
     152             : {
     153         765 :   const char *name = name_arg;
     154             : 
     155        1078 :   return !( !name
     156         765 :             || !namelen
     157         764 :             || has_invalid_email_chars (name, namelen)
     158         558 :             || mem_count_chr (name, '@', namelen) != 1
     159         314 :             || *name == '@'
     160         313 :             || name[namelen-1] == '@'
     161         313 :             || name[namelen-1] == '.'
     162         313 :             || my_memstr (name, namelen, ".."));
     163             : }
     164             : 
     165             : 
     166             : /* Check whether NAME represents a valid mailbox according to
     167             :    RFC822. Returns true if so. */
     168             : int
     169         765 : is_valid_mailbox (const char *name)
     170             : {
     171         765 :   return name? is_valid_mailbox_mem (name, strlen (name)) : 0;
     172             : }
     173             : 
     174             : 
     175             : /* Return the mailbox (local-part@domain) form a standard user id.
     176             :    All plain ASCII characters in the result are converted to
     177             :    lowercase.  Caller must free the result.  Returns NULL if no valid
     178             :    mailbox was found (or we are out of memory). */
     179             : char *
     180         636 : mailbox_from_userid (const char *userid)
     181             : {
     182             :   const char *s, *s_end;
     183             :   size_t len;
     184         636 :   char *result = NULL;
     185             : 
     186         636 :   s = strchr (userid, '<');
     187         636 :   if (s)
     188             :     {
     189             :       /* Seems to be a standard user id.  */
     190         427 :       s++;
     191         427 :       s_end = strchr (s, '>');
     192         427 :       if (s_end && s_end > s)
     193             :         {
     194         424 :           len = s_end - s;
     195         424 :           result = xtrymalloc (len + 1);
     196         424 :           if (!result)
     197           0 :             return NULL; /* Ooops - out of core.  */
     198         424 :           strncpy (result, s, len);
     199         424 :           result[len] = 0;
     200             :           /* Apply some basic checks on the address.  We do not use
     201             :              is_valid_mailbox because those checks are too strict.  */
     202         848 :           if (string_count_chr (result, '@') != 1  /* Need exactly one '@.  */
     203         422 :               || *result == '@'           /* local-part missing.  */
     204         421 :               || result[len-1] == '@'     /* domain missing.  */
     205         421 :               || result[len-1] == '.'     /* ends with a dot.  */
     206         419 :               || string_has_ctrl_or_space (result)
     207         418 :               || has_dotdot_after_at (result))
     208             :             {
     209           7 :               xfree (result);
     210           7 :               result = NULL;
     211           7 :               errno = EINVAL;
     212             :             }
     213             :         }
     214             :       else
     215           3 :         errno = EINVAL;
     216             :     }
     217         209 :   else if (is_valid_mailbox (userid))
     218             :     {
     219             :       /* The entire user id is a mailbox.  Return that one.  Note that
     220             :          this fallback method has some restrictions on the valid
     221             :          syntax of the mailbox.  However, those who want weird
     222             :          addresses should know about it and use the regular <...>
     223             :          syntax.  */
     224           1 :       result = xtrystrdup (userid);
     225             :     }
     226             :   else
     227         208 :     errno = EINVAL;
     228             : 
     229         636 :   return result? ascii_strlwr (result): NULL;
     230             : }
     231             : 
     232             : 
     233             : /* Check whether UID is a valid standard user id of the form
     234             :      "Heinrich Heine <heinrichh@duesseldorf.de>"
     235             :    and return true if this is the case. */
     236             : int
     237           1 : is_valid_user_id (const char *uid)
     238             : {
     239           1 :   if (!uid || !*uid)
     240           0 :     return 0;
     241             : 
     242           1 :   return 1;
     243             : }

Generated by: LCOV version 1.11