<commit_before>/*
 * Copyright 2013 MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <math.h>

/* strcasecmp on windows */
#include "mongoc-util-private.h"

#include "mongoc-config.h"
#include "mongoc-host-list.h"
#include "mongoc-host-list-private.h"
#include "mongoc-log.h"
#include "mongoc-handshake-private.h"
#include "mongoc-socket.h"
#include "mongoc-topology-private.h"
#include "mongoc-uri-private.h"
#include "mongoc-read-concern-private.h"
#include "mongoc-write-concern-private.h"
#include "mongoc-compression-private.h"
#include "utlist.h"

struct _mongoc_uri_t {
   char *str;
   bool is_srv;
   char srv[BSON_HOST_NAME_MAX + 1];
   mongoc_host_list_t *hosts;
   char *username;
   char *password;
   char *database;
   bson_t options;
   bson_t credentials;
   bson_t compressors;
   mongoc_read_prefs_t *read_prefs;
   mongoc_read_concern_t *read_concern;
   mongoc_write_concern_t *write_concern;
};

#define MONGOC_URI_ERROR(error, format, ...)         \
   bson_set_error (error,                            \
                   MONGOC_ERROR_COMMAND,             \
                   MONGOC_ERROR_COMMAND_INVALID_ARG, \
                   format,                           \
                   __VA_ARGS__);


static const char *escape_instructions = "Percent-encode username and password"
                                         " according to RFC 3986";

bool
_mongoc_uri_set_option_as_int32 (mongoc_uri_t *uri,
                                 const char *option,
                                 int32_t value);

static bool
ends_with (const char *str, const char *suffix);

static void
mongoc_uri_do_unescape (char **str)
{
   char *tmp;

   if ((tmp = *str)) {
      *str = mongoc_uri_unescape (tmp);
      bson_free (tmp);
   }
}


#define VALIDATE_SRV_ERR()                                                \
   do {                                                                   \
      bson_set_error (error,                                              \
                      MONGOC_ERROR_STREAM,                                \
                      MONGOC_ERROR_STREAM_NAME_RESOLUTION,                \
                      "Invalid host \"%s\" returned for service \"%s\": " \
                      "host must be subdomain of service name",           \
                      host,                                               \
                      service);                                           \
      return false;                                                       \
   } while (0)


static int
count_dots (const char *s)
{
   int n = 0;
   const char *dot = s;

   while ((dot = strchr (dot + 1, '.'))) {
      n++;
   }

   return n;
}


/* at least one character, and does not start or end with dot */
static bool
valid_hostname (const char *s)
{
   size_t len = strlen (s);

   return len > 1 && s[0] != '.' && s[len - 1] != '.';
}


static bool
validate_srv_result (mongoc_uri_t *uri, const char *host, bson_error_t *error)
{
   const char *service;
   const char *service_root;

   service = mongoc_uri_get_service (uri);
   BSON_ASSERT (service);

   if (!valid_hostname (host)) {
      VALIDATE_SRV_ERR ();
   }

   service_root = strchr (service, '.');
   BSON_ASSERT (service_root);

   /* host must be descendent of service root: if service is
    * "a.foo.co" host can be like "a.foo.co", "b.foo.co", "a.b.foo.co", etc.
    */
   if (strlen (host) < strlen (service_root)) {
      VALIDATE_SRV_ERR ();
   }

   if (!ends_with (host, service_root)) {
      VALIDATE_SRV_ERR ();
   }

   return true;
}

bool
mongoc_uri_append_host_and_port (mongoc_uri_t *uri,
                                 const char *host_and_port,
                                 bson_error_t *error)
{
   bool r;
   mongoc_host_list_t *link = bson_malloc0 (sizeof (mongoc_host_list_t));
   r = _mongoc_host_list_from_string_with_err (link, host_and_port, error);
   if (!r) {
      bson_free (link);
      return false;
   }

   if (uri->is_srv && !validate_srv_result (uri, link->host, error)) {
      bson_free (link);
      return false;
   }

   LL_APPEND (uri->hosts, link);
   return true;
}

bool
mongoc_uri_append_host (mongoc_uri_t *uri,
                        const char *host,
                        uint16_t port,
                        bson_error_t *error)
{
   bool r;
   mongoc_host_list_t *link = bson_malloc0 (sizeof (mongoc_host_list_t));
   r = _mongoc_host_list_from_hostport_with_err (link, host, port, error);
   if (!r) {
      bson_free (link);
      return false;
   }

   if (uri->is_srv && !validate_srv_result (uri, link->host, error)) {
      bson_free (link);
      return false;
   }

   LL_APPEND (uri->hosts, link);
   return true;
}

/*
 *--------------------------------------------------------------------------
 *
 * scan_to_unichar --
 *
 *       Scans 'str' until either a character matching 'match' is found,
 *       until one of the characters in 'terminators' is encountered, or
 *       until we reach the end of 'str'.
 *
 *       NOTE: 'terminators' may not include multibyte UTF-8 characters.
 *
 * Returns:
 *       If 'match' is found, returns a copy of the section of 'str' before
 *       that character.  Otherwise, returns NULL.
 *
 * Side Effects:
 *       If 'match' is found, sets 'end' to begin at the matching character<commit_msg>CDRIVER-1972 do not shadow link symbol<commit_after>/*
 * Copyright 2013 MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <math.h>

/* strcasecmp on windows */
#include "mongoc-util-private.h"

#include "mongoc-config.h"
#include "mongoc-host-list.h"
#include "mongoc-host-list-private.h"
#include "mongoc-log.h"
#include "mongoc-handshake-private.h"
#include "mongoc-socket.h"
#include "mongoc-topology-private.h"
#include "mongoc-uri-private.h"
#include "mongoc-read-concern-private.h"
#include "mongoc-write-concern-private.h"
#include "mongoc-compression-private.h"
#include "utlist.h"

struct _mongoc_uri_t {
   char *str;
   bool is_srv;
   char srv[BSON_HOST_NAME_MAX + 1];
   mongoc_host_list_t *hosts;
   char *username;
   char *password;
   char *database;
   bson_t options;
   bson_t credentials;
   bson_t compressors;
   mongoc_read_prefs_t *read_prefs;
   mongoc_read_concern_t *read_concern;
   mongoc_write_concern_t *write_concern;
};

#define MONGOC_URI_ERROR(error, format, ...)         \
   bson_set_error (error,                            \
                   MONGOC_ERROR_COMMAND,             \
                   MONGOC_ERROR_COMMAND_INVALID_ARG, \
                   format,                           \
                   __VA_ARGS__);


static const char *escape_instructions = "Percent-encode username and password"
                                         " according to RFC 3986";

bool
_mongoc_uri_set_option_as_int32 (mongoc_uri_t *uri,
                                 const char *option,
                                 int32_t value);

static bool
ends_with (const char *str, const char *suffix);

static void
mongoc_uri_do_unescape (char **str)
{
   char *tmp;

   if ((tmp = *str)) {
      *str = mongoc_uri_unescape (tmp);
      bson_free (tmp);
   }
}


#define VALIDATE_SRV_ERR()                                                \
   do {                                                                   \
      bson_set_error (error,                                              \
                      MONGOC_ERROR_STREAM,                                \
                      MONGOC_ERROR_STREAM_NAME_RESOLUTION,                \
                      "Invalid host \"%s\" returned for service \"%s\": " \
                      "host must be subdomain of service name",           \
                      host,                                               \
                      service);                                           \
      return false;                                                       \
   } while (0)


static int
count_dots (const char *s)
{
   int n = 0;
   const char *dot = s;

   while ((dot = strchr (dot + 1, '.'))) {
      n++;
   }

   return n;
}


/* at least one character, and does not start or end with dot */
static bool
valid_hostname (const char *s)
{
   size_t len = strlen (s);

   return len > 1 && s[0] != '.' && s[len - 1] != '.';
}


static bool
validate_srv_result (mongoc_uri_t *uri, const char *host, bson_error_t *error)
{
   const char *service;
   const char *service_root;

   service = mongoc_uri_get_service (uri);
   BSON_ASSERT (service);

   if (!valid_hostname (host)) {
      VALIDATE_SRV_ERR ();
   }

   service_root = strchr (service, '.');
   BSON_ASSERT (service_root);

   /* host must be descendent of service root: if service is
    * "a.foo.co" host can be like "a.foo.co", "b.foo.co", "a.b.foo.co", etc.
    */
   if (strlen (host) < strlen (service_root)) {
      VALIDATE_SRV_ERR ();
   }

   if (!ends_with (host, service_root)) {
      VALIDATE_SRV_ERR ();
   }

   return true;
}

bool
mongoc_uri_append_host_and_port (mongoc_uri_t *uri,
                                 const char *host_and_port,
                                 bson_error_t *error)
{
   bool r;
   mongoc_host_list_t *link_ = bson_malloc0 (sizeof (mongoc_host_list_t));
   r = _mongoc_host_list_from_string_with_err (link_, host_and_port, error);
   if (!r) {
      bson_free (link_);
      return false;
   }

   if (uri->is_srv && !validate_srv_result (uri, link_->host, error)) {
      bson_free (link_);
      return false;
   }

   LL_APPEND (uri->hosts, link_);
   return true;
}

bool
mongoc_uri_append_host (mongoc_uri_t *uri,
                        const char *host,
                        uint16_t port,
                        bson_error_t *error)
{
   bool r;
   mongoc_host_list_t *link_ = bson_malloc0 (sizeof (mongoc_host_list_t));
   r = _mongoc_host_list_from_hostport_with_err (link_, host, port, error);
   if (!r) {
      bson_free (link_);
      return false;
   }

   if (uri->is_srv && !validate_srv_result (uri, link_->host, error)) {
      bson_free (link_);
      return false;
   }

   LL_APPEND (uri->hosts, link_);
   return true;
}

/*
 *--------------------------------------------------------------------------
 *
 * scan_to_unichar --
 *
 *       Scans 'str' until either a character matching 'match' is found,
 *       until one of the characters in 'terminators' is encountered, or
 *       until we reach the end of 'str'.
 *
 *       NOTE: 'terminators' may not include multibyte UTF-8 characters.
 *
 * Returns:
 *       If 'match' is found, returns a copy of the section of 'str' before
 *       that character.  Otherwise, returns NULL.
 *
 * Side Effects:
 *       If 'match' is found, sets 'end' to begin at the matching character
{'fraction_non_alphanumeric': 0.11375639198591668, 'fraction_numerical': 0.0050297594098415626, 'mean_word_length': 1.8615975053969778, 'pattern_counts': {'":': 2, '<': 15, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5770262', 'n_tokens_mistral': 3781, 'n_tokens_neox': 3665, 'n_words': 1002}