Blame src/mbox-util.c

Packit d7e8d0
/* mbox-util.c - Mail address helper functions
Packit d7e8d0
 * Copyright (C) 1998-2010 Free Software Foundation, Inc.
Packit d7e8d0
 * Copyright (C) 1998-2015 Werner Koch
Packit d7e8d0
 *
Packit d7e8d0
 * This file is part of GnuPG.
Packit d7e8d0
 *
Packit d7e8d0
 * This file is free software; you can redistribute it and/or modify
Packit d7e8d0
 * it under the terms of the GNU Lesser General Public License as
Packit d7e8d0
 * published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
 * the License, or (at your option) any later version.
Packit d7e8d0
 *
Packit d7e8d0
 * This file is distributed in the hope that it will be useful,
Packit d7e8d0
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit d7e8d0
 * GNU Lesser General Public License for more details.
Packit d7e8d0
 *
Packit Service 30b792
 * You should have received a copy of the GNU Lesser General Public
Packit Service 30b792
 * License along with this program; if not, see <https://gnu.org/licenses/>.
Packit Service 30b792
 * SPDX-License-Identifier: LGPL-2.1-or-later
Packit d7e8d0
 */
Packit d7e8d0
Packit d7e8d0
/* NB: This code has been taken from GnuPG.  Please keep it in sync
Packit d7e8d0
 * with GnuPG.  */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
# include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include <stdio.h>
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#include <unistd.h>
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
Packit d7e8d0
#include "mbox-util.h"
Packit d7e8d0
Packit d7e8d0
/* Lowercase all ASCII characters in STRING.  */
Packit d7e8d0
static char *
Packit d7e8d0
ascii_strlwr (char *string)
Packit d7e8d0
{
Packit d7e8d0
  char *p;
Packit d7e8d0
Packit d7e8d0
  for (p = string; *p; p++ )
Packit d7e8d0
    if (!(*p & ~0x7f) && *p >= 'A' && *p <= 'Z')
Packit d7e8d0
      *p |= 0x20;
Packit d7e8d0
Packit d7e8d0
  return string;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static int
Packit d7e8d0
string_count_chr (const char *string, int c)
Packit d7e8d0
{
Packit d7e8d0
  int count;
Packit d7e8d0
Packit d7e8d0
  for (count=0; *string; string++ )
Packit d7e8d0
    if ( *string == c )
Packit d7e8d0
      count++;
Packit d7e8d0
  return count;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static int
Packit d7e8d0
mem_count_chr (const void *buffer, int c, size_t length)
Packit d7e8d0
{
Packit d7e8d0
  const char *s = buffer;
Packit d7e8d0
  int count;
Packit d7e8d0
Packit d7e8d0
  for (count=0; length; length--, s++)
Packit d7e8d0
    if (*s == c)
Packit d7e8d0
      count++;
Packit d7e8d0
  return count;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* This is a case-sensitive version of our memistr.  I wonder why no
Packit d7e8d0
   standard function memstr exists but I better do not use the name
Packit d7e8d0
   memstr to avoid future conflicts.  */
Packit d7e8d0
static const char *
Packit d7e8d0
my_memstr (const void *buffer, size_t buflen, const char *sub)
Packit d7e8d0
{
Packit d7e8d0
  const unsigned char *buf = buffer;
Packit d7e8d0
  const unsigned char *t = (const unsigned char *)buf;
Packit d7e8d0
  const unsigned char *s = (const unsigned char *)sub;
Packit d7e8d0
  size_t n = buflen;
Packit d7e8d0
Packit d7e8d0
  for ( ; n ; t++, n-- )
Packit d7e8d0
    {
Packit d7e8d0
      if (*t == *s)
Packit d7e8d0
        {
Packit d7e8d0
          for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
Packit d7e8d0
            ;
Packit d7e8d0
          if (!*s)
Packit d7e8d0
            return (const char*)buf;
Packit d7e8d0
          t = (const unsigned char *)buf;
Packit d7e8d0
          s = (const unsigned char *)sub ;
Packit d7e8d0
          n = buflen;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  return NULL;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static int
Packit d7e8d0
string_has_ctrl_or_space (const char *string)
Packit d7e8d0
{
Packit d7e8d0
  for (; *string; string++ )
Packit d7e8d0
    if (!(*string & 0x80) && *string <= 0x20)
Packit d7e8d0
      return 1;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Return true if STRING has two consecutive '.' after an '@'
Packit d7e8d0
   sign.  */
Packit d7e8d0
static int
Packit d7e8d0
has_dotdot_after_at (const char *string)
Packit d7e8d0
{
Packit d7e8d0
  string = strchr (string, '@');
Packit d7e8d0
  if (!string)
Packit d7e8d0
    return 0; /* No at-sign.  */
Packit d7e8d0
  string++;
Packit d7e8d0
  return !!strstr (string, "..");
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Check whether BUFFER has characters not valid in an RFC-822
Packit d7e8d0
   address.  LENGTH gives the length of BUFFER.
Packit d7e8d0
Packit d7e8d0
   To cope with OpenPGP we ignore non-ascii characters so that for
Packit d7e8d0
   example umlauts are legal in an email address.  An OpenPGP user ID
Packit d7e8d0
   must be utf-8 encoded but there is no strict requirement for
Packit d7e8d0
   RFC-822.  Thus to avoid IDNA encoding we put the address verbatim
Packit d7e8d0
   as utf-8 into the user ID under the assumption that mail programs
Packit d7e8d0
   handle IDNA at a lower level and take OpenPGP user IDs as utf-8.
Packit d7e8d0
   Note that we can't do an utf-8 encoding checking here because in
Packit d7e8d0
   keygen.c this function is called with the native encoding and
Packit d7e8d0
   native to utf-8 encoding is only done later.  */
Packit d7e8d0
static int
Packit d7e8d0
has_invalid_email_chars (const void *buffer, size_t length)
Packit d7e8d0
{
Packit d7e8d0
  const unsigned char *s = buffer;
Packit d7e8d0
  int at_seen=0;
Packit d7e8d0
  const char *valid_chars=
Packit d7e8d0
    "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Packit d7e8d0
Packit d7e8d0
  for ( ; length && *s; length--, s++ )
Packit d7e8d0
    {
Packit d7e8d0
      if ((*s & 0x80))
Packit d7e8d0
        continue; /* We only care about ASCII.  */
Packit d7e8d0
      if (*s == '@')
Packit d7e8d0
        at_seen=1;
Packit d7e8d0
      else if (!at_seen && !(strchr (valid_chars, *s)
Packit d7e8d0
                             || strchr ("!#$%&'*+/=?^`{|}~", *s)))
Packit d7e8d0
        return 1;
Packit d7e8d0
      else if (at_seen && !strchr (valid_chars, *s))
Packit d7e8d0
        return 1;
Packit d7e8d0
    }
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Same as is_valid_mailbox (see below) but operates on non-nul
Packit d7e8d0
   terminated buffer.  */
Packit d7e8d0
static int
Packit d7e8d0
is_valid_mailbox_mem (const void *name_arg, size_t namelen)
Packit d7e8d0
{
Packit d7e8d0
  const char *name = name_arg;
Packit d7e8d0
Packit d7e8d0
  return !( !name
Packit d7e8d0
            || !namelen
Packit d7e8d0
            || has_invalid_email_chars (name, namelen)
Packit d7e8d0
            || mem_count_chr (name, '@', namelen) != 1
Packit d7e8d0
            || *name == '@'
Packit d7e8d0
            || name[namelen-1] == '@'
Packit d7e8d0
            || name[namelen-1] == '.'
Packit d7e8d0
            || my_memstr (name, namelen, ".."));
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Check whether NAME represents a valid mailbox according to
Packit d7e8d0
   RFC822. Returns true if so. */
Packit d7e8d0
int
Packit d7e8d0
_gpgme_is_valid_mailbox (const char *name)
Packit d7e8d0
{
Packit d7e8d0
  return name? is_valid_mailbox_mem (name, strlen (name)) : 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Return the mailbox (local-part@domain) form a standard user id.
Packit d7e8d0
   All plain ASCII characters in the result are converted to
Packit d7e8d0
   lowercase.  Caller must free the result.  Returns NULL if no valid
Packit d7e8d0
   mailbox was found (or we are out of memory). */
Packit d7e8d0
char *
Packit d7e8d0
_gpgme_mailbox_from_userid (const char *userid)
Packit d7e8d0
{
Packit d7e8d0
  const char *s, *s_end;
Packit d7e8d0
  size_t len;
Packit d7e8d0
  char *result = NULL;
Packit d7e8d0
Packit d7e8d0
  s = strchr (userid, '<');
Packit d7e8d0
  if (s)
Packit d7e8d0
    {
Packit d7e8d0
      /* Seems to be a standard user id.  */
Packit d7e8d0
      s++;
Packit d7e8d0
      s_end = strchr (s, '>');
Packit d7e8d0
      if (s_end && s_end > s)
Packit d7e8d0
        {
Packit d7e8d0
          len = s_end - s;
Packit d7e8d0
          result = malloc (len + 1);
Packit d7e8d0
          if (!result)
Packit d7e8d0
            return NULL; /* Ooops - out of core.  */
Packit d7e8d0
          strncpy (result, s, len);
Packit d7e8d0
          result[len] = 0;
Packit d7e8d0
          /* Apply some basic checks on the address.  We do not use
Packit d7e8d0
             is_valid_mailbox because those checks are too strict.  */
Packit d7e8d0
          if (string_count_chr (result, '@') != 1  /* Need exactly one '@.  */
Packit d7e8d0
              || *result == '@'           /* local-part missing.  */
Packit d7e8d0
              || result[len-1] == '@'     /* domain missing.  */
Packit d7e8d0
              || result[len-1] == '.'     /* ends with a dot.  */
Packit d7e8d0
              || string_has_ctrl_or_space (result)
Packit d7e8d0
              || has_dotdot_after_at (result))
Packit d7e8d0
            {
Packit d7e8d0
              free (result);
Packit d7e8d0
              result = NULL;
Packit d7e8d0
              errno = EINVAL;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        errno = EINVAL;
Packit d7e8d0
    }
Packit d7e8d0
  else if (_gpgme_is_valid_mailbox (userid))
Packit d7e8d0
    {
Packit d7e8d0
      /* The entire user id is a mailbox.  Return that one.  Note that
Packit d7e8d0
         this fallback method has some restrictions on the valid
Packit d7e8d0
         syntax of the mailbox.  However, those who want weird
Packit d7e8d0
         addresses should know about it and use the regular <...>
Packit d7e8d0
         syntax.  */
Packit d7e8d0
      result = strdup (userid);
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    errno = EINVAL;
Packit d7e8d0
Packit d7e8d0
  return result? ascii_strlwr (result): NULL;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* /\* Check whether UID is a valid standard user id of the form */
Packit d7e8d0
/*      "Heinrich Heine <heinrichh@duesseldorf.de>" */
Packit d7e8d0
/*    and return true if this is the case. *\/ */
Packit d7e8d0
/* int */
Packit d7e8d0
/* is_valid_user_id (const char *uid) */
Packit d7e8d0
/* { */
Packit d7e8d0
/*   if (!uid || !*uid) */
Packit d7e8d0
/*     return 0; */
Packit d7e8d0
Packit d7e8d0
/*   return 1; */
Packit d7e8d0
/* } */
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/*
Packit d7e8d0
 * Exported public API
Packit d7e8d0
 */
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Return the mail address ("addr-spec" as per RFC-5322) from a string
Packit d7e8d0
 * which is assumed to be an user id ("address" in RFC-5322).  All
Packit d7e8d0
 * plain ASCII characters (those with bit 7 cleared) in the result
Packit d7e8d0
 * are converted to lowercase.  Caller must free the result using
Packit d7e8d0
 * gpgme_free.  Returns NULL if no valid address was found (in which
Packit d7e8d0
 * case ERRNO is set to EINVAL) or for other errors.  */
Packit d7e8d0
char *
Packit d7e8d0
gpgme_addrspec_from_uid (const char *uid)
Packit d7e8d0
{
Packit d7e8d0
  return _gpgme_mailbox_from_userid (uid);
Packit d7e8d0
}