void Magick::Image::selectiveBlurChannel(const ChannelType channel_,
  const double radius_,const double sigma_,const double threshold_)
{
  MagickCore::Image
    *newImage;

  GetPPException;
  GetAndSetPPChannelMask(channel_);
  newImage=SelectiveBlurImage(constImage(),radius_,sigma_,threshold_,
    exceptionInfo);
  RestorePPChannelMask;
  replaceImage(newImage);
  ThrowImageException;
}


Magick::Image::Image(const Image &image_,const Geometry &geometry_)
  : _imgRef(new ImageRef)
{
  const RectangleInfo
    geometry=geometry_;

  OffsetInfo
    offset;

  MagickCore::Image
    *image;

  GetPPException;
  image=CloneImage(image_.constImage(),geometry_.width(),geometry_.height(),
    MagickTrue,exceptionInfo);
  replaceImage(image);
  _imgRef->options(new Options(*image_.constOptions()));
  offset.x=0;
  offset.y=0;
  (void) CopyImagePixels(image,image_.constImage(),&geometry,&offset,
    exceptionInfo);
  ThrowImageException;
}


void Magick::Image::randomThresholdChannel(const ChannelType channel_,
  const double low_,const double high_)
{
  modifyImage();
  GetPPException;
  GetAndSetPPChannelMask(channel_);
  (void) RandomThresholdImage(image(),low_,high_,exceptionInfo);
  RestorePPChannelMask;
  ThrowImageException;
}


void Magick::Image::distort(const DistortMethod method_,
  const size_t numberArguments_,const double *arguments_,const bool bestfit_)
{
  MagickCore::Image
    *newImage;

  GetPPException;
  newImage=DistortImage(constImage(), method_,numberArguments_,arguments_,
    bestfit_ == true ? MagickTrue : MagickFalse,exceptionInfo);
  replaceImage(newImage);
  ThrowImageException;
}


void Magick::Image::filterType(const Magick::FilterType filterType_)
{
  modifyImage();
  image()->filter=filterType_;
}


MagickPPExport int Magick::operator > (const Magick::Image &left_,
  const Magick::Image &right_)
{
  return(!(left_ < right_) && (left_ != right_));
}


size_t Magick::Image::animationDelay(void) const
{
  return(constImage()->delay);
}


Magick::ColorspaceType Magick::Image::colorSpaceType(void) const
{
  return(constOptions()->colorspaceType());
}


void Magick::Image::floodFillTexture(const Magick::Geometry &point_,
  const Magick::Image &texture_,const bool invert_)
{
  floodFillTexture(point_.xOff(),point_.yOff(),texture_,invert_);
}


static int dxtory_decode_v1_444(AVCodecContext *avctx, AVFrame *pic,
                                const uint8_t *src, int src_size)
{
    int h, w;
    uint8_t *Y, *U, *V;
    int ret;

    if (src_size < avctx->width * avctx->height * 3L) {
        av_log(avctx, AV_LOG_ERROR, "packet too small\n");
        return AVERROR_INVALIDDATA;
    }

    avctx->pix_fmt = AV_PIX_FMT_YUV444P;
    if ((ret = ff_get_buffer(avctx, pic, 0)) < 0)
        return ret;

    Y = pic->data[0];
    U = pic->data[1];
    V = pic->data[2];
    for (h = 0; h < avctx->height; h++) {
        for (w = 0; w < avctx->width; w++) {
            Y[w] = *src++;
            U[w] = *src++ ^ 0x80;
            V[w] = *src++ ^ 0x80;
        }
        Y += pic->linesize[0];
        U += pic->linesize[1];
        V += pic->linesize[2];
    }

    return 0;
}


static int dxtory_decode_v1_rgb(AVCodecContext *avctx, AVFrame *pic,
                                const uint8_t *src, int src_size,
                                int id, int bpp)
{
    int h;
    uint8_t *dst;
    int ret;

    if (src_size < avctx->width * avctx->height * (int64_t)bpp) {
        av_log(avctx, AV_LOG_ERROR, "packet too small\n");
        return AVERROR_INVALIDDATA;
    }

    avctx->pix_fmt = id;
    if ((ret = ff_get_buffer(avctx, pic, 0)) < 0)
        return ret;

    dst = pic->data[0];
    for (h = 0; h < avctx->height; h++) {
        memcpy(dst, src, avctx->width * bpp);
        src += avctx->width * bpp;
        dst += pic->linesize[0];
    }

    return 0;
}


static int dxtory_decode_v1_420(AVCodecContext *avctx, AVFrame *pic,
                                const uint8_t *src, int src_size)
{
    int h, w;
    uint8_t *Y1, *Y2, *U, *V;
    int ret;

    if (src_size < avctx->width * avctx->height * 3L / 2) {
        av_log(avctx, AV_LOG_ERROR, "packet too small\n");
        return AVERROR_INVALIDDATA;
    }

    avctx->pix_fmt = AV_PIX_FMT_YUV420P;
    if ((ret = ff_get_buffer(avctx, pic, 0)) < 0)
        return ret;

    Y1 = pic->data[0];
    Y2 = pic->data[0] + pic->linesize[0];
    U  = pic->data[1];
    V  = pic->data[2];
    for (h = 0; h < avctx->height; h += 2) {
        for (w = 0; w < avctx->width; w += 2) {
            AV_COPY16(Y1 + w, src);
            AV_COPY16(Y2 + w, src + 2);
            U[w >> 1] = src[4] + 0x80;
            V[w >> 1] = src[5] + 0x80;
            src += 6;
        }
        Y1 += pic->linesize[0] << 1;
        Y2 += pic->linesize[0] << 1;
        U  += pic->linesize[1];
        V  += pic->linesize[2];
    }

    return 0;
}


static inline uint8_t decode_sym(GetBitContext *gb, uint8_t lru[8])
{
    uint8_t c, val;

    c = get_unary(gb, 0, 8);
    if (!c) {
        val = get_bits(gb, 8);
        memmove(lru + 1, lru, sizeof(*lru) * (8 - 1));
    } else {
        val = lru[c - 1];
        memmove(lru + 1, lru, sizeof(*lru) * (c - 1));
    }
    lru[0] = val;

    return val;
}


UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
                                   bool has_scheme)
{
  char *portptr = NULL;
  char endbracket;
  int len;

  /*
   * Find the end of an IPv6 address, either on the ']' ending bracket or
   * a percent-encoded zone index.
   */
  if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n",
                 &endbracket, &len)) {
    if(']' == endbracket)
      portptr = &hostname[len];
    else if('%' == endbracket) {
      int zonelen = len;
      if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) {
        if(']' != endbracket)
          return CURLUE_BAD_IPV6;
        portptr = &hostname[--zonelen + len + 1];
      }
      else
        return CURLUE_BAD_IPV6;
    }
    else
      return CURLUE_BAD_IPV6;

    /* this is a RFC2732-style specified IP-address */
    if(portptr && *portptr) {
      if(*portptr != ':')
        return CURLUE_BAD_IPV6;
    }
    else
      portptr = NULL;
  }
  else
    portptr = strchr(hostname, ':');

  if(portptr) {
    char *rest;
    long port;
    char portbuf[7];

    /* Browser behavior adaptation. If there's a colon with no digits after,
       just cut off the name there which makes us ignore the colon and just
       use the default port. Firefox, Chrome and Safari all do that.

       Don't do it if the URL has no scheme, to make something that looks like
       a scheme not work!
    */
    if(!portptr[1]) {
      *portptr = '\0';
      return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER;
    }

    if(!ISDIGIT(portptr[1]))
      return CURLUE_BAD_PORT_NUMBER;

    port = strtol(portptr + 1, &rest, 10);  /* Port number must be decimal */

    if(port > 0xffff)
      return CURLUE_BAD_PORT_NUMBER;

    if(rest[0])
      return CURLUE_BAD_PORT_NUMBER;

    *portptr++ = '\0'; /* cut off the name there */
    *rest = 0;
    /* generate a new port number string to get rid of leading zeroes etc */
    msnprintf(portbuf, sizeof(portbuf), "%ld", port);
    u->portnum = port;
    u->port = strdup(portbuf);
    if(!u->port)
      return CURLUE_OUT_OF_MEMORY;
  }

  return CURLUE_OK;
}


CURLU *curl_url(void)
{
  return calloc(sizeof(struct Curl_URL), 1);
}


static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
{
  CURLUcode result = seturl(url, u, flags);
  if(result) {
    free_urlhandle(u);
    memset(u, 0, sizeof(struct Curl_URL));
  }
  return result;
}


void curl_url_cleanup(CURLU *u)
{
  if(u) {
    free_urlhandle(u);
    free(u);
  }
}


static int samldb_fill_foreignSecurityPrincipal_object(struct samldb_ctx *ac)
{
	struct ldb_context *ldb = NULL;
	const struct ldb_val *rdn_value = NULL;
	struct ldb_message_element *sid_el = NULL;
	struct dom_sid *sid = NULL;
	struct ldb_control *as_system = NULL;
	struct ldb_control *provision = NULL;
	bool allowed = false;
	int ret;

	ldb = ldb_module_get_ctx(ac->module);

	as_system = ldb_request_get_control(ac->req, LDB_CONTROL_AS_SYSTEM_OID);
	if (as_system != NULL) {
		allowed = true;
	}

	provision = ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID);
	if (provision != NULL) {
		allowed = true;
	}

	sid_el = ldb_msg_find_element(ac->msg, "objectSid");

	if (!allowed && sid_el == NULL) {
		return dsdb_module_werror(ac->module,
				LDB_ERR_OBJECT_CLASS_VIOLATION,
				WERR_DS_MISSING_REQUIRED_ATT,
				"objectSid missing on foreignSecurityPrincipal");
	}

	if (!allowed) {
		return dsdb_module_werror(ac->module,
				LDB_ERR_UNWILLING_TO_PERFORM,
				WERR_DS_ILLEGAL_MOD_OPERATION,
				"foreignSecurityPrincipal object not allowed");
	}

	if (sid_el != NULL) {
		sid = samdb_result_dom_sid(ac->msg, ac->msg, "objectSid");
		if (sid == NULL) {
			ldb_set_errstring(ldb,
					  "samldb: invalid objectSid!");
			return LDB_ERR_CONSTRAINT_VIOLATION;
		}
	}

	if (sid == NULL) {
		rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
		if (rdn_value == NULL) {
			return ldb_operr(ldb);
		}
		sid = dom_sid_parse_talloc(ac->msg,
					   (const char *)rdn_value->data);
		if (sid == NULL) {
			ldb_set_errstring(ldb,
					  "samldb: No valid SID found in ForeignSecurityPrincipal CN!");
			return LDB_ERR_CONSTRAINT_VIOLATION;
		}
		if (! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
			return ldb_operr(ldb);
		}
	}

	/* finally proceed with adding the entry */
	ret = samldb_add_step(ac, samldb_add_entry);
	if (ret != LDB_SUCCESS) return ret;

	return samldb_first_step(ac);
}


static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac,
					 struct dom_sid *sid)
{
	struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
	int ret = 0;
	struct security_token *user_token = NULL;
	struct security_descriptor *domain_sd = NULL;
	struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
	const char *operation = "";
	const struct dsdb_class *objectclass = NULL;

	if (dsdb_module_am_system(ac->module)) {
		return LDB_SUCCESS;
	}

	switch (ac->req->operation) {
	case LDB_ADD:
		operation = "add";
		break;
	case LDB_MODIFY:
		operation = "modify";
		break;
	default:
		return ldb_module_operr(ac->module);
	}

	user_token = acl_user_token(ac->module);
	if (user_token == NULL) {
		return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
	}

	ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
	if (ret != LDB_SUCCESS) {
		return ret;
	}
	ret = acl_check_extended_right(ac,
				       ac->module,
				       ac->req,
				       objectclass,
				       domain_sd,
				       user_token,
				       GUID_DRS_UNEXPIRE_PASSWORD,
				       SEC_ADS_CONTROL_ACCESS,
				       sid);
	if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
		return ret;
	}

	ldb_debug_set(ldb, LDB_DEBUG_WARNING,
		      "Failed to %s %s: "
		      "Setting pwdLastSet to -1 requires the "
		      "Unexpire-Password right that was not given "
		      "on the Domain object",
		      operation,
		      ldb_dn_get_linearized(ac->msg->dn));
	dsdb_acl_debug(domain_sd, user_token,
		       domain_dn, true, 10);

	return ret;
}


static int samldb_schema_governsid_valid_check(struct samldb_ctx *ac)
{
	int ret = samldb_unique_attr_check(ac, "governsID", "attributeID",
					   ldb_get_schema_basedn(
						   ldb_module_get_ctx(ac->module)));
	if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
		ret = LDB_ERR_UNWILLING_TO_PERFORM;
	}
	return ret;
}


NTSTATUS dns_timestamp_to_nt_time(NTTIME *_nt, uint32_t t)
{
	NTTIME nt = t;
	if (nt > NTTIME_MAX / NTTIME_TO_HOURS) {
		*_nt = NTTIME_MAX;
		return NT_STATUS_INTEGER_OVERFLOW;
	}
	*_nt = nt * NTTIME_TO_HOURS;
	return NT_STATUS_OK;
}


static int samldb_verify_subnet(struct samldb_ctx *ac, struct ldb_dn *dn)
{
	struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
	const char *cidr = NULL;
	const struct ldb_val *rdn_value = NULL;

	rdn_value = ldb_dn_get_rdn_val(dn);
	if (rdn_value == NULL) {
		ldb_set_errstring(ldb, "samldb: ldb_dn_get_rdn_val "
				  "failed");
		return LDB_ERR_UNWILLING_TO_PERFORM;
	}

	cidr = ldb_dn_escape_value(ac, *rdn_value);
	DBG_INFO("looking at cidr '%s'\n", cidr);
	if (cidr == NULL) {
		ldb_set_errstring(ldb,
				  "samldb: adding an empty subnet cidr seems wrong");
		return LDB_ERR_UNWILLING_TO_PERFORM;
	}

	if (verify_cidr(cidr)){
		ldb_set_errstring(ldb,
				  "samldb: subnet value is invalid");
		return LDB_ERR_INVALID_DN_SYNTAX;
	}

	return LDB_SUCCESS;
}


NTTIME samdb_result_nttime(const struct ldb_message *msg, const char *attr,
			   NTTIME default_value)
{
	return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
}


static NTSTATUS dcesrv_lsa_CREDRWRITEDOMAINCREDENTIALS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
		       struct lsa_CREDRWRITEDOMAINCREDENTIALS *r)
{
	DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
}


static int replmd_update_rpmd(struct ldb_module *module,
			      const struct dsdb_schema *schema,
			      struct ldb_request *req,
			      const char * const *rename_attrs,
			      struct ldb_message *msg, uint64_t *seq_num,
			      time_t t, bool is_schema_nc,
			      bool *is_urgent, bool *rodc)
{
	const struct ldb_val *omd_value;
	enum ndr_err_code ndr_err;
	struct replPropertyMetaDataBlob omd;
	unsigned int i;
	NTTIME now;
	const struct GUID *our_invocation_id;
	int ret;
	const char * const *attrs = NULL;
	const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
	struct ldb_result *res;
	struct ldb_context *ldb;
	struct ldb_message_element *objectclass_el;
	enum urgent_situation situation;
	bool rmd_is_provided;
	bool rmd_is_just_resorted = false;
	const char *not_rename_attrs[4 + msg->num_elements];
	bool is_forced_rodc = false;

	if (rename_attrs) {
		attrs = rename_attrs;
	} else {
		for (i = 0; i < msg->num_elements; i++) {
			not_rename_attrs[i] = msg->elements[i].name;
		}
		not_rename_attrs[i] = "replPropertyMetaData";
		not_rename_attrs[i+1] = "objectClass";
		not_rename_attrs[i+2] = "instanceType";
		not_rename_attrs[i+3] = NULL;
		attrs = not_rename_attrs;
	}

	ldb = ldb_module_get_ctx(module);

	ret = samdb_rodc(ldb, rodc);
	if (ret != LDB_SUCCESS) {
		DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
		*rodc = false;
	}

	if (*rodc &&
	    ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
		is_forced_rodc = true;
	}

	our_invocation_id = samdb_ntds_invocation_id(ldb);
	if (!our_invocation_id) {
		/* this happens during an initial vampire while
		   updating the schema */
		DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
		return LDB_SUCCESS;
	}

	unix_to_nt_time(&now, t);

	if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
		rmd_is_provided = true;
		if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
			rmd_is_just_resorted = true;
		}
	} else {
		rmd_is_provided = false;
	}

	/* if isDeleted is present and is TRUE, then we consider we are deleting,
	 * otherwise we consider we are updating */
	if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
		situation = REPL_URGENT_ON_DELETE;
	} else if (rename_attrs) {
		situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
	} else {
		situation = REPL_URGENT_ON_UPDATE;
	}

	if (rmd_is_provided) {
		/* In this case the change_replmetadata control was supplied */
		/* We check that it's the only attribute that is provided
		 * (it's a rare case so it's better to keep the code simplier)
		 * We also check that the highest local_usn is bigger or the same as
		 * uSNChanged. */
		uint64_t db_seq;
		if( msg->num_elements != 1 ||
			strncmp(msg->elements[0].name,
				"replPropertyMetaData", 20) ) {
			DEBUG(0,(__location__ ": changereplmetada control called without "\
				"a specified replPropertyMetaData attribute or with others\n"));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		if (situation != REPL_URGENT_ON_UPDATE) {
			DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
		if (!omd_value) {
			DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
					       (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
					    DSDB_FLAG_NEXT_MODULE |
					    DSDB_SEARCH_SHOW_RECYCLED |
					    DSDB_SEARCH_SHOW_EXTENDED_DN |
					    DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
					    DSDB_SEARCH_REVEAL_INTERNALS, req);

		if (ret != LDB_SUCCESS) {
			return ret;
		}

		if (rmd_is_just_resorted == false) {
			*seq_num = find_max_local_usn(omd);

			db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);

			/*
			 * The test here now allows for a new
			 * replPropertyMetaData with no change, if was
			 * just dbcheck re-sorting the values.
			 */
			if (*seq_num <= db_seq) {
				DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
					 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
					 (long long)*seq_num, (long long)db_seq));
				return LDB_ERR_OPERATIONS_ERROR;
			}
		}

	} else {
		/* search for the existing replPropertyMetaDataBlob. We need
		 * to use REVEAL and ask for DNs in storage format to support
		 * the check for values being the same in
		 * replmd_update_rpmd_element()
		 */
		ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
					    DSDB_FLAG_NEXT_MODULE |
					    DSDB_SEARCH_SHOW_RECYCLED |
					    DSDB_SEARCH_SHOW_EXTENDED_DN |
					    DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
					    DSDB_SEARCH_REVEAL_INTERNALS, req);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
		if (!omd_value) {
			DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
					       (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		if (omd.version != 1) {
			DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
				 omd.version, ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		for (i=0; i<msg->num_elements;) {
			struct ldb_message_element *el = &msg->elements[i];
			struct ldb_message_element *old_el;

			old_el = ldb_msg_find_element(res->msgs[0], el->name);
			ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
							 &omd, schema, seq_num,
							 our_invocation_id,
							 now, is_schema_nc,
							 is_forced_rodc,
							 req);
			if (ret != LDB_SUCCESS) {
				return ret;
			}

			if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
				*is_urgent = replmd_check_urgent_attribute(el);
			}

			if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
				i++;
				continue;
			}

			el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;

			if (el->num_values != 0) {
				i++;
				continue;
			}

			ldb_msg_remove_element(msg, el);
		}
	}

	/*
	 * Assert that we have an objectClass attribute - this is major
	 * corruption if we don't have this!
	 */
	objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
	if (objectclass_el != NULL) {
		/*
		 * Now check if this objectClass means we need to do urgent replication
		 */
		if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
								   situation)) {
			*is_urgent = true;
		}
	} else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
		ldb_asprintf_errstring(ldb, __location__
				       ": objectClass missing on %s\n",
				       ldb_dn_get_linearized(msg->dn));
		return LDB_ERR_OBJECT_CLASS_VIOLATION;
	}

	/*
	 * replmd_update_rpmd_element has done an update if the
	 * seq_num is set
	 */
	if (*seq_num != 0 || rmd_is_just_resorted == true) {
		struct ldb_val *md_value;
		struct ldb_message_element *el;

		/*if we are RODC and this is a DRSR update then its ok*/
		if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
		    && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
		    && !is_forced_rodc) {
			unsigned instanceType;

			if (*rodc) {
				ldb_set_errstring(ldb, "RODC modify is forbidden!");
				return LDB_ERR_REFERRAL;
			}

			instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
			if (!(instanceType & INSTANCE_TYPE_WRITE)) {
				return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
						 "cannot change replicated attribute on partial replica");
			}
		}

		md_value = talloc(msg, struct ldb_val);
		if (md_value == NULL) {
			ldb_oom(ldb);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
		if (ret != LDB_SUCCESS) {
			ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
			return ret;
		}

		ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
					       (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
		if (ret != LDB_SUCCESS) {
			DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return ret;
		}

		el->num_values = 1;
		el->values = md_value;
	}

	return LDB_SUCCESS;
}


static int64_t get_lockout_threshold(struct ldb_message *domain_msg,
				     struct ldb_message *pso_msg)
{
	if (pso_msg != NULL) {
		return ldb_msg_find_attr_as_int(pso_msg,
						"msDS-LockoutThreshold", 0);
	} else {
		return ldb_msg_find_attr_as_int(domain_msg,
						"lockoutThreshold", 0);
	}
}


static NTSTATUS pdb_samba_dsdb_getgrgid(struct pdb_methods *m, GROUP_MAP *map,
				 gid_t gid)
{
	struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
		m->private_data, struct pdb_samba_dsdb_state);
	NTSTATUS status;
	struct id_map id_map;
	struct id_map *id_maps[2];
	TALLOC_CTX *tmp_ctx = talloc_stackframe();
	NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);

	id_map.xid.id = gid;
	id_map.xid.type = ID_TYPE_GID;
	id_maps[0] = &id_map;
	id_maps[1] = NULL;

	status = idmap_xids_to_sids(state->idmap_ctx, tmp_ctx, id_maps);
	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(tmp_ctx);
		return status;
	}
	status = pdb_samba_dsdb_getgrsid(m, map, *id_map.sid);
	talloc_free(tmp_ctx);
	return status;
}


static int replmd_start_transaction(struct ldb_module *module)
{
	/* create our private structure for this transaction */
	struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
								struct replmd_private);
	replmd_txn_cleanup(replmd_private);

	/* free any leftover mod_usn records from cancelled
	   transactions */
	while (replmd_private->ncs) {
		struct nc_entry *e = replmd_private->ncs;
		DLIST_REMOVE(replmd_private->ncs, e);
		talloc_free(e);
	}

	replmd_private->originating_updates = false;

	return ldb_next_start_trans(module);
}


static NTSTATUS pdb_samba_dsdb_set_unix_primary_group(struct pdb_methods *m,
					       TALLOC_CTX *mem_ctx,
					       struct samu *user)
{
	return NT_STATUS_NOT_IMPLEMENTED;
}


static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid)
{
	struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
	struct dom_sid *sid;
	struct ldb_result *res;
	int ret;
	const char * const noattrs[] = { NULL };

	sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
	if (sid == NULL) {
		return ldb_operr(ldb);
	}

	ret = dsdb_module_search(ac->module, ac, &res,
				 ldb_get_default_basedn(ldb),
				 LDB_SCOPE_SUBTREE,
				 noattrs, DSDB_FLAG_NEXT_MODULE,
				 ac->req,
				 "(objectSid=%s)",
				 ldap_encode_ndr_dom_sid(ac, sid));
	if (ret != LDB_SUCCESS) {
		return ret;
	}
	if (res->count != 1) {
		talloc_free(res);
		ldb_asprintf_errstring(ldb,
				       "Failed to find primary group with RID %u!",
				       rid);
		return LDB_ERR_UNWILLING_TO_PERFORM;
	}
	talloc_free(res);

	return LDB_SUCCESS;
}


static NTSTATUS pdb_samba_dsdb_add_group_mapping_entry(struct pdb_methods *m,
						GROUP_MAP *map)
{
	return NT_STATUS_NOT_IMPLEMENTED;
}


static NTSTATUS pdb_samba_dsdb_create_alias(struct pdb_methods *m,
				     const char *name, uint32_t *rid)
{
	TALLOC_CTX *frame = talloc_stackframe();
	struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
		m->private_data, struct pdb_samba_dsdb_state);
	struct dom_sid *sid;

	struct ldb_dn *dn;
	NTSTATUS status;

	/* Internally this uses transactions to ensure all the steps
	 * happen or fail as one */
	status = dsdb_add_domain_alias(state->ldb, frame, name, &sid, &dn);
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(frame);
	}

	sid_peek_rid(sid, rid);
	TALLOC_FREE(frame);
	return NT_STATUS_OK;
}


static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
{
	struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
	struct replmd_private *replmd_private =
		talloc_get_type_abort(ldb_module_get_private(ar->module),
		struct replmd_private);
	int ret;
	static const char *attrs[] = {
		"replUpToDateVector",
		"repsFrom",
		"instanceType",
		NULL
	};
	struct ldb_request *search_req;

	ar->search_msg = NULL;

	/*
	 * Let the caller know that we did an originating updates
	 */
	ar->objs->originating_updates = replmd_private->originating_updates;

	ret = ldb_build_search_req(&search_req,
				   ldb,
				   ar,
				   ar->objs->partition_dn,
				   LDB_SCOPE_BASE,
				   "(objectClass=*)",
				   attrs,
				   NULL,
				   ar,
				   replmd_replicated_uptodate_search_callback,
				   ar->req);
	LDB_REQ_SET_LOCATION(search_req);
	if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);

	return ldb_next_request(ar->module, search_req);
}


static NTSTATUS dcesrv_lsa_SetInfoPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
				  struct lsa_SetInfoPolicy *r)
{
	/* need to support this */
	DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
}


static NTSTATUS dcesrv_lsa_EnumPrivsAccount(struct dcesrv_call_state *dce_call,
				     TALLOC_CTX *mem_ctx,
				     struct lsa_EnumPrivsAccount *r)
{
	struct dcesrv_handle *h;
	struct lsa_account_state *astate;
	int ret;
	unsigned int i, j;
	struct ldb_message **res;
	const char * const attrs[] = { "privilege", NULL};
	struct ldb_message_element *el;
	const char *sidstr;
	struct lsa_PrivilegeSet *privs;

	DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);

	astate = h->data;

	privs = talloc(mem_ctx, struct lsa_PrivilegeSet);
	if (privs == NULL) {
		return NT_STATUS_NO_MEMORY;
	}
	privs->count = 0;
	privs->unknown = 0;
	privs->set = NULL;

	*r->out.privs = privs;

	sidstr = ldap_encode_ndr_dom_sid(mem_ctx, astate->account_sid);
	if (sidstr == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	ret = gendb_search(astate->policy->pdb, mem_ctx, NULL, &res, attrs,
			   "objectSid=%s", sidstr);
	if (ret < 0) {
		return NT_STATUS_INTERNAL_DB_CORRUPTION;
	}
	if (ret != 1) {
		return NT_STATUS_OK;
	}

	el = ldb_msg_find_element(res[0], "privilege");
	if (el == NULL || el->num_values == 0) {
		return NT_STATUS_OK;
	}

	privs->set = talloc_array(privs,
				  struct lsa_LUIDAttribute, el->num_values);
	if (privs->set == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	j = 0;
	for (i=0;i<el->num_values;i++) {
		int id = sec_privilege_id((const char *)el->values[i].data);
		if (id == SEC_PRIV_INVALID) {
			/* Perhaps an account right, not a privilege */
			continue;
		}
		privs->set[j].attribute = 0;
		privs->set[j].luid.low = id;
		privs->set[j].luid.high = 0;
		j++;
	}

	privs->count = j;

	return NT_STATUS_OK;
}


static NTSTATUS pdb_samba_dsdb_add_groupmem(struct pdb_methods *m,
				     TALLOC_CTX *mem_ctx,
				     uint32_t group_rid, uint32_t member_rid)
{
	return pdb_samba_dsdb_mod_groupmem(m, mem_ctx, group_rid, member_rid,
				    LDB_FLAG_MOD_ADD);
}


int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
			const char *attr_name, uint64_t v)
{
	return samdb_msg_add_int64(sam_ldb, mem_ctx, msg, attr_name, (int64_t)v);
}


static int map_op_remote_callback(struct ldb_request *req,
				  struct ldb_reply *ares)
{
	struct ldb_context *ldb;
	struct map_context *ac;

	ac = talloc_get_type(req->context, struct map_context);
	ldb = ldb_module_get_ctx(ac->module);

	if (!ares) {
		return ldb_module_done(ac->req, NULL, NULL,
					LDB_ERR_OPERATIONS_ERROR);
	}
	if (ares->error != LDB_SUCCESS) {
		return ldb_module_done(ac->req, ares->controls,
					ares->response, ares->error);
	}

	if (ares->type != LDB_REPLY_DONE) {
		ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
		return ldb_module_done(ac->req, NULL, NULL,
					LDB_ERR_OPERATIONS_ERROR);
	}

	return ldb_module_done(ac->req, ares->controls,
					ares->response, ares->error);
}


static NTSTATUS dcesrv_interface_lsarpc_init_server(struct dcesrv_context *dce_ctx,
						    const struct dcesrv_endpoint_server *ep_server)
{
	if (lpcfg_lsa_over_netlogon(dce_ctx->lp_ctx)) {
		NTSTATUS ret = dcesrv_interface_register(dce_ctx,
						NCACN_NP_PIPE_NETLOGON,
						NCACN_NP_PIPE_LSASS,
						&dcesrv_lsarpc_interface, NULL);
		if (!NT_STATUS_IS_OK(ret)) {
			DEBUG(1,("lsarpc_op_init_server: failed to register endpoint '\\pipe\\netlogon'\n"));
			return ret;
		}
	}
	return lsarpc__op_init_server(dce_ctx, ep_server);
}


static int samldb_extended_allocate_rid_pool(struct ldb_module *module, struct ldb_request *req)
{
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	struct dsdb_fsmo_extended_op *exop;
	int ret;

	exop = talloc_get_type(req->op.extended.data,
			       struct dsdb_fsmo_extended_op);
	if (!exop) {
		ldb_set_errstring(ldb,
				  "samldb_extended_allocate_rid_pool: invalid extended data");
		return LDB_ERR_PROTOCOL_ERROR;
	}

	ret = ridalloc_allocate_rid_pool_fsmo(module, exop, req);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
}


NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb,
					  TALLOC_CTX *mem_ctx,
					  struct ldb_dn *domain_dn,
					  struct ldb_message *msg,
					  const char *attr)
{
	uint64_t attr_time = ldb_msg_find_attr_as_uint64(msg, attr, 0);
	int64_t minPwdAge;

	if (attr_time == 0) {
		return 0;
	}

	minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "minPwdAge", NULL);

	/* yes, this is a -= not a += as minPwdAge is stored as the negative
	   of the number of 100-nano-seconds */
	attr_time -= minPwdAge;

	return attr_time;
}


static int replmd_name_modify(struct replmd_replicated_request *ar,
			      struct ldb_request *req, struct ldb_dn *dn)
{
	struct ldb_message *msg;
	const char *rdn_name;
	const struct ldb_val *rdn_val;
	const struct dsdb_attribute *rdn_attr;
	int ret;

	msg = ldb_msg_new(req);
	if (msg == NULL) {
		goto failed;
	}
	msg->dn = dn;

	rdn_name = ldb_dn_get_rdn_name(dn);
	if (rdn_name == NULL) {
		goto failed;
	}

	/* normalize the rdn attribute name */
	rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
	if (rdn_attr == NULL) {
		goto failed;
	}
	rdn_name = rdn_attr->lDAPDisplayName;

	rdn_val = ldb_dn_get_rdn_val(dn);
	if (rdn_val == NULL) {
		goto failed;
	}

	if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
		goto failed;
	}
	if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
		goto failed;
	}

	/*
	 * We have to mark this as a replicated update otherwise
	 * schema_data may reject a rename in the schema partition
	 */

	ret = dsdb_module_modify(ar->module, msg,
				 DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
				 req);
	if (ret != LDB_SUCCESS) {
		DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
			 ldb_dn_get_linearized(dn),
			 ldb_errstring(ldb_module_get_ctx(ar->module))));
		return ret;
	}

	talloc_free(msg);

	return LDB_SUCCESS;

failed:
	talloc_free(msg);
	DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
		 ldb_dn_get_linearized(dn)));
	return LDB_ERR_OPERATIONS_ERROR;
}


static NTSTATUS dcesrv_lsa_LSARUNREGISTERAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
		       struct lsa_LSARUNREGISTERAUDITEVENT *r)
{
	DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
}


uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
{
	struct ldb_message *msg;
	struct ldb_context *wins_db = h->ldb;
	TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
	int trans;
	int ret;
	unsigned int i;

	trans = ldb_transaction_start(wins_db);
	if (trans != LDB_SUCCESS) goto failed;

	if (flags & WINSDB_FLAG_ALLOC_VERSION) {
		/* passing '0' means auto-allocate a new one */
		rec->version = winsdb_set_maxVersion(h, 0);
		if (rec->version == 0) goto failed;
	}
	if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
		rec->wins_owner = h->local_owner;
	}

	msg = winsdb_message(wins_db, rec, tmp_ctx);
	if (msg == NULL) goto failed;

	for (i=0;i<msg->num_elements;i++) {
		msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
	}

	ret = ldb_modify(wins_db, msg);
	if (ret != LDB_SUCCESS) goto failed;

	trans = ldb_transaction_commit(wins_db);
	if (trans != LDB_SUCCESS) goto failed;

	wins_hook(h, rec, WINS_HOOK_MODIFY, h->hook_script);

	talloc_free(tmp_ctx);
	return NBT_RCODE_OK;

failed:
	if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
	talloc_free(tmp_ctx);
	return NBT_RCODE_SVR;
}


static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
{
	struct ldb_context *ldb;
	int ret;
	char *tmp_str;
	char *filter;
	struct ldb_request *search_req;
	static const char *attrs[] = { "repsFrom", "replUpToDateVector",
				       "parentGUID", "instanceType",
				       "replPropertyMetaData", "nTSecurityDescriptor",
				       "isDeleted", NULL };
	struct GUID_txt_buf guid_str_buf;

	if (ar->index_current >= ar->objs->num_objects) {

		/*
		 * Now that we've applied all the objects, check the new linked
		 * attributes and store them (we apply them in .prepare_commit)
		 */
		ret = replmd_store_linked_attributes(ar);

		if (ret != LDB_SUCCESS) {
			return ret;
		}

		/* done applying objects, move on to the next stage */
		return replmd_replicated_uptodate_vector(ar);
	}

	ldb = ldb_module_get_ctx(ar->module);
	ar->search_msg = NULL;
	ar->isDeleted = false;

	tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
				  &guid_str_buf);

	filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
	if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);

	ret = ldb_build_search_req(&search_req,
				   ldb,
				   ar,
				   ar->objs->partition_dn,
				   LDB_SCOPE_SUBTREE,
				   filter,
				   attrs,
				   NULL,
				   ar,
				   replmd_replicated_apply_search_callback,
				   ar->req);
	LDB_REQ_SET_LOCATION(search_req);

	/*
	 * We set DSDB_SEARCH_SHOW_EXTENDED_DN to get the GUID on the
	 * DN.  This in turn helps our operational module find the
	 * record by GUID, not DN lookup which is more error prone if
	 * DN indexing changes.  We prefer to keep chasing GUIDs
	 * around if possible, even within a transaction.
	 *
	 * The aim here is to keep replication moving and allow a
	 * reindex later.
	 */
	ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED
					|DSDB_SEARCH_SHOW_EXTENDED_DN);

	if (ret != LDB_SUCCESS) {
		return ret;
	}

	return ldb_next_request(ar->module, search_req);
}


const char *samdb_cn_to_lDAPDisplayName(TALLOC_CTX *mem_ctx, const char *cn)
{
	char **tokens, *ret;
	size_t i;

	tokens = str_list_make(mem_ctx, cn, " -_");
	if (tokens == NULL || tokens[0] == NULL) {
		return NULL;
	}

	/* "tolower()" and "toupper()" should also work properly on 0x00 */
	tokens[0][0] = tolower(tokens[0][0]);
	for (i = 1; tokens[i] != NULL; i++)
		tokens[i][0] = toupper(tokens[i][0]);

	ret = talloc_strdup(mem_ctx, tokens[0]);
	for (i = 1; tokens[i] != NULL; i++)
		ret = talloc_asprintf_append_buffer(ret, "%s", tokens[i]);

	talloc_free(tokens);

	return ret;
}


struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx,
				  const struct ldb_map_attribute *map, const struct ldb_val *val)
{
	if (map && (map->type == LDB_MAP_CONVERT) && (map->u.convert.convert_remote)) {
		return map->u.convert.convert_remote(module, mem_ctx, val);
	}

	return ldb_val_dup(mem_ctx, val);
}


int samdb_rodc(struct ldb_context *sam_ctx, bool *am_rodc)
{
	const struct GUID *objectGUID;
	int ret;
	bool *cached;

	/* see if we have a cached copy */
	cached = (bool *)ldb_get_opaque(sam_ctx, "cache.am_rodc");
	if (cached) {
		*am_rodc = *cached;
		return LDB_SUCCESS;
	}

	objectGUID = samdb_ntds_objectGUID(sam_ctx);
	if (!objectGUID) {
		return ldb_operr(sam_ctx);
	}

	ret = samdb_is_rodc(sam_ctx, objectGUID, am_rodc);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	cached = talloc(sam_ctx, bool);
	if (cached == NULL) {
		return ldb_oom(sam_ctx);
	}
	*cached = *am_rodc;

	ret = ldb_set_opaque(sam_ctx, "cache.am_rodc", cached);
	if (ret != LDB_SUCCESS) {
		talloc_free(cached);
		return ldb_operr(sam_ctx);
	}

	return LDB_SUCCESS;
}


NTSTATUS dsdb_get_extended_dn_uint64(struct ldb_dn *dn, uint64_t *val, const char *component_name)
{
	const struct ldb_val *v;
	int error = 0;

	v = ldb_dn_get_extended_component(dn, component_name);
	if (v == NULL) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	/* Just check we don't allow the caller to fill our stack */
	if (v->length >= 64) {
		return NT_STATUS_INVALID_PARAMETER;
	} else {
		char s[v->length+1];
		memcpy(s, v->data, v->length);
		s[v->length] = 0;

		*val = smb_strtoull(s, NULL, 0, &error, SMB_STR_STANDARD);
		if (error != 0) {
			return NT_STATUS_INVALID_PARAMETER;
		}
	}
	return NT_STATUS_OK;
}


static NTSTATUS pdb_samba_dsdb_delete_alias(struct pdb_methods *m,
				     const struct dom_sid *sid)
{
	const char *attrs[] = { NULL };
	struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
		m->private_data, struct pdb_samba_dsdb_state);
	struct ldb_message *msg;
	struct ldb_dn *dn;
	int rc;
	struct dom_sid_buf buf;
	TALLOC_CTX *tmp_ctx = talloc_stackframe();
	NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);

	dn = ldb_dn_new_fmt(
		tmp_ctx,
		state->ldb,
		"<SID=%s>",
		dom_sid_str_buf(sid, &buf));
	if (!dn || !ldb_dn_validate(dn)) {
		talloc_free(tmp_ctx);
		return NT_STATUS_NO_MEMORY;
	}

	if (ldb_transaction_start(state->ldb) != LDB_SUCCESS) {
		DEBUG(0, ("Failed to start transaction in dsdb_add_domain_alias(): %s\n", ldb_errstring(state->ldb)));
		talloc_free(tmp_ctx);
		return NT_STATUS_INTERNAL_ERROR;
	}

	rc = dsdb_search_one(state->ldb, tmp_ctx, &msg, dn, LDB_SCOPE_BASE, attrs, 0, "(objectclass=group)"
			     "(|(grouptype=%d)(grouptype=%d)))",
			     GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
			     GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
	if (rc == LDB_ERR_NO_SUCH_OBJECT) {
		talloc_free(tmp_ctx);
		ldb_transaction_cancel(state->ldb);
		return NT_STATUS_NO_SUCH_ALIAS;
	}
	rc = ldb_delete(state->ldb, dn);
	if (rc == LDB_ERR_NO_SUCH_OBJECT) {
		talloc_free(tmp_ctx);
		ldb_transaction_cancel(state->ldb);
		return NT_STATUS_NO_SUCH_ALIAS;
	} else if (rc != LDB_SUCCESS) {
		DEBUG(10, ("ldb_delete failed %s\n",
			   ldb_errstring(state->ldb)));
		ldb_transaction_cancel(state->ldb);
		talloc_free(tmp_ctx);
		return NT_STATUS_LDAP(rc);
	}

	if (ldb_transaction_commit(state->ldb) != LDB_SUCCESS) {
		DEBUG(0, ("Failed to commit transaction in pdb_samba_dsdb_delete_alias(): %s\n",
			  ldb_errstring(state->ldb)));
		talloc_free(tmp_ctx);
		return NT_STATUS_INTERNAL_ERROR;
	}

	talloc_free(tmp_ctx);
	return NT_STATUS_OK;
}


static NTSTATUS pdb_samba_dsdb_set_trusted_domain(struct pdb_methods *methods,
						  const char* domain,
						  const struct pdb_trusted_domain *td)
{
	struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
		methods->private_data, struct pdb_samba_dsdb_state);
	TALLOC_CTX *tmp_ctx = talloc_stackframe();
	bool in_txn = false;
	struct ldb_dn *base_dn = NULL;
	struct ldb_message *msg = NULL;
	const char *attrs[] = {
		NULL
	};
	char *netbios_encoded = NULL;
	char *dns_encoded = NULL;
	char *sid_encoded = NULL;
	int ret;
	struct trustAuthInOutBlob taiob;
	enum ndr_err_code ndr_err;
	NTSTATUS status;
	bool ok;

	base_dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->ldb));
	if (base_dn == NULL) {
		TALLOC_FREE(tmp_ctx);
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}
	/*
	 * We expect S-1-5-21-A-B-C, but we don't
	 * allow S-1-5-21-0-0-0 as this is used
	 * for claims and compound identities.
	 */
	ok = dom_sid_is_valid_account_domain(&td->security_identifier);
	if (!ok) {
		status = NT_STATUS_INVALID_PARAMETER;
		goto out;
	}

	if (strequal(td->netbios_name, "BUILTIN")) {
		status = NT_STATUS_INVALID_PARAMETER;
		goto out;
	}
	if (strequal(td->domain_name, "BUILTIN")) {
		status = NT_STATUS_INVALID_PARAMETER;
		goto out;
	}

	dns_encoded = ldb_binary_encode_string(tmp_ctx, td->domain_name);
	if (dns_encoded == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}
	netbios_encoded = ldb_binary_encode_string(tmp_ctx, td->netbios_name);
	if (netbios_encoded == NULL) {
		status =NT_STATUS_NO_MEMORY;
		goto out;
	}
	sid_encoded = ldap_encode_ndr_dom_sid(tmp_ctx, &td->security_identifier);
	if (sid_encoded == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	ok = samdb_is_pdc(state->ldb);
	if (!ok) {
		DBG_ERR("Adding TDO is only allowed on a PDC.\n");
		TALLOC_FREE(tmp_ctx);
		status = NT_STATUS_INVALID_DOMAIN_ROLE;
		goto out;
	}

	status = dsdb_trust_search_tdo(state->ldb,
				       td->netbios_name,
				       td->domain_name,
				       attrs,
				       tmp_ctx,
				       &msg);
	if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
		DBG_ERR("dsdb_trust_search_tdo returned %s\n",
			nt_errstr(status));
		status = NT_STATUS_INVALID_DOMAIN_STATE;
		goto out;
	}

	ret = ldb_transaction_start(state->ldb);
	if (ret != LDB_SUCCESS) {
		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
		goto out;
	}
	in_txn = true;

	msg = ldb_msg_new(tmp_ctx);
	if (msg == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	msg->dn = ldb_dn_copy(tmp_ctx, base_dn);

	ok = ldb_dn_add_child_fmt(msg->dn, "cn=%s,cn=System", td->domain_name);
	if (!ok) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	ret = ldb_msg_add_string(msg, "objectClass", "trustedDomain");
	if (ret != LDB_SUCCESS) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	ret = ldb_msg_add_string(msg, "flatname", td->netbios_name);
	if (ret != LDB_SUCCESS) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	ret = ldb_msg_add_string(msg, "trustPartner", td->domain_name);
	if (ret != LDB_SUCCESS) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	ret = samdb_msg_add_dom_sid(state->ldb,
				    tmp_ctx,
				    msg,
				    "securityIdentifier",
				    &td->security_identifier);
	if (ret != LDB_SUCCESS) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	ret = samdb_msg_add_int(state->ldb,
				tmp_ctx,
				msg,
				"trustType",
				td->trust_type);
	if (ret != LDB_SUCCESS) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	ret = samdb_msg_add_int(state->ldb,
				tmp_ctx,
				msg,
				"trustAttributes",
				td->trust_attributes);
	if (ret != LDB_SUCCESS) {
		status =NT_STATUS_NO_MEMORY;
		goto out;
	}

	ret = samdb_msg_add_int(state->ldb,
				tmp_ctx,
				msg,
				"trustDirection",
				td->trust_direction);
	if (ret != LDB_SUCCESS) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	if (td->trust_auth_incoming.data != NULL) {
		ret = ldb_msg_add_value(msg,
					"trustAuthIncoming",
					&td->trust_auth_incoming,
					NULL);
		if (ret != LDB_SUCCESS) {
			status = NT_STATUS_NO_MEMORY;
			goto out;
		}
	}
	if (td->trust_auth_outgoing.data != NULL) {
		ret = ldb_msg_add_value(msg,
					"trustAuthOutgoing",
					&td->trust_auth_outgoing,
					NULL);
		if (ret != LDB_SUCCESS) {
			status = NT_STATUS_NO_MEMORY;
			goto out;
		}
	}

	/* create the trusted_domain */
	ret = ldb_add(state->ldb, msg);
	switch (ret) {
	case  LDB_SUCCESS:
		break;

	case  LDB_ERR_ENTRY_ALREADY_EXISTS:
		DBG_ERR("Failed to create trusted domain record %s: %s\n",
			ldb_dn_get_linearized(msg->dn),
			ldb_errstring(state->ldb));
		status = NT_STATUS_DOMAIN_EXISTS;
		goto out;

	case  LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
		DBG_ERR("Failed to create trusted domain record %s: %s\n",
			ldb_dn_get_linearized(msg->dn),
			ldb_errstring(state->ldb));
		status = NT_STATUS_ACCESS_DENIED;
		goto out;

	default:
		DBG_ERR("Failed to create trusted domain record %s: %s\n",
			ldb_dn_get_linearized(msg->dn),
			ldb_errstring(state->ldb));
		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
		goto out;
	}

	ndr_err = ndr_pull_struct_blob(
		&td->trust_auth_outgoing,
		tmp_ctx,
		&taiob,
		(ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		status = ndr_map_error2ntstatus(ndr_err);
		goto out;
	}

	if (td->trust_direction == LSA_TRUST_DIRECTION_INBOUND) {
		status = add_trust_user(tmp_ctx,
					state->ldb,
					base_dn,
					td->netbios_name,
					&taiob);
		if (!NT_STATUS_IS_OK(status)) {
			goto out;
		}
	}

	ret = ldb_transaction_commit(state->ldb);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_INTERNAL_DB_CORRUPTION;
	}
	in_txn = false;

	/*
	 * TODO: Notify winbindd that we have a new trust
	 */

	status = NT_STATUS_OK;

out:
	if (in_txn) {
		ldb_transaction_cancel(state->ldb);
	}
	TALLOC_FREE(tmp_ctx);
	return status;
}


uint32_t dsdb_dn_rmd_flags(struct ldb_dn *dn)
{
	uint32_t rmd_flags = 0;
	NTSTATUS status = dsdb_get_extended_dn_uint32(dn, &rmd_flags,
						      "RMD_FLAGS");
	if (NT_STATUS_IS_OK(status)) {
		return rmd_flags;
	}
	return 0;
}


static DATA_BLOB *descr_get_descriptor_to_show(struct ldb_module *module,
					       TALLOC_CTX *mem_ctx,
					       struct ldb_val *sd,
					       uint32_t sd_flags)
{
	struct security_descriptor *old_sd, *final_sd;
	DATA_BLOB *linear_sd;
	enum ndr_err_code ndr_err;

	old_sd = talloc(mem_ctx, struct security_descriptor);
	if (!old_sd) {
		return NULL;
	}
	ndr_err = ndr_pull_struct_blob(sd, old_sd,
				       old_sd,
				       (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);

	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		talloc_free(old_sd);
		return NULL;
	}

	final_sd = descr_handle_sd_flags(mem_ctx, old_sd, NULL, sd_flags);

	if (!final_sd) {
		return NULL;
	}

	linear_sd = talloc(mem_ctx, DATA_BLOB);
	if (!linear_sd) {
		return NULL;
	}

	ndr_err = ndr_push_struct_blob(linear_sd, mem_ctx,
				       final_sd,
				       (ndr_push_flags_fn_t)ndr_push_security_descriptor);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return NULL;
	}

	return linear_sd;
}


static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
			  struct ldb_message_element *el, struct parsed_dn **pdn,
			  const char *ldap_oid, struct ldb_request *parent)
{
	unsigned int i;
	bool values_are_sorted = true;
	struct ldb_context *ldb = ldb_module_get_ctx(module);

	if (el == NULL) {
		*pdn = NULL;
		return LDB_SUCCESS;
	}

	(*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
	if (!*pdn) {
		ldb_module_oom(module);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	for (i=0; i<el->num_values; i++) {
		struct ldb_val *v = &el->values[i];
		NTSTATUS status;
		struct ldb_dn *dn;
		struct parsed_dn *p;

		p = &(*pdn)[i];

		p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
		if (p->dsdb_dn == NULL) {
			return LDB_ERR_INVALID_DN_SYNTAX;
		}

		dn = p->dsdb_dn->dn;

		status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
		if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
		    unlikely(GUID_all_zero(&p->guid))) {
			/* we got a DN without a GUID - go find the GUID */
			int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
			if (ret != LDB_SUCCESS) {
				char *dn_str = NULL;
				dn_str = ldb_dn_get_extended_linearized(mem_ctx,
									(dn), 1);
				ldb_asprintf_errstring(ldb,
						"Unable to find GUID for DN %s\n",
						dn_str);
				if (ret == LDB_ERR_NO_SUCH_OBJECT &&
				    LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
				    ldb_attr_cmp(el->name, "member") == 0) {
					return LDB_ERR_UNWILLING_TO_PERFORM;
				}
				return ret;
			}
			ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
			if (ret != LDB_SUCCESS) {
				return ret;
			}
		} else if (!NT_STATUS_IS_OK(status)) {
			return LDB_ERR_OPERATIONS_ERROR;
		}
		if (i > 0 && values_are_sorted) {
			int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
			if (cmp < 0) {
				values_are_sorted = false;
			}
		}
		/* keep a pointer to the original ldb_val */
		p->v = v;
	}
	if (! values_are_sorted) {
		TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
	}
	return LDB_SUCCESS;
}


static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
					enum urgent_situation situation)
{
	unsigned int i, j;
	for (i=0; urgent_objects[i].update_name; i++) {

		if ((situation & urgent_objects[i].repl_situation) == 0) {
			continue;
		}

		for (j=0; j<objectclass_el->num_values; j++) {
			const struct ldb_val *v = &objectclass_el->values[j];
			if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
				return true;
			}
		}
	}
	return false;
}


static WERROR dcesrv_dssetup_DsRoleDnsNameToFlatName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
					struct dssetup_DsRoleDnsNameToFlatName *r)
{
	DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
}


			       const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
{
	va_list ap;
	struct ldb_dn *ret;
	struct ldb_message **res = NULL;
	int count;

	va_start(ap, format);
	count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, NULL, format, ap);
	va_end(ap);

	if (count != 1) return NULL;

	ret = talloc_steal(mem_ctx, res[0]->dn);
	talloc_free(res);

	return ret;
}


static NTSTATUS dcesrv_lsa_EnumTrustedDomainsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
					struct lsa_EnumTrustedDomainsEx *r)
{
	struct dcesrv_handle *policy_handle;
	struct lsa_TrustDomainInfoInfoEx *entries;
	struct lsa_policy_state *policy_state;
	struct ldb_message **domains;
	const char *attrs[] = {
		"flatname",
		"trustPartner",
		"securityIdentifier",
		"trustDirection",
		"trustType",
		"trustAttributes",
		NULL
	};
	NTSTATUS nt_status;

	int count, i;

	*r->out.resume_handle = 0;

	r->out.domains->domains = NULL;
	r->out.domains->count = 0;

	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);

	policy_state = policy_handle->data;

	/* search for all users in this domain. This could possibly be cached and
	   resumed based on resume_key */
	count = gendb_search(policy_state->sam_ldb, mem_ctx, policy_state->system_dn, &domains, attrs,
			     "objectclass=trustedDomain");
	if (count < 0) {
		return NT_STATUS_INTERNAL_DB_CORRUPTION;
	}

	/* convert to lsa_DomainInformation format */
	entries = talloc_array(mem_ctx, struct lsa_TrustDomainInfoInfoEx, count);
	if (!entries) {
		return NT_STATUS_NO_MEMORY;
	}
	for (i=0;i<count;i++) {
		nt_status = fill_trust_domain_ex(mem_ctx, domains[i], &entries[i]);
		if (!NT_STATUS_IS_OK(nt_status)) {
			return nt_status;
		}
	}

	/* sort the results by name */
	TYPESAFE_QSORT(entries, count, compare_TrustDomainInfoInfoEx);

	if (*r->in.resume_handle >= count) {
		*r->out.resume_handle = -1;

		return NT_STATUS_NO_MORE_ENTRIES;
	}

	/* return the rest, limit by max_size. Note that we
	   use the w2k3 element size value of 60 */
	r->out.domains->count = count - *r->in.resume_handle;
	r->out.domains->count = MIN(r->out.domains->count,
				 1+(r->in.max_size/LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER));

	r->out.domains->domains = entries + *r->in.resume_handle;

	if (r->out.domains->count < count - *r->in.resume_handle) {
		*r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;
		return STATUS_MORE_ENTRIES;
	}

	*r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;

	return NT_STATUS_OK;
}


int dsdb_set_extended_dn_guid(struct ldb_dn *dn, const struct GUID *guid, const char *component_name)
{
	struct ldb_val v;
	NTSTATUS status;
	int ret;

	status = GUID_to_ndr_blob(guid, dn, &v);
	if (!NT_STATUS_IS_OK(status)) {
		return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
	}

	ret = ldb_dn_set_extended_component(dn, component_name, &v);
	data_blob_free(&v);
	return ret;
}


struct ldb_dn *samdb_aggregate_schema_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
{
	struct ldb_dn *schema_dn = ldb_get_schema_basedn(sam_ctx);
	struct ldb_dn *aggregate_dn;
	if (!schema_dn) {
		return NULL;
	}

	aggregate_dn = ldb_dn_copy(mem_ctx, schema_dn);
	if (!aggregate_dn) {
		return NULL;
	}
	if (!ldb_dn_add_child_fmt(aggregate_dn, "CN=Aggregate")) {
		return NULL;
	}
	return aggregate_dn;
}


static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, const char *name)
{
	static const char * const attrs[] = { MAP_DN_FROM, MAP_DN_TO, NULL };
	struct ldb_context *ldb;
	struct ldb_dn *dn;
	struct ldb_message *msg;
	struct ldb_result *res;
	int ret;

	if (!name) {
		data->local_base_dn = NULL;
		data->remote_base_dn = NULL;
		return LDB_SUCCESS;
	}

	ldb = ldb_module_get_ctx(module);

	dn = ldb_dn_new_fmt(data, ldb, "%s=%s", MAP_DN_NAME, name);
	if ( ! ldb_dn_validate(dn)) {
		ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
			  "Failed to construct '%s' DN!", MAP_DN_NAME);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ret = ldb_search(ldb, data, &res, dn, LDB_SCOPE_BASE, attrs, NULL);
	talloc_free(dn);
	if (ret != LDB_SUCCESS) {
		return ret;
	}
	if (res->count == 0) {
		ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
			  "No results for '%s=%s'!", MAP_DN_NAME, name);
		talloc_free(res);
		return LDB_ERR_CONSTRAINT_VIOLATION;
	}
	if (res->count > 1) {
		ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
			  "Too many results for '%s=%s'!", MAP_DN_NAME, name);
		talloc_free(res);
		return LDB_ERR_CONSTRAINT_VIOLATION;
	}

	msg = res->msgs[0];
	data->local_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_FROM);
	data->remote_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_TO);
	talloc_free(res);

	return LDB_SUCCESS;
}


static int check_cidr_zero_bits(uint8_t *address, unsigned int len,
				unsigned int mask)
{
	/* <address> is an integer in big-endian form, <len> bits long. All
	   bits between <mask> and <len> must be zero. */
	int i;
	unsigned int byte_len;
	unsigned int byte_mask;
	unsigned int bit_mask;
	if (len == 32) {
		DBG_INFO("Looking at address %02x%02x%02x%02x, mask %u\n",
			 address[0], address[1], address[2], address[3],
			  mask);
	} else if (len == 128){
		DBG_INFO("Looking at address "
			 "%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
			 "%02x%02x-%02x%02x-%02x%02x-%02x%02x, mask %u\n",
			 address[0], address[1], address[2], address[3],
			 address[4], address[5], address[6], address[7],
			 address[8], address[9], address[10], address[11],
			 address[12], address[13], address[14], address[15],
			 mask);
	}

	if (mask > len){
		DBG_INFO("mask %u is too big (> %u)\n", mask, len);
		return -1;
	}
	if (mask == len){
		/* single address subnet.
		 * In IPv4 all 255s is invalid by the bitmask != address rule
		 * in MS-ADTS. IPv6 does not suffer.
		 */
		if (len == 32){
			if (address[0] == 255 &&
			    address[1] == 255 &&
			    address[2] == 255 &&
			    address[3] == 255){
				return -1;
			}
		}
		return 0;
	}

	byte_len = len / 8;
	byte_mask = mask / 8;

	for (i = byte_len - 1; i > byte_mask; i--){
		DBG_DEBUG("checking byte %d %02x\n", i, address[i]);
		if (address[i] != 0){
			return -1;
		}
	}
	bit_mask = (1 << (8 - (mask & 7))) - 1;
	DBG_DEBUG("checking bitmask %02x & %02x overlap %02x\n", bit_mask, address[byte_mask],
		  bit_mask & address[byte_mask]);
	if (address[byte_mask] & bit_mask){
		return -1;
	}

	/* According to MS-ADTS, the mask can't exactly equal the bitmask for
	 * IPv4 (but this is fine for v6). That is 255.255.80.0/17 is bad,
	 * because the bitmask implied by "/17" is 255.255.80.0.
	 *
	 * The bit_mask used in the previous check is the complement of what
	 * we want here.
	 */
	if (len == 32 && address[byte_mask] == (uint8_t)~bit_mask){
		bool ok = false;
		for (i = 0; i < byte_mask; i++){
			if (address[i] != 255){
				ok = true;
				break;
			}
		}
		if (ok == false){
			return -1;
		}
	}
	return 0;
}


int samdb_msg_add_uint_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
			     const char *attr_name, unsigned int v, int flags)
{
	return samdb_msg_add_int_flags(sam_ldb, mem_ctx, msg, attr_name, (int)v, flags);
}


static int check_address_roundtrip(const char *address, int family,
				   const uint8_t *address_bytes,
				   char *buffer, int buffer_len)
{
	/*
	 * Check that the address is in the canonical RFC5952 format for IPv6,
	 * and lacks extra leading zeros for each dotted decimal for IPv4.
	 * Handily this is what inet_ntop() gives you.
	 */
	const char *address_redux = inet_ntop(family, address_bytes,
					      buffer, buffer_len);
	if (address_redux == NULL){
		DBG_INFO("Address round trip %s failed unexpectedly"
			 " with errno %d\n", address, errno);
		return -1;
	}
	if (strcasecmp(address, address_redux) != 0){
		DBG_INFO("Address %s round trips to %s; fail!\n",
			 address, address_redux);
		/* If the address family is IPv6, and the address is in a
		   certain range

		 */
		if (strchr(address_redux, '.') != NULL){
			DEBUG(0, ("The IPv6 address '%s' has the misfortune of "
				  "lying in a range that was once used for "
				  "IPv4 embedding (that is, it might also be "
				  "represented as '%s').\n", address,
				  address_redux));
		}
		return -1;
	}
	return 0;
}


NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx,
				   struct ldb_context *sam_ctx,
				   struct ldb_message *user_msg,
				   struct ldb_message *domain_msg,
				   struct ldb_message *pso_msg,
				   struct ldb_message **_mod_msg)
{
	int ret, badPwdCount;
	unsigned int i;
	int64_t lockoutThreshold, lockOutObservationWindow;
	struct dom_sid *sid;
	struct timeval tv_now = timeval_current();
	NTTIME now = timeval_to_nttime(&tv_now);
	NTSTATUS status;
	uint32_t pwdProperties, rid = 0;
	struct ldb_message *mod_msg;

	sid = samdb_result_dom_sid(mem_ctx, user_msg, "objectSid");

	pwdProperties = ldb_msg_find_attr_as_uint(domain_msg,
						  "pwdProperties", -1);
	if (sid && !(pwdProperties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
		status = dom_sid_split_rid(NULL, sid, NULL, &rid);
		if (!NT_STATUS_IS_OK(status)) {
			/*
			 * This can't happen anyway, but always try
			 * and update the badPwdCount on failure
			 */
			rid = 0;
		}
	}
	TALLOC_FREE(sid);

	/*
	 * Work out if we are doing password lockout on the domain.
	 * Also, the built in administrator account is exempt:
	 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa375371%28v=vs.85%29.aspx
	 */
	lockoutThreshold = get_lockout_threshold(domain_msg, pso_msg);
	if (lockoutThreshold == 0 || (rid == DOMAIN_RID_ADMINISTRATOR)) {
		DEBUG(5, ("Not updating badPwdCount on %s after wrong password\n",
			  ldb_dn_get_linearized(user_msg->dn)));
		return NT_STATUS_OK;
	}

	mod_msg = ldb_msg_new(mem_ctx);
	if (mod_msg == NULL) {
		return NT_STATUS_NO_MEMORY;
	}
	mod_msg->dn = ldb_dn_copy(mod_msg, user_msg->dn);
	if (mod_msg->dn == NULL) {
		TALLOC_FREE(mod_msg);
		return NT_STATUS_NO_MEMORY;
	}

	lockOutObservationWindow = get_lockout_observation_window(domain_msg,
								  pso_msg);

	badPwdCount = dsdb_effective_badPwdCount(user_msg, lockOutObservationWindow, now);

	badPwdCount++;

	ret = samdb_msg_add_int(sam_ctx, mod_msg, mod_msg, "badPwdCount", badPwdCount);
	if (ret != LDB_SUCCESS) {
		TALLOC_FREE(mod_msg);
		return NT_STATUS_NO_MEMORY;
	}
	ret = samdb_msg_add_int64(sam_ctx, mod_msg, mod_msg, "badPasswordTime", now);
	if (ret != LDB_SUCCESS) {
		TALLOC_FREE(mod_msg);
		return NT_STATUS_NO_MEMORY;
	}

	if (badPwdCount >= lockoutThreshold) {
		ret = samdb_msg_add_int64(sam_ctx, mod_msg, mod_msg, "lockoutTime", now);
		if (ret != LDB_SUCCESS) {
			TALLOC_FREE(mod_msg);
			return NT_STATUS_NO_MEMORY;
		}
		DEBUGC( DBGC_AUTH, 1, ("Locked out user %s after %d wrong passwords\n",
			  ldb_dn_get_linearized(user_msg->dn), badPwdCount));
	} else {
		DEBUGC( DBGC_AUTH, 5, ("Updated badPwdCount on %s after %d wrong passwords\n",
			  ldb_dn_get_linearized(user_msg->dn), badPwdCount));
	}

	/* mark all the message elements as LDB_FLAG_MOD_REPLACE */
	for (i=0; i< mod_msg->num_elements; i++) {
		mod_msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
	}

	*_mod_msg = mod_msg;
	return NT_STATUS_OK;
}


				const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
{
	va_list ap;
	const char *str;

	va_start(ap, format);
	str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
	va_end(ap);

	return str;
}


static NTSTATUS pdb_samba_dsdb_enum_group_mapping(struct pdb_methods *m,
					   const struct dom_sid *sid,
					   enum lsa_SidType sid_name_use,
					   GROUP_MAP ***pp_rmap,
					   size_t *p_num_entries,
					   bool unix_only)
{
	return NT_STATUS_NOT_IMPLEMENTED;
}


bool dsdb_request_has_control(struct ldb_request *req, const char *oid)
{
	return (ldb_request_get_control(req, oid) != NULL);
}


static DATA_BLOB *get_new_descriptor(struct ldb_module *module,
				     struct ldb_dn *dn,
				     TALLOC_CTX *mem_ctx,
				     const struct dsdb_class *objectclass,
				     const struct ldb_val *parent,
				     const struct ldb_val *object,
				     const struct ldb_val *old_sd,
				     uint32_t sd_flags)
{
	struct security_descriptor *user_descriptor = NULL, *parent_descriptor = NULL;
	struct security_descriptor *old_descriptor = NULL;
	struct security_descriptor *new_sd, *final_sd;
	DATA_BLOB *linear_sd;
	enum ndr_err_code ndr_err;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	struct auth_session_info *session_info
		= ldb_get_opaque(ldb, DSDB_SESSION_INFO);
	const struct dom_sid *domain_sid = samdb_domain_sid(ldb);
	struct dom_sid *default_owner;
	struct dom_sid *default_group;
	struct security_descriptor *default_descriptor = NULL;
	struct GUID *object_list = NULL;

	if (objectclass != NULL) {
		default_descriptor = get_sd_unpacked(module, mem_ctx, objectclass);
		object_list = talloc_zero_array(mem_ctx, struct GUID, 2);
		if (object_list == NULL) {
			return NULL;
		}
		object_list[0] = objectclass->schemaIDGUID;
	}

	if (object) {
		user_descriptor = talloc(mem_ctx, struct security_descriptor);
		if (!user_descriptor) {
			return NULL;
		}
		ndr_err = ndr_pull_struct_blob(object, user_descriptor,
					       user_descriptor,
					       (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);

		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			talloc_free(user_descriptor);
			return NULL;
		}
	} else {
		user_descriptor = default_descriptor;
	}

	if (old_sd) {
		old_descriptor = talloc(mem_ctx, struct security_descriptor);
		if (!old_descriptor) {
			return NULL;
		}
		ndr_err = ndr_pull_struct_blob(old_sd, old_descriptor,
					       old_descriptor,
					       (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);

		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			talloc_free(old_descriptor);
			return NULL;
		}
	}

	if (parent) {
		parent_descriptor = talloc(mem_ctx, struct security_descriptor);
		if (!parent_descriptor) {
			return NULL;
		}
		ndr_err = ndr_pull_struct_blob(parent, parent_descriptor,
					       parent_descriptor,
					       (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);

		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			talloc_free(parent_descriptor);
			return NULL;
		}
	}

	if (user_descriptor && default_descriptor &&
	    (user_descriptor->dacl == NULL))
	{
		user_descriptor->dacl = default_descriptor->dacl;
		user_descriptor->type |= default_descriptor->type & (
			SEC_DESC_DACL_PRESENT |
			SEC_DESC_DACL_DEFAULTED|SEC_DESC_DACL_AUTO_INHERIT_REQ |
			SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_PROTECTED |
			SEC_DESC_DACL_TRUSTED);
	}

	if (user_descriptor && default_descriptor &&
	    (user_descriptor->sacl == NULL))
	{
		user_descriptor->sacl = default_descriptor->sacl;
		user_descriptor->type |= default_descriptor->type & (
			SEC_DESC_SACL_PRESENT |
			SEC_DESC_SACL_DEFAULTED|SEC_DESC_SACL_AUTO_INHERIT_REQ |
			SEC_DESC_SACL_AUTO_INHERITED|SEC_DESC_SACL_PROTECTED |
			SEC_DESC_SERVER_SECURITY);
	}

	if (!(sd_flags & SECINFO_OWNER) && user_descriptor) {
		user_descriptor->owner_sid = NULL;

		/*
		 * We need the correct owner sid
		 * when calculating the DACL or SACL
		 */
		if (old_descriptor) {
			user_descriptor->owner_sid = old_descriptor->owner_sid;
		}
	}
	if (!(sd_flags & SECINFO_GROUP) && user_descriptor) {
		user_descriptor->group_sid = NULL;

		/*
		 * We need the correct group sid
		 * when calculating the DACL or SACL
		 */
		if (old_descriptor) {
			user_descriptor->group_sid = old_descriptor->group_sid;
		}
	}
	if (!(sd_flags & SECINFO_DACL) && user_descriptor) {
		user_descriptor->dacl = NULL;

		/*
		 * We add SEC_DESC_DACL_PROTECTED so that
		 * create_security_descriptor() skips
		 * the unused inheritance calculation
		 */
		user_descriptor->type |= SEC_DESC_DACL_PROTECTED;
	}
	if (!(sd_flags & SECINFO_SACL) && user_descriptor) {
		user_descriptor->sacl = NULL;

		/*
		 * We add SEC_DESC_SACL_PROTECTED so that
		 * create_security_descriptor() skips
		 * the unused inheritance calculation
		 */
		user_descriptor->type |= SEC_DESC_SACL_PROTECTED;
	}

	default_owner = get_default_ag(mem_ctx, dn,
				       session_info->security_token, ldb);
	default_group = get_default_group(mem_ctx, ldb, default_owner);
	new_sd = create_security_descriptor(mem_ctx,
					    parent_descriptor,
					    user_descriptor,
					    true,
					    object_list,
					    SEC_DACL_AUTO_INHERIT |
					    SEC_SACL_AUTO_INHERIT,
					    session_info->security_token,
					    default_owner, default_group,
					    map_generic_rights_ds);
	if (!new_sd) {
		return NULL;
	}
	final_sd = descr_handle_sd_flags(mem_ctx, new_sd, old_descriptor, sd_flags);

	if (!final_sd) {
		return NULL;
	}

	if (final_sd->dacl) {
		final_sd->dacl->revision = SECURITY_ACL_REVISION_ADS;
	}
	if (final_sd->sacl) {
		final_sd->sacl->revision = SECURITY_ACL_REVISION_ADS;
	}

	{
		TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
		DBG_DEBUG("Object %s created with descriptor %s\n\n",
			  ldb_dn_get_linearized(dn),
			  sddl_encode(tmp_ctx, final_sd, domain_sid));
		TALLOC_FREE(tmp_ctx);
	}

	linear_sd = talloc(mem_ctx, DATA_BLOB);
	if (!linear_sd) {
		return NULL;
	}

	ndr_err = ndr_push_struct_blob(linear_sd, mem_ctx,
				       final_sd,
				       (ndr_push_flags_fn_t)ndr_push_security_descriptor);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return NULL;
	}

	return linear_sd;
}


			   const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
{
	va_list ap;
	int count;
	struct ldb_message **res;
	const char *attrs[2] = { NULL, NULL };

	attrs[0] = attr_name;

	va_start(ap, format);
	count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
	va_end(ap);

	if (count != 1) {
		return default_value;
	}

	return ldb_msg_find_attr_as_int64(res[0], attr_name, default_value);
}


static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
{
	int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
	if (ret == 0) {
		return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
				     &pdn2->dsdb_dn->extra_part);
	}
	return ret;
}


NTTIME samdb_result_account_expires(const struct ldb_message *msg)
{
	NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "accountExpires",
						 0);

	if (ret == 0)
		ret = 0x7FFFFFFFFFFFFFFFULL;

	return ret;
}


static int tr_prepare_rename(struct tr_context *ac,
			     const struct ldb_message_element *new_dn)
{
	struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
	int ret;

	ac->rename_dn = ldb_dn_from_ldb_val(ac, ldb, &new_dn->values[0]);
	if (ac->rename_dn == NULL) {
		return ldb_module_oom(ac->module);
	}

	ac->rename_res = talloc_zero(ac, struct ldb_result);
	if (ac->rename_res == NULL) {
		return ldb_module_oom(ac->module);
	}

	ret = ldb_build_rename_req(&ac->rename_req, ldb, ac,
				   ac->req_msg->dn,
				   ac->rename_dn,
				   NULL,
				   ac->rename_res,
				   ldb_modify_default_callback,
				   ac->req);
	LDB_REQ_SET_LOCATION(ac->rename_req);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	return ret;
}


static struct dom_sid *get_default_ag(TALLOC_CTX *mem_ctx,
			       struct ldb_dn *dn,
			       struct security_token *token,
			       struct ldb_context *ldb)
{
	TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
	const struct dom_sid *domain_sid = samdb_domain_sid(ldb);
	struct dom_sid *da_sid = dom_sid_add_rid(tmp_ctx, domain_sid, DOMAIN_RID_ADMINS);
	struct dom_sid *ea_sid = dom_sid_add_rid(tmp_ctx, domain_sid, DOMAIN_RID_ENTERPRISE_ADMINS);
	struct dom_sid *sa_sid = dom_sid_add_rid(tmp_ctx, domain_sid, DOMAIN_RID_SCHEMA_ADMINS);
	struct dom_sid *dag_sid;
	struct ldb_dn *nc_root;
	int ret;

	ret = dsdb_find_nc_root(ldb, tmp_ctx, dn, &nc_root);
	if (ret != LDB_SUCCESS) {
		talloc_free(tmp_ctx);
		return NULL;
	}

	if (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0) {
		if (security_token_has_sid(token, sa_sid)) {
			dag_sid = dom_sid_dup(mem_ctx, sa_sid);
		} else if (security_token_has_sid(token, ea_sid)) {
			dag_sid = dom_sid_dup(mem_ctx, ea_sid);
		} else if (security_token_has_sid(token, da_sid)) {
			dag_sid = dom_sid_dup(mem_ctx, da_sid);
		} else if (security_token_is_system(token)) {
			dag_sid = dom_sid_dup(mem_ctx, sa_sid);
		} else {
			dag_sid = NULL;
		}
	} else if (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) {
		if (security_token_has_sid(token, ea_sid)) {
			dag_sid = dom_sid_dup(mem_ctx, ea_sid);
		} else if (security_token_has_sid(token, da_sid)) {
			dag_sid = dom_sid_dup(mem_ctx, da_sid);
		} else if (security_token_is_system(token)) {
			dag_sid = dom_sid_dup(mem_ctx, ea_sid);
		} else {
			dag_sid = NULL;
		}
	} else if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) {
		if (security_token_has_sid(token, da_sid)) {
			dag_sid = dom_sid_dup(mem_ctx, da_sid);
		} else if (security_token_has_sid(token, ea_sid)) {
				dag_sid = dom_sid_dup(mem_ctx, ea_sid);
		} else if (security_token_is_system(token)) {
			dag_sid = dom_sid_dup(mem_ctx, da_sid);
		} else {
			dag_sid = NULL;
		}
	} else {
		dag_sid = NULL;
	}

	talloc_free(tmp_ctx);
	return dag_sid;
}


static struct ldb_dn *ldb_dn_rebase_remote(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
{
	struct ldb_dn *new_dn;

	new_dn = ldb_dn_copy(mem_ctx, dn);
	if ( ! ldb_dn_validate(new_dn)) {
		talloc_free(new_dn);
		return NULL;
	}

	/* may be we don't need to rebase at all */
	if ( ! data->remote_base_dn || ! data->local_base_dn) {
		return new_dn;
	}

	if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->local_base_dn))) {
		talloc_free(new_dn);
		return NULL;
	}

	if ( ! ldb_dn_add_base(new_dn, data->remote_base_dn)) {
		talloc_free(new_dn);
		return NULL;
	}

	return new_dn;
}


const char *samdb_dn_to_dnshostname(struct ldb_context *ldb,
				    TALLOC_CTX *mem_ctx,
				    struct ldb_dn *server_dn)
{
	int ldb_ret;
	struct ldb_result *res = NULL;
	const char * const attrs[] = { "dNSHostName", NULL};

	ldb_ret = ldb_search(ldb, mem_ctx, &res,
			     server_dn,
			     LDB_SCOPE_BASE,
			     attrs, NULL);
	if (ldb_ret != LDB_SUCCESS) {
		DEBUG(4, ("Failed to find dNSHostName for dn %s, ldb error: %s",
			  ldb_dn_get_linearized(server_dn), ldb_errstring(ldb)));
		return NULL;
	}

	return ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
}


struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr)
{
	const struct ldb_val *v;
	struct GUID guid;
	NTSTATUS status;

	v = ldb_msg_find_ldb_val(msg, attr);
	if (!v) return GUID_zero();

	status = GUID_from_ndr_blob(v, &guid);
	if (!NT_STATUS_IS_OK(status)) {
		return GUID_zero();
	}

	return guid;
}


static NTSTATUS dcesrv_lsa_GetUserName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
				struct lsa_GetUserName *r)
{
	enum dcerpc_transport_t transport =
		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
	struct auth_session_info *session_info =
		dcesrv_call_session_info(dce_call);
	NTSTATUS status = NT_STATUS_OK;
	const char *account_name;
	const char *authority_name;
	struct lsa_String *_account_name;
	struct lsa_String *_authority_name = NULL;

	if (transport != NCACN_NP && transport != NCALRPC) {
		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
	}

	/* this is what w2k3 does */
	r->out.account_name = r->in.account_name;
	r->out.authority_name = r->in.authority_name;

	if (r->in.account_name
	    && *r->in.account_name
	    /* && *(*r->in.account_name)->string */
	    ) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (r->in.authority_name
	    && *r->in.authority_name
	    /* && *(*r->in.authority_name)->string */
	    ) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	account_name = talloc_reference(mem_ctx, session_info->info->account_name);
	authority_name = talloc_reference(mem_ctx, session_info->info->domain_name);

	_account_name = talloc(mem_ctx, struct lsa_String);
	NT_STATUS_HAVE_NO_MEMORY(_account_name);
	_account_name->string = account_name;

	if (r->in.authority_name) {
		_authority_name = talloc(mem_ctx, struct lsa_String);
		NT_STATUS_HAVE_NO_MEMORY(_authority_name);
		_authority_name->string = authority_name;
	}

	*r->out.account_name = _account_name;
	if (r->out.authority_name) {
		*r->out.authority_name = _authority_name;
	}

	return status;
}


static int new_partition_set_replicated_metadata(struct ldb_context *ldb,
						 struct ldb_module *module, struct ldb_request *last_req,
						 struct partition_private_data *data,
						 struct dsdb_partition *partition)
{
	unsigned int i;
	int ret;
	/* for each replicate, copy from main partition.  If we get an error, we report it up the chain */
	for (i=0; data->replicate && data->replicate[i]; i++) {
		struct ldb_result *replicate_res;
		struct ldb_request *add_req;
		ret = dsdb_module_search_dn(module, last_req, &replicate_res,
					    data->replicate[i],
					    NULL,
					    DSDB_FLAG_NEXT_MODULE, NULL);
		if (ret == LDB_ERR_NO_SUCH_OBJECT) {
			continue;
		}
		if (ret != LDB_SUCCESS) {
			ldb_asprintf_errstring(ldb,
					       "Failed to search for %s from " DSDB_PARTITION_DN
					       " replicateEntries for new partition at %s on %s: %s",
					       ldb_dn_get_linearized(data->replicate[i]),
					       partition->backend_url,
					       ldb_dn_get_linearized(partition->ctrl->dn),
					       ldb_errstring(ldb));
			return ret;
		}

		/* Build add request */
		ret = ldb_build_add_req(&add_req, ldb, replicate_res,
					replicate_res->msgs[0], NULL, NULL,
					ldb_op_default_callback, last_req);
		LDB_REQ_SET_LOCATION(add_req);
		last_req = add_req;
		if (ret != LDB_SUCCESS) {
			/* return directly, this is a very unlikely error */
			return ret;
		}
		/* do request */
		ret = ldb_next_request(partition->module, add_req);
		/* wait */
		if (ret == LDB_SUCCESS) {
			ret = ldb_wait(add_req->handle, LDB_WAIT_ALL);
		}

		switch (ret) {
		case LDB_SUCCESS:
			break;

		case LDB_ERR_ENTRY_ALREADY_EXISTS:
			/* Handle this case specially - if the
			 * metadata already exists, replace it */
		{
			struct ldb_request *del_req;

			/* Don't leave a confusing string in the ldb_errstring() */
			ldb_reset_err_string(ldb);
			/* Build del request */
			ret = ldb_build_del_req(&del_req, ldb, replicate_res, replicate_res->msgs[0]->dn, NULL, NULL,
						ldb_op_default_callback, last_req);
			LDB_REQ_SET_LOCATION(del_req);
			last_req = del_req;
			if (ret != LDB_SUCCESS) {
				/* return directly, this is a very unlikely error */
				return ret;
			}
			/* do request */
			ret = ldb_next_request(partition->module, del_req);

			/* wait */
			if (ret == LDB_SUCCESS) {
				ret = ldb_wait(del_req->handle, LDB_WAIT_ALL);
			}
			if (ret != LDB_SUCCESS) {
				ldb_asprintf_errstring(ldb,
						       "Failed to delete  (for re-add) %s from " DSDB_PARTITION_DN
						       " replicateEntries in new partition at %s on %s: %s",
						       ldb_dn_get_linearized(data->replicate[i]),
						       partition->backend_url,
						       ldb_dn_get_linearized(partition->ctrl->dn),
						       ldb_errstring(ldb));
				return ret;
			}

			/* Build add request */
			ret = ldb_build_add_req(&add_req, ldb, replicate_res, replicate_res->msgs[0], NULL, NULL,
						ldb_op_default_callback, last_req);
			LDB_REQ_SET_LOCATION(add_req);
			last_req = add_req;
			if (ret != LDB_SUCCESS) {
				/* return directly, this is a very unlikely error */
				return ret;
			}

			/* do the add again */
			ret = ldb_next_request(partition->module, add_req);

			/* wait */
			if (ret == LDB_SUCCESS) {
				ret = ldb_wait(add_req->handle, LDB_WAIT_ALL);
			}

			if (ret != LDB_SUCCESS) {
				ldb_asprintf_errstring(ldb,
						       "Failed to add (after delete) %s from " DSDB_PARTITION_DN
						       " replicateEntries to new partition at %s on %s: %s",
						       ldb_dn_get_linearized(data->replicate[i]),
						       partition->backend_url,
						       ldb_dn_get_linearized(partition->ctrl->dn),
						       ldb_errstring(ldb));
				return ret;
			}
			break;
		}
		default:
		{
			ldb_asprintf_errstring(ldb,
					       "Failed to add %s from " DSDB_PARTITION_DN
					       " replicateEntries to new partition at %s on %s: %s",
					       ldb_dn_get_linearized(data->replicate[i]),
					       partition->backend_url,
					       ldb_dn_get_linearized(partition->ctrl->dn),
					       ldb_errstring(ldb));
			return ret;
		}
		}

		/* And around again, for the next thing we must merge */
	}
	return LDB_SUCCESS;
}


const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
{
	TALLOC_CTX *tmp_ctx;
	const struct dom_sid *domain_sid;
	const char *attrs[] = {
		"objectSid",
		NULL
	};
	struct ldb_result *res;
	int ret;

	/* see if we have a cached copy */
	domain_sid = (struct dom_sid *)ldb_get_opaque(ldb, "cache.domain_sid");
	if (domain_sid) {
		return domain_sid;
	}

	tmp_ctx = talloc_new(ldb);
	if (tmp_ctx == NULL) {
		goto failed;
	}

	ret = ldb_search(ldb, tmp_ctx, &res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, attrs, "objectSid=*");

	if (ret != LDB_SUCCESS) {
		goto failed;
	}

	if (res->count != 1) {
		goto failed;
	}

	domain_sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid");
	if (domain_sid == NULL) {
		goto failed;
	}

	/* cache the domain_sid in the ldb */
	if (ldb_set_opaque(ldb, "cache.domain_sid", discard_const_p(struct dom_sid, domain_sid)) != LDB_SUCCESS) {
		goto failed;
	}

	talloc_steal(ldb, domain_sid);
	talloc_free(tmp_ctx);

	return domain_sid;

failed:
	talloc_free(tmp_ctx);
	return NULL;
}


static NTSTATUS dcesrv_lsa_GetSystemAccessAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
		       struct lsa_GetSystemAccessAccount *r)
{
	struct dcesrv_handle *h;
	struct lsa_account_state *astate;
	int ret;
	unsigned int i;
	struct ldb_message **res;
	const char * const attrs[] = { "privilege", NULL};
	struct ldb_message_element *el;
	const char *sidstr;

	*(r->out.access_mask) = 0x00000000;

	DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);

	astate = h->data;

	sidstr = ldap_encode_ndr_dom_sid(mem_ctx, astate->account_sid);
	if (sidstr == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	ret = gendb_search(astate->policy->pdb, mem_ctx, NULL, &res, attrs,
			   "objectSid=%s", sidstr);
	if (ret < 0) {
		return NT_STATUS_INTERNAL_DB_CORRUPTION;
	}
	if (ret != 1) {
		return NT_STATUS_OK;
	}

	el = ldb_msg_find_element(res[0], "privilege");
	if (el == NULL || el->num_values == 0) {
		return NT_STATUS_OK;
	}

	for (i=0;i<el->num_values;i++) {
		uint32_t right_bit = sec_right_bit((const char *)el->values[i].data);
		if (right_bit == 0) {
			/* Perhaps an privilege, not a right */
			continue;
		}
		*(r->out.access_mask) |= right_bit;
	}

	return NT_STATUS_OK;
}


static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
			     struct replmd_private *replmd_private,
			     struct ldb_message_element *el,
			     struct replmd_replicated_request *ac,
			     NTTIME now,
			     struct ldb_dn *forward_dn,
			     const struct dsdb_attribute *sa,
			     struct ldb_request *parent)
{
	unsigned int i;
	TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	struct parsed_dn *pdn;
	/* We will take a reference to the schema in replmd_add_backlink */
	const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
	struct ldb_val *new_values = NULL;
	int ret;

	if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
		el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
	} else {
		ldb_asprintf_errstring(ldb,
				       "Attribute %s is single valued but "
				       "more than one value has been supplied",
				       el->name);
		talloc_free(tmp_ctx);
		return LDB_ERR_CONSTRAINT_VIOLATION;
	}

	/*
	 * At the successful end of these functions el->values is
	 * overwritten with new_values.  However get_parsed_dns()
	 * points p->v at the supplied el and it effectively gets used
	 * as a working area by replmd_build_la_val().  So we must
	 * duplicate it because our caller only called
	 * ldb_msg_copy_shallow().
	 */

	el->values = talloc_memdup(tmp_ctx,
				   el->values,
				   sizeof(el->values[0]) * el->num_values);
	if (el->values == NULL) {
		ldb_module_oom(module);
		talloc_free(tmp_ctx);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
			     sa->syntax->ldap_oid, parent);
	if (ret != LDB_SUCCESS) {
		talloc_free(tmp_ctx);
		return ret;
	}

	ret = check_parsed_dn_duplicates(module, el, pdn);
	if (ret != LDB_SUCCESS) {
		talloc_free(tmp_ctx);
		return ret;
	}

	new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
	if (new_values == NULL) {
		ldb_module_oom(module);
		talloc_free(tmp_ctx);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	for (i = 0; i < el->num_values; i++) {
		struct parsed_dn *p = &pdn[i];
		ret = replmd_build_la_val(new_values, p->v, p->dsdb_dn,
					  &ac->our_invocation_id,
					  ac->seq_num, now);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}

		ret = replmd_defer_add_backlink(module, replmd_private,
						schema, ac,
						forward_dn, &p->guid, true, sa,
						parent);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}

		new_values[i] = *p->v;
	}
	el->values = talloc_steal(mem_ctx, new_values);

	talloc_free(tmp_ctx);
	return LDB_SUCCESS;
}


static NTSTATUS update_uint32_t_value(TALLOC_CTX *mem_ctx,
				      struct ldb_context *sam_ldb,
				      struct ldb_message *orig,
				      struct ldb_message *dest,
				      const char *attribute,
				      uint32_t value,
				      uint32_t *orig_value)
{
	const struct ldb_val *orig_val;
	uint32_t orig_uint = 0;
	unsigned int flags = 0;
	int ret;
	int error = 0;

	orig_val = ldb_msg_find_ldb_val(orig, attribute);
	if (!orig_val || !orig_val->data) {
		/* add new attribute */
		flags = LDB_FLAG_MOD_ADD;

	} else {
		orig_uint = smb_strtoul((const char *)orig_val->data,
					NULL,
					0,
					&error,
					SMB_STR_STANDARD);
		if (error != 0 || orig_uint != value) {
			/* replace also if can't get value */
			flags = LDB_FLAG_MOD_REPLACE;
		}
	}

	if (flags == 0) {
		/* stored value is identical, nothing to change */
		goto done;
	}

	ret = samdb_msg_append_uint(sam_ldb, dest, dest, attribute, value, flags);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_NO_MEMORY;
	}

done:
	if (orig_value) {
		*orig_value = orig_uint;
	}
	return NT_STATUS_OK;
}


void recovery_cipher_init(bool passphrase_protection, bool pin_protection,
                          const char *language, const char *label, bool _enforce_wordlist,
                          uint32_t _auto_lock_delay_ms, uint32_t _u2f_counter, bool _dry_run)
{
    enforce_wordlist = _enforce_wordlist;
    dry_run = _dry_run;

    if (!dry_run) {
        if (pin_protection) {
            if (!change_pin()) {
                recovery_abort();
                fsm_sendFailure(FailureType_Failure_ActionCancelled, "PINs do not match");
                layoutHome();
                return;
            }
        } else {
            storage_setPin("");
        }

        storage_setPassphraseProtected(passphrase_protection);
        storage_setLanguage(language);
        storage_setLabel(label);
        storage_setAutoLockDelayMs(_auto_lock_delay_ms);
        storage_setU2FCounter(_u2f_counter);
    } else if (!pin_protect("Enter Your PIN")) {
        layoutHome();
        return;
    }

    if (!confirm(ButtonRequestType_ButtonRequest_Other,
                 dry_run ? "Recovery Dry Run" : "Recovery",
                 "When entering your recovery seed, use the substitution cipher "
                 "and check that each word shows up correctly on the screen.")) {
        fsm_sendFailure(FailureType_Failure_ActionCancelled, "Recovery cancelled");
        if (!dry_run)
            storage_reset();
        layoutHome();
        return;
    }

    /* Clear mnemonic */
    memset(mnemonic, 0, sizeof(mnemonic) / sizeof(char));

    /* Set to recovery cipher mode and generate and show next cipher */
    awaiting_character = true;
    recovery_started = true;
    next_character();
}


static void get_current_word(char *current_word)
{
    char *pos = strrchr(mnemonic, ' ');

    if(pos)
    {
        pos++;
        strlcpy(current_word, pos, CURRENT_WORD_BUF);
    }
    else
    {
        strlcpy(current_word, mnemonic, CURRENT_WORD_BUF);
    }
}


int HevcPpsUnit::deserialize()
{
    int rez = HevcUnit::deserialize();
    if (rez)
        return rez;

    try
    {
        pps_id = extractUEGolombCode();
        if (pps_id > 63)
            return 1;
        sps_id = extractUEGolombCode();
        if (sps_id > 15)
            return 1;
        dependent_slice_segments_enabled_flag = m_reader.getBit();
        output_flag_present_flag = m_reader.getBit();
        num_extra_slice_header_bits = m_reader.getBits(3);

        return 0;
    }
    catch (VodCoreException& e)
    {
        return NOT_ENOUGH_BUFFER;
    }
}


int HevcUnitWithProfile::profile_tier_level(int subLayers)
{
    try
    {
        int profile_space = m_reader.getBits(2);
        int tier_flag = m_reader.getBit();
        profile_idc = m_reader.getBits(5);
        m_reader.skipBits(32);  // general_profile_compatibility_flag
        int progressive_source_flag = m_reader.getBit();
        interlaced_source_flag = m_reader.getBit();
        int non_packed_constraint_flag = m_reader.getBit();
        int frame_only_constraint_flag = m_reader.getBit();
        m_reader.skipBits(32);  // reserved_zero_44bits
        m_reader.skipBits(12);  // reserved_zero_44bits
        level_idc = m_reader.getBits(8);

        for (int i = 0; i < subLayers - 1; i++)
        {
            sub_layer_profile_present_flag[i] = m_reader.getBit();
            sub_layer_level_present_flag[i] = m_reader.getBit();
        }
        if (subLayers > 1)
        {
            for (int i = subLayers - 1; i < 8; i++) m_reader.skipBits(2);  // reserved_zero_2bits
        }

        for (int i = 0; i < subLayers - 1; i++)
        {
            if (sub_layer_profile_present_flag[i])
            {
                sub_layer_profile_space[i] = m_reader.getBits(2);
                bool sub_layer_tier_flag = m_reader.getBit();
                int sub_layer_profile_idc = m_reader.getBits(5);
                for (int j = 0; j < 32; j++) m_reader.skipBit();  // sub_layer_profile_compatibility_flag[ i ][ j ] u(1)
                m_reader.skipBit();                               // sub_layer_progressive_source_flag[ i ] u(1)
                m_reader.skipBit();                               // sub_layer_interlaced_source_flag[ i ] u(1)
                m_reader.skipBit();                               // sub_layer_non_packed_constraint_flag[ i ] u(1)
                m_reader.skipBit();                               // sub_layer_frame_only_constraint_flag[ i ] u(1)
                m_reader.skipBits(32);                            // sub_layer_reserved_zero_44bits
                m_reader.skipBits(12);                            // sub_layer_reserved_zero_44bits
            }
            if (sub_layer_level_present_flag[i])
                m_reader.skipBits(8);  // sub_layer_level_idc[ i ]
        }
        return 0;
    }
    catch (BitStreamException& e)
    {
        return NOT_ENOUGH_BUFFER;
    }
}


bool VvcHrdUnit::sublayer_hrd_parameters(int subLayerId)
{
    for (int j = 0; j <= hrd_cpb_cnt_minus1; j++)
    {
        unsigned bit_rate_value_minus1 = extractUEGolombCode();
        if (bit_rate_value_minus1 == 0xffffffff)
            return 1;
        unsigned cpb_size_value_minus1 = extractUEGolombCode();
        if (cpb_size_value_minus1 == 0xffffffff)
            return 1;
        if (general_du_hrd_params_present_flag)
        {
            unsigned cpb_size_du_value_minus1 = extractUEGolombCode();
            if (cpb_size_du_value_minus1 == 0xffffffff)
                return 1;
            unsigned bit_rate_du_value_minus1 = extractUEGolombCode();
            if (bit_rate_du_value_minus1 == 0xffffffff)
                return 1;
        }
        m_reader.skipBit();  // cbr_flag
    }
    return 0;
}


int HevcSpsUnit::hrd_parameters(bool commonInfPresentFlag, int maxNumSubLayersMinus1)
{
    if (commonInfPresentFlag)
    {
        nal_hrd_parameters_present_flag = m_reader.getBit();
        vcl_hrd_parameters_present_flag = m_reader.getBit();
        if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag)
        {
            sub_pic_hrd_params_present_flag = m_reader.getBit();
            if (sub_pic_hrd_params_present_flag)
            {
                m_reader.skipBits(8);  // tick_divisor_minus2 u(8)
                m_reader.skipBits(5);  // du_cpb_removal_delay_increment_length_minus1 u(5)
                m_reader.skipBit();    // sub_pic_cpb_params_in_pic_timing_sei_flag u(1)
                m_reader.skipBits(5);  // dpb_output_delay_du_length_minus1 u(5)
            }
            m_reader.skipBits(4);  // bit_rate_scale u(4)
            m_reader.skipBits(4);  // cpb_size_scale u(4)
            if (sub_pic_hrd_params_present_flag)
                m_reader.skipBits(4);  // cpb_size_du_scale u(4)
            m_reader.skipBits(5);      // initial_cpb_removal_delay_length_minus1 u(5)
            m_reader.skipBits(5);      // au_cpb_removal_delay_length_minus1 u(5)
            m_reader.skipBits(5);      // dpb_output_delay_length_minus1 u(5)
        }
    }

    cpb_cnt_minus1.resize(maxNumSubLayersMinus1 + 1);
    for (int i = 0; i <= maxNumSubLayersMinus1; i++)
    {
        bool low_delay_hrd_flag = false;
        bool fixed_pic_rate_general_flag = m_reader.getBit();
        bool fixed_pic_rate_within_cvs_flag = true;
        if (!fixed_pic_rate_general_flag)
            fixed_pic_rate_within_cvs_flag = m_reader.getBit();
        if (fixed_pic_rate_within_cvs_flag)
        {
            unsigned elemental_duration_in_tc_minus1 = extractUEGolombCode();
            if (elemental_duration_in_tc_minus1 > 2047)
                return 1;
        }
        else
            low_delay_hrd_flag = m_reader.getBit();
        if (!low_delay_hrd_flag)
        {
            cpb_cnt_minus1[i] = extractUEGolombCode();
            if (cpb_cnt_minus1[i] > 32)
                return 1;
        }
        if (nal_hrd_parameters_present_flag)
            if (sub_layer_hrd_parameters(i) != 0)
                return 1;
        if (vcl_hrd_parameters_present_flag)
            if (sub_layer_hrd_parameters(i) != 0)
                return 1;
    }
    return 0;
}


double VvcVpsUnit::getFPS() const { return num_units_in_tick ? time_scale / (float)num_units_in_tick : 0; }


HevcHdrUnit::HevcHdrUnit() : isHDR10(false), isHDR10plus(false), isDVRPU(false), isDVEL(false) {}


string VvcSpsUnit::getDescription() const
{
    string result = getProfileString();
    result += string(" Resolution: ") + int32ToStr(pic_width_max_in_luma_samples) + string(":") +
              int32ToStr(pic_height_max_in_luma_samples) + string("p");

    double fps = getFPS();
    result += "  Frame rate: ";
    result += (fps ? doubleToStr(fps) : string("not found"));
    return result;
}


int HevcUnit::extractSEGolombCode()
{
    unsigned rez = extractUEGolombCode();
    if (rez % 2 == 0)
        return -(int)(rez / 2);
    else
        return (int)((rez + 1) / 2);
}


HevcSpsUnit::HevcSpsUnit()
    : HevcUnitWithProfile(),
      vps_id(0),
      max_sub_layers(0),
      sps_id(0),
      chromaFormat(0),
      separate_colour_plane_flag(false),
      pic_width_in_luma_samples(0),
      pic_height_in_luma_samples(0),
      bit_depth_luma_minus8(0),
      bit_depth_chroma_minus8(0),
      log2_max_pic_order_cnt_lsb(0),
      nal_hrd_parameters_present_flag(false),
      vcl_hrd_parameters_present_flag(false),
      sub_pic_hrd_params_present_flag(false),
      num_short_term_ref_pic_sets(0),
      video_full_range_flag(0),
      colour_primaries(2),
      transfer_characteristics(2),
      matrix_coeffs(2),
      chroma_sample_loc_type_top_field(0),
      chroma_sample_loc_type_bottom_field(0),
      num_units_in_tick(0),
      time_scale(0),
      PicSizeInCtbsY_bits(0)
{
}


static HashTable *spl_array_get_gc(zval *object, zval ***gc_data, int *gc_data_count TSRMLS_DC) /* {{{ */
{
	spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);

	*gc_data = &intern->array;
	*gc_data_count = 1;
	return zend_std_get_properties(object TSRMLS_CC);
}


base_context::base_context()
{
  set_acceleration_functions(de265_acceleration_AUTO);
}


void slice_unit::allocate_thread_contexts(int n)
{
  assert(thread_contexts==NULL);

  thread_contexts = new thread_context[n];
  nThreadContexts = n;
}


decoder_context::decoder_context()
{
  //memset(ctx, 0, sizeof(decoder_context));

  // --- parameters ---

  param_sei_check_hash = false;
  param_conceal_stream_errors = true;
  param_suppress_faulty_pictures = false;

  param_disable_deblocking = false;
  param_disable_sao = false;
  //param_disable_mc_residual_idct = false;
  //param_disable_intra_residual_idct = false;

  // --- processing ---

  param_sps_headers_fd = -1;
  param_vps_headers_fd = -1;
  param_pps_headers_fd = -1;
  param_slice_headers_fd = -1;

  param_image_allocation_functions = de265_image::default_image_allocation;
  param_image_allocation_userdata  = NULL;

  /*
  memset(&vps, 0, sizeof(video_parameter_set)*DE265_MAX_VPS_SETS);
  memset(&sps, 0, sizeof(seq_parameter_set)  *DE265_MAX_SPS_SETS);
  memset(&pps, 0, sizeof(pic_parameter_set)  *DE265_MAX_PPS_SETS);
  memset(&slice,0,sizeof(slice_segment_header)*DE265_MAX_SLICES);
  */

  current_vps = NULL;
  current_sps = NULL;
  current_pps = NULL;

  //memset(&thread_pool,0,sizeof(struct thread_pool));
  num_worker_threads = 0;

  // frame-rate

  limit_HighestTid = 6;   // decode all temporal layers (up to layer 6)
  framerate_ratio = 100;  // decode all 100%

  goal_HighestTid = 6;
  current_HighestTid = 6;
  layer_framerate_ratio = 100;

  compute_framedrop_table();

  //

  current_image_poc_lsb = 0;
  first_decoded_picture = 0;
  NoRaslOutputFlag = 0;
  HandleCraAsBlaFlag = 0;
  FirstAfterEndOfSequenceNAL = 0;
  PicOrderCntMsb = 0;
  prevPicOrderCntLsb = 0;
  prevPicOrderCntMsb = 0;
  img = NULL;

  /*
  int PocLsbLt[MAX_NUM_REF_PICS];
  int UsedByCurrPicLt[MAX_NUM_REF_PICS];
  int DeltaPocMsbCycleLt[MAX_NUM_REF_PICS];
  int CurrDeltaPocMsbPresentFlag[MAX_NUM_REF_PICS];
  int FollDeltaPocMsbPresentFlag[MAX_NUM_REF_PICS];

  int NumPocStCurrBefore;
  int NumPocStCurrAfter;
  int NumPocStFoll;
  int NumPocLtCurr;
  int NumPocLtFoll;

  // These lists contain absolute POC values.
  int PocStCurrBefore[MAX_NUM_REF_PICS]; // used for reference in current picture, smaller POC
  int PocStCurrAfter[MAX_NUM_REF_PICS];  // used for reference in current picture, larger POC
  int PocStFoll[MAX_NUM_REF_PICS]; // not used for reference in current picture, but in future picture
  int PocLtCurr[MAX_NUM_REF_PICS]; // used in current picture
  int PocLtFoll[MAX_NUM_REF_PICS]; // used in some future picture

  // These lists contain indices into the DPB.
  int RefPicSetStCurrBefore[DE265_DPB_SIZE];
  int RefPicSetStCurrAfter[DE265_DPB_SIZE];
  int RefPicSetStFoll[DE265_DPB_SIZE];
  int RefPicSetLtCurr[DE265_DPB_SIZE];
  int RefPicSetLtFoll[DE265_DPB_SIZE];

  uint8_t nal_unit_type;

  char IdrPicFlag;
  char RapPicFlag;
  */

  // --- internal data ---

  first_decoded_picture = true;
  //ctx->FirstAfterEndOfSequenceNAL = true;
  //ctx->last_RAP_picture_NAL_type = NAL_UNIT_UNDEFINED;

  //de265_init_image(&ctx->coeff);

  // --- decoded picture buffer ---

  current_image_poc_lsb = -1; // any invalid number
}


static void ff_unlock_avcodec(const AVCodec *codec)
{
    if (!(codec->caps_internal & FF_CODEC_CAP_INIT_THREADSAFE) && codec->init)
        ff_mutex_unlock(&codec_mutex);
}


Status ConvBackpropExtractAndVerifyDimension(
    StringPiece label, const TensorShape& input_shape,
    const TensorShape& filter_shape, const TensorShape& output_shape,
    const gtl::ArraySlice<int32> dilations, const std::vector<int32>& strides,
    Padding padding, int64 padding_before, int64 padding_after, int spatial_dim,
    int filter_spatial_dim, ConvBackpropSpatialDimension* dim) {
  dim->input_size = input_shape.dim_size(spatial_dim);
  dim->filter_size = filter_shape.dim_size(filter_spatial_dim);
  dim->output_size = output_shape.dim_size(spatial_dim);
  dim->stride = strides[spatial_dim];
  dim->dilation = dilations[spatial_dim];
  int64 out_size = 0;
  TF_RETURN_IF_ERROR(GetWindowedOutputSizeVerboseV2(
      dim->input_size, dim->filter_size, dim->dilation, dim->stride, padding,
      &out_size, &padding_before, &padding_after));
  if (dim->output_size != out_size) {
    return errors::InvalidArgument(
        label, ": Size of out_backprop doesn't match computed: ", "actual = ",
        dim->output_size, ", computed = ", out_size,
        " spatial_dim: ", spatial_dim, " input: ", dim->input_size,
        " filter: ", dim->filter_size, " output: ", dim->output_size,
        " stride: ", dim->stride, " dilation: ", dim->dilation);
  }

  int64 effective_filter_size = (dim->filter_size - 1) * dim->dilation + 1;
  dim->expanded_output_size = (dim->output_size - 1) * dim->stride + 1;
  const auto padded_out_size = dim->input_size + effective_filter_size - 1;
  dim->pad_before = effective_filter_size - 1 - padding_before;
  dim->pad_after =
      padded_out_size - dim->expanded_output_size - dim->pad_before;
  VLOG(2) << label << ": expanded_out = " << dim->expanded_output_size
          << ", effective_filter_size = " << effective_filter_size
          << ", padded_out = " << padded_out_size
          << ", pad_before = " << dim->pad_before
          << ", pad_after = " << dim->pad_after
          << ", dilation = " << dim->dilation << ", strides = " << dim->stride;
  return Status::OK();
}