Blame src/mbox-util.c

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