github.com/prebid/prebid-server@v0.275.0/exchange/utils.go (about)

     1  package exchange
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"math/rand"
    10  
    11  	"github.com/buger/jsonparser"
    12  	"github.com/prebid/go-gdpr/vendorconsent"
    13  	gpplib "github.com/prebid/go-gpp"
    14  	gppConstants "github.com/prebid/go-gpp/constants"
    15  	"github.com/prebid/openrtb/v19/openrtb2"
    16  
    17  	"github.com/prebid/prebid-server/config"
    18  	"github.com/prebid/prebid-server/errortypes"
    19  	"github.com/prebid/prebid-server/firstpartydata"
    20  	"github.com/prebid/prebid-server/gdpr"
    21  	"github.com/prebid/prebid-server/metrics"
    22  	"github.com/prebid/prebid-server/openrtb_ext"
    23  	"github.com/prebid/prebid-server/privacy"
    24  	"github.com/prebid/prebid-server/privacy/ccpa"
    25  	"github.com/prebid/prebid-server/privacy/lmt"
    26  	"github.com/prebid/prebid-server/schain"
    27  	"github.com/prebid/prebid-server/stored_responses"
    28  	"github.com/prebid/prebid-server/util/ptrutil"
    29  )
    30  
    31  var channelTypeMap = map[metrics.RequestType]config.ChannelType{
    32  	metrics.ReqTypeAMP:      config.ChannelAMP,
    33  	metrics.ReqTypeORTB2App: config.ChannelApp,
    34  	metrics.ReqTypeVideo:    config.ChannelVideo,
    35  	metrics.ReqTypeORTB2Web: config.ChannelWeb,
    36  }
    37  
    38  const unknownBidder string = ""
    39  
    40  type requestSplitter struct {
    41  	bidderToSyncerKey map[string]string
    42  	me                metrics.MetricsEngine
    43  	privacyConfig     config.Privacy
    44  	gdprPermsBuilder  gdpr.PermissionsBuilder
    45  	hostSChainNode    *openrtb2.SupplyChainNode
    46  	bidderInfo        config.BidderInfos
    47  }
    48  
    49  // cleanOpenRTBRequests splits the input request into requests which are sanitized for each bidder. Intended behavior is:
    50  //
    51  //  1. BidRequest.Imp[].Ext will only contain the "prebid" field and a "bidder" field which has the params for the intended Bidder.
    52  //  2. Every BidRequest.Imp[] requested Bids from the Bidder who keys it.
    53  //  3. BidRequest.User.BuyerUID will be set to that Bidder's ID.
    54  func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
    55  	auctionReq AuctionRequest,
    56  	requestExt *openrtb_ext.ExtRequest,
    57  	gdprDefaultValue gdpr.Signal, bidAdjustmentFactors map[string]float64,
    58  ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
    59  	req := auctionReq.BidRequestWrapper
    60  	aliases, errs := parseAliases(req.BidRequest)
    61  	if len(errs) > 0 {
    62  		return
    63  	}
    64  
    65  	allowedBidderRequests = make([]BidderRequest, 0)
    66  
    67  	bidderImpWithBidResp := stored_responses.InitStoredBidResponses(req.BidRequest, auctionReq.StoredBidResponses)
    68  
    69  	impsByBidder, err := splitImps(req.BidRequest.Imp)
    70  	if err != nil {
    71  		errs = []error{err}
    72  		return
    73  	}
    74  
    75  	aliasesGVLIDs, errs := parseAliasesGVLIDs(req.BidRequest)
    76  	if len(errs) > 0 {
    77  		return
    78  	}
    79  
    80  	var allBidderRequests []BidderRequest
    81  	allBidderRequests, errs = getAuctionBidderRequests(auctionReq, requestExt, rs.bidderToSyncerKey, impsByBidder, aliases, rs.hostSChainNode)
    82  
    83  	bidderNameToBidderReq := buildBidResponseRequest(req.BidRequest, bidderImpWithBidResp, aliases, auctionReq.BidderImpReplaceImpID)
    84  	//this function should be executed after getAuctionBidderRequests
    85  	allBidderRequests = mergeBidderRequests(allBidderRequests, bidderNameToBidderReq)
    86  
    87  	var gpp gpplib.GppContainer
    88  	if req.BidRequest.Regs != nil && len(req.BidRequest.Regs.GPP) > 0 {
    89  		gpp, err = gpplib.Parse(req.BidRequest.Regs.GPP)
    90  		if err != nil {
    91  			errs = append(errs, err)
    92  		}
    93  	}
    94  
    95  	if auctionReq.Account.PriceFloors.IsAdjustForBidAdjustmentEnabled() {
    96  		//Apply BidAdjustmentFactor to imp.BidFloor
    97  		applyBidAdjustmentToFloor(allBidderRequests, bidAdjustmentFactors)
    98  	}
    99  
   100  	gdprSignal, err := getGDPR(req)
   101  	if err != nil {
   102  		errs = append(errs, err)
   103  	}
   104  
   105  	consent, err := getConsent(req, gpp)
   106  	if err != nil {
   107  		errs = append(errs, err)
   108  	}
   109  	gdprApplies := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && gdprDefaultValue == gdpr.SignalYes)
   110  
   111  	ccpaEnforcer, err := extractCCPA(req.BidRequest, rs.privacyConfig, &auctionReq.Account, aliases, channelTypeMap[auctionReq.LegacyLabels.RType], gpp)
   112  	if err != nil {
   113  		errs = append(errs, err)
   114  	}
   115  
   116  	lmtEnforcer := extractLMT(req.BidRequest, rs.privacyConfig)
   117  
   118  	// request level privacy policies
   119  	coppa := req.BidRequest.Regs != nil && req.BidRequest.Regs.COPPA == 1
   120  	lmt := lmtEnforcer.ShouldEnforce(unknownBidder)
   121  
   122  	privacyLabels.CCPAProvided = ccpaEnforcer.CanEnforce()
   123  	privacyLabels.CCPAEnforced = ccpaEnforcer.ShouldEnforce(unknownBidder)
   124  	privacyLabels.COPPAEnforced = coppa
   125  	privacyLabels.LMTEnforced = lmt
   126  
   127  	var gdprEnforced bool
   128  	var gdprPerms gdpr.Permissions = &gdpr.AlwaysAllow{}
   129  
   130  	if gdprApplies {
   131  		gdprEnforced = auctionReq.TCF2Config.ChannelEnabled(channelTypeMap[auctionReq.LegacyLabels.RType])
   132  	}
   133  
   134  	if gdprEnforced {
   135  		privacyLabels.GDPREnforced = true
   136  		parsedConsent, err := vendorconsent.ParseString(consent)
   137  		if err == nil {
   138  			version := int(parsedConsent.Version())
   139  			privacyLabels.GDPRTCFVersion = metrics.TCFVersionToValue(version)
   140  		}
   141  
   142  		gdprRequestInfo := gdpr.RequestInfo{
   143  			AliasGVLIDs: aliasesGVLIDs,
   144  			Consent:     consent,
   145  			GDPRSignal:  gdprSignal,
   146  			PublisherID: auctionReq.LegacyLabels.PubID,
   147  		}
   148  		gdprPerms = rs.gdprPermsBuilder(auctionReq.TCF2Config, gdprRequestInfo)
   149  	}
   150  
   151  	// bidder level privacy policies
   152  	for _, bidderRequest := range allBidderRequests {
   153  		privacyEnforcement := privacy.Enforcement{
   154  			COPPA: coppa,
   155  			LMT:   lmt,
   156  		}
   157  
   158  		// fetchBids activity
   159  		scopedName := privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderRequest.BidderName.String()}
   160  		fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName, privacy.NewRequestFromBidRequest(*req))
   161  		if !fetchBidsActivityAllowed {
   162  			// skip the call to a bidder if fetchBids activity is not allowed
   163  			// do not add this bidder to allowedBidderRequests
   164  			continue
   165  		}
   166  
   167  		var auctionPermissions gdpr.AuctionPermissions
   168  		var gdprErr error
   169  
   170  		if gdprEnforced {
   171  			auctionPermissions, gdprErr = gdprPerms.AuctionActivitiesAllowed(ctx, bidderRequest.BidderCoreName, bidderRequest.BidderName)
   172  			if !auctionPermissions.AllowBidRequest {
   173  				// auction request is not permitted by GDPR
   174  				// do not add this bidder to allowedBidderRequests
   175  				rs.me.RecordAdapterGDPRRequestBlocked(bidderRequest.BidderCoreName)
   176  				continue
   177  			}
   178  		}
   179  
   180  		passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName, privacy.NewRequestFromBidRequest(*req))
   181  		if !passIDActivityAllowed {
   182  			privacyEnforcement.UFPD = true
   183  		} else {
   184  			// run existing policies (GDPR, CCPA, COPPA, LMT)
   185  			// potentially block passing IDs based on GDPR
   186  			if gdprEnforced {
   187  				if gdprErr == nil {
   188  					privacyEnforcement.GDPRID = !auctionPermissions.PassID
   189  				} else {
   190  					privacyEnforcement.GDPRID = true
   191  				}
   192  			}
   193  			// potentially block passing IDs based on CCPA
   194  			privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
   195  		}
   196  
   197  		passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName, privacy.NewRequestFromBidRequest(*req))
   198  		if !passGeoActivityAllowed {
   199  			privacyEnforcement.PreciseGeo = true
   200  		} else {
   201  			// run existing policies (GDPR, CCPA, COPPA, LMT)
   202  			// potentially block passing geo based on GDPR
   203  			if gdprEnforced {
   204  				if gdprErr == nil {
   205  					privacyEnforcement.GDPRGeo = !auctionPermissions.PassGeo
   206  				} else {
   207  					privacyEnforcement.GDPRGeo = true
   208  				}
   209  			}
   210  			// potentially block passing geo based on CCPA
   211  			privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
   212  
   213  		}
   214  
   215  		if auctionReq.FirstPartyData != nil && auctionReq.FirstPartyData[bidderRequest.BidderName] != nil {
   216  			applyFPD(auctionReq.FirstPartyData[bidderRequest.BidderName], bidderRequest.BidRequest)
   217  		}
   218  
   219  		privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTIDs, scopedName, privacy.NewRequestFromBidRequest(*req))
   220  
   221  		privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy)
   222  		allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
   223  
   224  		// GPP downgrade: always downgrade unless we can confirm GPP is supported
   225  		if shouldSetLegacyPrivacy(rs.bidderInfo, string(bidderRequest.BidderCoreName)) {
   226  			setLegacyGDPRFromGPP(bidderRequest.BidRequest, gpp)
   227  			setLegacyUSPFromGPP(bidderRequest.BidRequest, gpp)
   228  		}
   229  	}
   230  
   231  	return
   232  }
   233  
   234  func shouldSetLegacyPrivacy(bidderInfo config.BidderInfos, bidder string) bool {
   235  	binfo, defined := bidderInfo[bidder]
   236  
   237  	if !defined || binfo.OpenRTB == nil {
   238  		return true
   239  	}
   240  
   241  	return !binfo.OpenRTB.GPPSupported
   242  }
   243  
   244  func ccpaEnabled(account *config.Account, privacyConfig config.Privacy, requestType config.ChannelType) bool {
   245  	if accountEnabled := account.CCPA.EnabledForChannelType(requestType); accountEnabled != nil {
   246  		return *accountEnabled
   247  	}
   248  	return privacyConfig.CCPA.Enforce
   249  }
   250  
   251  func extractCCPA(orig *openrtb2.BidRequest, privacyConfig config.Privacy, account *config.Account, aliases map[string]string, requestType config.ChannelType, gpp gpplib.GppContainer) (privacy.PolicyEnforcer, error) {
   252  	// Quick extra wrapper until RequestWrapper makes its way into CleanRequests
   253  	ccpaPolicy, err := ccpa.ReadFromRequestWrapper(&openrtb_ext.RequestWrapper{BidRequest: orig}, gpp)
   254  	if err != nil {
   255  		return privacy.NilPolicyEnforcer{}, err
   256  	}
   257  
   258  	validBidders := GetValidBidders(aliases)
   259  	ccpaParsedPolicy, err := ccpaPolicy.Parse(validBidders)
   260  	if err != nil {
   261  		return privacy.NilPolicyEnforcer{}, err
   262  	}
   263  
   264  	ccpaEnforcer := privacy.EnabledPolicyEnforcer{
   265  		Enabled:        ccpaEnabled(account, privacyConfig, requestType),
   266  		PolicyEnforcer: ccpaParsedPolicy,
   267  	}
   268  	return ccpaEnforcer, nil
   269  }
   270  
   271  func extractLMT(orig *openrtb2.BidRequest, privacyConfig config.Privacy) privacy.PolicyEnforcer {
   272  	return privacy.EnabledPolicyEnforcer{
   273  		Enabled:        privacyConfig.LMT.Enforce,
   274  		PolicyEnforcer: lmt.ReadFromRequest(orig),
   275  	}
   276  }
   277  
   278  func ExtractReqExtBidderParamsMap(bidRequest *openrtb2.BidRequest) (map[string]json.RawMessage, error) {
   279  	if bidRequest == nil {
   280  		return nil, errors.New("error bidRequest should not be nil")
   281  	}
   282  
   283  	reqExt := &openrtb_ext.ExtRequest{}
   284  	if len(bidRequest.Ext) > 0 {
   285  		err := json.Unmarshal(bidRequest.Ext, &reqExt)
   286  		if err != nil {
   287  			return nil, fmt.Errorf("error decoding Request.ext : %s", err.Error())
   288  		}
   289  	}
   290  
   291  	if reqExt.Prebid.BidderParams == nil {
   292  		return nil, nil
   293  	}
   294  
   295  	var bidderParams map[string]json.RawMessage
   296  	err := json.Unmarshal(reqExt.Prebid.BidderParams, &bidderParams)
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  
   301  	return bidderParams, nil
   302  }
   303  
   304  func getAuctionBidderRequests(auctionRequest AuctionRequest,
   305  	requestExt *openrtb_ext.ExtRequest,
   306  	bidderToSyncerKey map[string]string,
   307  	impsByBidder map[string][]openrtb2.Imp,
   308  	aliases map[string]string,
   309  	hostSChainNode *openrtb2.SupplyChainNode) ([]BidderRequest, []error) {
   310  
   311  	bidderRequests := make([]BidderRequest, 0, len(impsByBidder))
   312  	req := auctionRequest.BidRequestWrapper
   313  	explicitBuyerUIDs, err := extractBuyerUIDs(req.BidRequest.User)
   314  	if err != nil {
   315  		return nil, []error{err}
   316  	}
   317  
   318  	bidderParamsInReqExt, err := ExtractReqExtBidderParamsMap(req.BidRequest)
   319  	if err != nil {
   320  		return nil, []error{err}
   321  	}
   322  
   323  	sChainWriter, err := schain.NewSChainWriter(requestExt, hostSChainNode)
   324  	if err != nil {
   325  		return nil, []error{err}
   326  	}
   327  
   328  	var errs []error
   329  	for bidder, imps := range impsByBidder {
   330  		coreBidder := resolveBidder(bidder, aliases)
   331  
   332  		reqCopy := *req.BidRequest
   333  		reqCopy.Imp = imps
   334  
   335  		sChainWriter.Write(&reqCopy, bidder)
   336  
   337  		reqCopy.Ext, err = buildRequestExtForBidder(bidder, req.BidRequest.Ext, requestExt, bidderParamsInReqExt, auctionRequest.Account.AlternateBidderCodes)
   338  		if err != nil {
   339  			return nil, []error{err}
   340  		}
   341  
   342  		if err := removeUnpermissionedEids(&reqCopy, bidder, requestExt); err != nil {
   343  			errs = append(errs, fmt.Errorf("unable to enforce request.ext.prebid.data.eidpermissions because %v", err))
   344  			continue
   345  		}
   346  
   347  		bidderRequest := BidderRequest{
   348  			BidderName:     openrtb_ext.BidderName(bidder),
   349  			BidderCoreName: coreBidder,
   350  			BidRequest:     &reqCopy,
   351  			BidderLabels: metrics.AdapterLabels{
   352  				Source:      auctionRequest.LegacyLabels.Source,
   353  				RType:       auctionRequest.LegacyLabels.RType,
   354  				Adapter:     coreBidder,
   355  				PubID:       auctionRequest.LegacyLabels.PubID,
   356  				CookieFlag:  auctionRequest.LegacyLabels.CookieFlag,
   357  				AdapterBids: metrics.AdapterBidPresent,
   358  			},
   359  		}
   360  
   361  		syncerKey := bidderToSyncerKey[string(coreBidder)]
   362  		if hadSync := prepareUser(&reqCopy, bidder, syncerKey, explicitBuyerUIDs, auctionRequest.UserSyncs); !hadSync && req.BidRequest.App == nil {
   363  			bidderRequest.BidderLabels.CookieFlag = metrics.CookieFlagNo
   364  		} else {
   365  			bidderRequest.BidderLabels.CookieFlag = metrics.CookieFlagYes
   366  		}
   367  
   368  		bidderRequests = append(bidderRequests, bidderRequest)
   369  	}
   370  	return bidderRequests, errs
   371  }
   372  
   373  func buildRequestExtForBidder(bidder string, requestExt json.RawMessage, requestExtParsed *openrtb_ext.ExtRequest, bidderParamsInReqExt map[string]json.RawMessage, cfgABC *openrtb_ext.ExtAlternateBidderCodes) (json.RawMessage, error) {
   374  	// Resolve alternatebiddercode for current bidder
   375  	var reqABC *openrtb_ext.ExtAlternateBidderCodes
   376  	if len(requestExt) != 0 && requestExtParsed != nil && requestExtParsed.Prebid.AlternateBidderCodes != nil {
   377  		reqABC = requestExtParsed.Prebid.AlternateBidderCodes
   378  	}
   379  	alternateBidderCodes := buildRequestExtAlternateBidderCodes(bidder, cfgABC, reqABC)
   380  
   381  	if (len(requestExt) == 0 || requestExtParsed == nil) && alternateBidderCodes == nil {
   382  		return nil, nil
   383  	}
   384  
   385  	// Resolve Bidder Params
   386  	var bidderParams json.RawMessage
   387  	if bidderParamsInReqExt != nil {
   388  		bidderParams = bidderParamsInReqExt[bidder]
   389  	}
   390  
   391  	// Copy Allowed Fields
   392  	// Per: https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#prebid-server-ortb2-extension-summary
   393  	prebid := openrtb_ext.ExtRequestPrebid{
   394  		BidderParams:         bidderParams,
   395  		AlternateBidderCodes: alternateBidderCodes,
   396  	}
   397  
   398  	if requestExtParsed != nil {
   399  		prebid.Channel = requestExtParsed.Prebid.Channel
   400  		prebid.CurrencyConversions = requestExtParsed.Prebid.CurrencyConversions
   401  		prebid.Debug = requestExtParsed.Prebid.Debug
   402  		prebid.Integration = requestExtParsed.Prebid.Integration
   403  		prebid.MultiBid = buildRequestExtMultiBid(bidder, requestExtParsed.Prebid.MultiBid, alternateBidderCodes)
   404  		prebid.Sdk = requestExtParsed.Prebid.Sdk
   405  		prebid.Server = requestExtParsed.Prebid.Server
   406  	}
   407  
   408  	// Marshal New Prebid Object
   409  	prebidJson, err := json.Marshal(prebid)
   410  	if err != nil {
   411  		return nil, err
   412  	}
   413  
   414  	// Parse Existing Ext
   415  	extMap := make(map[string]json.RawMessage)
   416  	if len(requestExt) != 0 {
   417  		if err := json.Unmarshal(requestExt, &extMap); err != nil {
   418  			return nil, err
   419  		}
   420  	}
   421  
   422  	// Update Ext With Prebid Json
   423  	if bytes.Equal(prebidJson, []byte(`{}`)) {
   424  		delete(extMap, "prebid")
   425  	} else {
   426  		extMap["prebid"] = prebidJson
   427  	}
   428  
   429  	if len(extMap) > 0 {
   430  		return json.Marshal(extMap)
   431  	} else {
   432  		return nil, nil
   433  	}
   434  }
   435  
   436  func buildRequestExtAlternateBidderCodes(bidder string, accABC *openrtb_ext.ExtAlternateBidderCodes, reqABC *openrtb_ext.ExtAlternateBidderCodes) *openrtb_ext.ExtAlternateBidderCodes {
   437  	if reqABC != nil {
   438  		alternateBidderCodes := &openrtb_ext.ExtAlternateBidderCodes{
   439  			Enabled: reqABC.Enabled,
   440  		}
   441  		if bidderCodes, ok := reqABC.Bidders[bidder]; ok {
   442  			alternateBidderCodes.Bidders = map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
   443  				bidder: bidderCodes,
   444  			}
   445  		}
   446  		return alternateBidderCodes
   447  	}
   448  
   449  	if accABC != nil {
   450  		alternateBidderCodes := &openrtb_ext.ExtAlternateBidderCodes{
   451  			Enabled: accABC.Enabled,
   452  		}
   453  		if bidderCodes, ok := accABC.Bidders[bidder]; ok {
   454  			alternateBidderCodes.Bidders = map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
   455  				bidder: bidderCodes,
   456  			}
   457  		}
   458  		return alternateBidderCodes
   459  	}
   460  
   461  	return nil
   462  }
   463  
   464  func buildRequestExtMultiBid(adapter string, reqMultiBid []*openrtb_ext.ExtMultiBid, adapterABC *openrtb_ext.ExtAlternateBidderCodes) []*openrtb_ext.ExtMultiBid {
   465  	adapterMultiBid := make([]*openrtb_ext.ExtMultiBid, 0)
   466  	for _, multiBid := range reqMultiBid {
   467  		if multiBid.Bidder != "" {
   468  			if multiBid.Bidder == adapter || isBidderInExtAlternateBidderCodes(adapter, multiBid.Bidder, adapterABC) {
   469  				adapterMultiBid = append(adapterMultiBid, multiBid)
   470  			}
   471  		} else {
   472  			for _, bidder := range multiBid.Bidders {
   473  				if bidder == adapter || isBidderInExtAlternateBidderCodes(adapter, bidder, adapterABC) {
   474  					adapterMultiBid = append(adapterMultiBid, &openrtb_ext.ExtMultiBid{
   475  						Bidders: []string{bidder},
   476  						MaxBids: multiBid.MaxBids,
   477  					})
   478  				}
   479  			}
   480  		}
   481  	}
   482  
   483  	if len(adapterMultiBid) > 0 {
   484  		return adapterMultiBid
   485  	}
   486  
   487  	return nil
   488  }
   489  
   490  func isBidderInExtAlternateBidderCodes(adapter, currentMultiBidBidder string, adapterABC *openrtb_ext.ExtAlternateBidderCodes) bool {
   491  	if adapterABC != nil {
   492  		if abc, ok := adapterABC.Bidders[adapter]; ok {
   493  			for _, bidder := range abc.AllowedBidderCodes {
   494  				if bidder == "*" || bidder == currentMultiBidBidder {
   495  					return true
   496  				}
   497  			}
   498  		}
   499  	}
   500  	return false
   501  }
   502  
   503  // extractBuyerUIDs parses the values from user.ext.prebid.buyeruids, and then deletes those values from the ext.
   504  // This prevents a Bidder from using these values to figure out who else is involved in the Auction.
   505  func extractBuyerUIDs(user *openrtb2.User) (map[string]string, error) {
   506  	if user == nil {
   507  		return nil, nil
   508  	}
   509  	if len(user.Ext) == 0 {
   510  		return nil, nil
   511  	}
   512  
   513  	var userExt openrtb_ext.ExtUser
   514  	if err := json.Unmarshal(user.Ext, &userExt); err != nil {
   515  		return nil, err
   516  	}
   517  	if userExt.Prebid == nil {
   518  		return nil, nil
   519  	}
   520  
   521  	// The API guarantees that user.ext.prebid.buyeruids exists and has at least one ID defined,
   522  	// as long as user.ext.prebid exists.
   523  	buyerUIDs := userExt.Prebid.BuyerUIDs
   524  	userExt.Prebid = nil
   525  
   526  	// Remarshal (instead of removing) if the ext has other known fields
   527  	if userExt.Consent != "" || len(userExt.Eids) > 0 {
   528  		if newUserExtBytes, err := json.Marshal(userExt); err != nil {
   529  			return nil, err
   530  		} else {
   531  			user.Ext = newUserExtBytes
   532  		}
   533  	} else {
   534  		user.Ext = nil
   535  	}
   536  	return buyerUIDs, nil
   537  }
   538  
   539  // splitImps takes a list of Imps and returns a map of imps which have been sanitized for each bidder.
   540  //
   541  // For example, suppose imps has two elements. One goes to rubicon, while the other goes to appnexus and index.
   542  // The returned map will have three keys: rubicon, appnexus, and index--each with one Imp.
   543  // The "imp.ext" value of the appnexus Imp will only contain the "prebid" values, and "appnexus" value at the "bidder" key.
   544  // The "imp.ext" value of the rubicon Imp will only contain the "prebid" values, and "rubicon" value at the "bidder" key.
   545  //
   546  // The goal here is so that Bidders only get Imps and Imp.Ext values which are intended for them.
   547  func splitImps(imps []openrtb2.Imp) (map[string][]openrtb2.Imp, error) {
   548  	bidderImps := make(map[string][]openrtb2.Imp)
   549  
   550  	for i, imp := range imps {
   551  		var impExt map[string]json.RawMessage
   552  		if err := json.Unmarshal(imp.Ext, &impExt); err != nil {
   553  			return nil, fmt.Errorf("invalid json for imp[%d]: %v", i, err)
   554  		}
   555  
   556  		var impExtPrebid map[string]json.RawMessage
   557  		if impExtPrebidJSON, exists := impExt[openrtb_ext.PrebidExtKey]; exists {
   558  			// validation already performed by impExt unmarshal. no error is possible here, proven by tests.
   559  			json.Unmarshal(impExtPrebidJSON, &impExtPrebid)
   560  		}
   561  
   562  		var impExtPrebidBidder map[string]json.RawMessage
   563  		if impExtPrebidBidderJSON, exists := impExtPrebid[openrtb_ext.PrebidExtBidderKey]; exists {
   564  			// validation already performed by impExt unmarshal. no error is possible here, proven by tests.
   565  			json.Unmarshal(impExtPrebidBidderJSON, &impExtPrebidBidder)
   566  		}
   567  
   568  		sanitizedImpExt, err := createSanitizedImpExt(impExt, impExtPrebid)
   569  		if err != nil {
   570  			return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: %v", i, err)
   571  		}
   572  
   573  		for bidder, bidderExt := range impExtPrebidBidder {
   574  			impCopy := imp
   575  
   576  			sanitizedImpExt[openrtb_ext.PrebidExtBidderKey] = bidderExt
   577  
   578  			impExtJSON, err := json.Marshal(sanitizedImpExt)
   579  			if err != nil {
   580  				return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: cannot marshal ext: %v", i, err)
   581  			}
   582  			impCopy.Ext = impExtJSON
   583  
   584  			bidderImps[bidder] = append(bidderImps[bidder], impCopy)
   585  		}
   586  	}
   587  
   588  	return bidderImps, nil
   589  }
   590  
   591  var allowedImpExtFields = map[string]interface{}{
   592  	openrtb_ext.AuctionEnvironmentKey:       struct{}{},
   593  	openrtb_ext.FirstPartyDataExtKey:        struct{}{},
   594  	openrtb_ext.FirstPartyDataContextExtKey: struct{}{},
   595  	openrtb_ext.GPIDKey:                     struct{}{},
   596  	openrtb_ext.SKAdNExtKey:                 struct{}{},
   597  	openrtb_ext.TIDKey:                      struct{}{},
   598  }
   599  
   600  var allowedImpExtPrebidFields = map[string]interface{}{
   601  	openrtb_ext.IsRewardedInventoryKey: struct{}{},
   602  	openrtb_ext.OptionsKey:             struct{}{},
   603  }
   604  
   605  func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map[string]json.RawMessage, error) {
   606  	sanitizedImpExt := make(map[string]json.RawMessage, 6)
   607  	sanitizedImpPrebidExt := make(map[string]json.RawMessage, 2)
   608  
   609  	// copy allowed imp[].ext.prebid fields
   610  	for k := range allowedImpExtPrebidFields {
   611  		if v, exists := impExtPrebid[k]; exists {
   612  			sanitizedImpPrebidExt[k] = v
   613  		}
   614  	}
   615  
   616  	// marshal sanitized imp[].ext.prebid
   617  	if len(sanitizedImpPrebidExt) > 0 {
   618  		if impExtPrebidJSON, err := json.Marshal(sanitizedImpPrebidExt); err == nil {
   619  			sanitizedImpExt[openrtb_ext.PrebidExtKey] = impExtPrebidJSON
   620  		} else {
   621  			return nil, fmt.Errorf("cannot marshal ext.prebid: %v", err)
   622  		}
   623  	}
   624  
   625  	// copy reserved imp[].ext fields known to not be bidder names
   626  	for k := range allowedImpExtFields {
   627  		if v, exists := impExt[k]; exists {
   628  			sanitizedImpExt[k] = v
   629  		}
   630  	}
   631  
   632  	return sanitizedImpExt, nil
   633  }
   634  
   635  // prepareUser changes req.User so that it's ready for the given bidder.
   636  // This *will* mutate the request, but will *not* mutate any objects nested inside it.
   637  //
   638  // In this function, "givenBidder" may or may not be an alias. "coreBidder" must *not* be an alias.
   639  // It returns true if a Cookie User Sync existed, and false otherwise.
   640  func prepareUser(req *openrtb2.BidRequest, givenBidder, syncerKey string, explicitBuyerUIDs map[string]string, usersyncs IdFetcher) bool {
   641  	cookieId, hadCookie, _ := usersyncs.GetUID(syncerKey)
   642  
   643  	if id, ok := explicitBuyerUIDs[givenBidder]; ok {
   644  		req.User = copyWithBuyerUID(req.User, id)
   645  	} else if hadCookie {
   646  		req.User = copyWithBuyerUID(req.User, cookieId)
   647  	}
   648  
   649  	return hadCookie
   650  }
   651  
   652  // copyWithBuyerUID either overwrites the BuyerUID property on user with the argument, or returns
   653  // a new (empty) User with the BuyerUID already set.
   654  func copyWithBuyerUID(user *openrtb2.User, buyerUID string) *openrtb2.User {
   655  	if user == nil {
   656  		return &openrtb2.User{
   657  			BuyerUID: buyerUID,
   658  		}
   659  	}
   660  	if user.BuyerUID == "" {
   661  		clone := *user
   662  		clone.BuyerUID = buyerUID
   663  		return &clone
   664  	}
   665  	return user
   666  }
   667  
   668  // removeUnpermissionedEids modifies the request to remove any request.user.ext.eids not permissions for the specific bidder
   669  func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, requestExt *openrtb_ext.ExtRequest) error {
   670  	// ensure request might have eids (as much as we can check before unmarshalling)
   671  	if request.User == nil || len(request.User.Ext) == 0 {
   672  		return nil
   673  	}
   674  
   675  	// ensure request has eid permissions to enforce
   676  	if requestExt == nil || requestExt.Prebid.Data == nil || len(requestExt.Prebid.Data.EidPermissions) == 0 {
   677  		return nil
   678  	}
   679  
   680  	// low level unmarshal to preserve other request.user.ext values. prebid server is non-destructive.
   681  	var userExt map[string]json.RawMessage
   682  	if err := json.Unmarshal(request.User.Ext, &userExt); err != nil {
   683  		return err
   684  	}
   685  
   686  	eidsJSON, eidsSpecified := userExt["eids"]
   687  	if !eidsSpecified {
   688  		return nil
   689  	}
   690  
   691  	var eids []openrtb2.EID
   692  	if err := json.Unmarshal(eidsJSON, &eids); err != nil {
   693  		return err
   694  	}
   695  
   696  	// exit early if there are no eids (empty array)
   697  	if len(eids) == 0 {
   698  		return nil
   699  	}
   700  
   701  	// translate eid permissions to a map for quick lookup
   702  	eidRules := make(map[string][]string)
   703  	for _, p := range requestExt.Prebid.Data.EidPermissions {
   704  		eidRules[p.Source] = p.Bidders
   705  	}
   706  
   707  	eidsAllowed := make([]openrtb2.EID, 0, len(eids))
   708  	for _, eid := range eids {
   709  		allowed := false
   710  		if rule, hasRule := eidRules[eid.Source]; hasRule {
   711  			for _, ruleBidder := range rule {
   712  				if ruleBidder == "*" || ruleBidder == bidder {
   713  					allowed = true
   714  					break
   715  				}
   716  			}
   717  		} else {
   718  			allowed = true
   719  		}
   720  
   721  		if allowed {
   722  			eidsAllowed = append(eidsAllowed, eid)
   723  		}
   724  	}
   725  
   726  	// exit early if all eids are allowed and nothing needs to be removed
   727  	if len(eids) == len(eidsAllowed) {
   728  		return nil
   729  	}
   730  
   731  	// marshal eidsAllowed back to userExt
   732  	if len(eidsAllowed) == 0 {
   733  		delete(userExt, "eids")
   734  	} else {
   735  		eidsRaw, err := json.Marshal(eidsAllowed)
   736  		if err != nil {
   737  			return err
   738  		}
   739  		userExt["eids"] = eidsRaw
   740  	}
   741  
   742  	// exit early if userExt is empty
   743  	if len(userExt) == 0 {
   744  		setUserExtWithCopy(request, nil)
   745  		return nil
   746  	}
   747  
   748  	userExtJSON, err := json.Marshal(userExt)
   749  	if err != nil {
   750  		return err
   751  	}
   752  	setUserExtWithCopy(request, userExtJSON)
   753  	return nil
   754  }
   755  
   756  func setUserExtWithCopy(request *openrtb2.BidRequest, userExtJSON json.RawMessage) {
   757  	userCopy := *request.User
   758  	userCopy.Ext = userExtJSON
   759  	request.User = &userCopy
   760  }
   761  
   762  // resolveBidder returns the known BidderName associated with bidder, if bidder is an alias. If it's not an alias, the bidder is returned.
   763  func resolveBidder(bidder string, aliases map[string]string) openrtb_ext.BidderName {
   764  	if coreBidder, ok := aliases[bidder]; ok {
   765  		return openrtb_ext.BidderName(coreBidder)
   766  	}
   767  	return openrtb_ext.BidderName(bidder)
   768  }
   769  
   770  // parseAliases parses the aliases from the BidRequest
   771  func parseAliases(orig *openrtb2.BidRequest) (map[string]string, []error) {
   772  	var aliases map[string]string
   773  	if value, dataType, _, err := jsonparser.Get(orig.Ext, openrtb_ext.PrebidExtKey, "aliases"); dataType == jsonparser.Object && err == nil {
   774  		if err := json.Unmarshal(value, &aliases); err != nil {
   775  			return nil, []error{err}
   776  		}
   777  	} else if dataType != jsonparser.NotExist && err != jsonparser.KeyPathNotFoundError {
   778  		return nil, []error{err}
   779  	}
   780  	return aliases, nil
   781  }
   782  
   783  // parseAliasesGVLIDs parses the Bidder Alias GVLIDs from the BidRequest
   784  func parseAliasesGVLIDs(orig *openrtb2.BidRequest) (map[string]uint16, []error) {
   785  	var aliasesGVLIDs map[string]uint16
   786  	if value, dataType, _, err := jsonparser.Get(orig.Ext, openrtb_ext.PrebidExtKey, "aliasgvlids"); dataType == jsonparser.Object && err == nil {
   787  		if err := json.Unmarshal(value, &aliasesGVLIDs); err != nil {
   788  			return nil, []error{err}
   789  		}
   790  	} else if dataType != jsonparser.NotExist && err != jsonparser.KeyPathNotFoundError {
   791  		return nil, []error{err}
   792  	}
   793  	return aliasesGVLIDs, nil
   794  }
   795  
   796  func GetValidBidders(aliases map[string]string) map[string]struct{} {
   797  	validBidders := openrtb_ext.BuildBidderNameHashSet()
   798  
   799  	for k := range aliases {
   800  		validBidders[k] = struct{}{}
   801  	}
   802  
   803  	return validBidders
   804  }
   805  
   806  // Quick little randomizer for a list of strings. Stuffing it in utils to keep other files clean
   807  func randomizeList(list []openrtb_ext.BidderName) {
   808  	l := len(list)
   809  	perm := rand.Perm(l)
   810  	var j int
   811  	for i := 0; i < l; i++ {
   812  		j = perm[i]
   813  		list[i], list[j] = list[j], list[i]
   814  	}
   815  }
   816  
   817  func getExtCacheInstructions(requestExtPrebid *openrtb_ext.ExtRequestPrebid) extCacheInstructions {
   818  	//returnCreative defaults to true
   819  	cacheInstructions := extCacheInstructions{returnCreative: true}
   820  	foundBidsRC := false
   821  	foundVastRC := false
   822  
   823  	if requestExtPrebid != nil && requestExtPrebid.Cache != nil {
   824  		if requestExtPrebid.Cache.Bids != nil {
   825  			cacheInstructions.cacheBids = true
   826  			if requestExtPrebid.Cache.Bids.ReturnCreative != nil {
   827  				cacheInstructions.returnCreative = *requestExtPrebid.Cache.Bids.ReturnCreative
   828  				foundBidsRC = true
   829  			}
   830  		}
   831  
   832  		if requestExtPrebid.Cache.VastXML != nil {
   833  			cacheInstructions.cacheVAST = true
   834  			if requestExtPrebid.Cache.VastXML.ReturnCreative != nil {
   835  				cacheInstructions.returnCreative = *requestExtPrebid.Cache.VastXML.ReturnCreative
   836  				foundVastRC = true
   837  			}
   838  		}
   839  	}
   840  
   841  	if foundBidsRC && foundVastRC {
   842  		cacheInstructions.returnCreative = *requestExtPrebid.Cache.Bids.ReturnCreative || *requestExtPrebid.Cache.VastXML.ReturnCreative
   843  	}
   844  
   845  	return cacheInstructions
   846  }
   847  
   848  func getExtTargetData(requestExtPrebid *openrtb_ext.ExtRequestPrebid, cacheInstructions extCacheInstructions) *targetData {
   849  	if requestExtPrebid != nil && requestExtPrebid.Targeting != nil {
   850  		return &targetData{
   851  			includeWinners:            *requestExtPrebid.Targeting.IncludeWinners,
   852  			includeBidderKeys:         *requestExtPrebid.Targeting.IncludeBidderKeys,
   853  			includeCacheBids:          cacheInstructions.cacheBids,
   854  			includeCacheVast:          cacheInstructions.cacheVAST,
   855  			includeFormat:             requestExtPrebid.Targeting.IncludeFormat,
   856  			priceGranularity:          *requestExtPrebid.Targeting.PriceGranularity,
   857  			mediaTypePriceGranularity: requestExtPrebid.Targeting.MediaTypePriceGranularity,
   858  			preferDeals:               requestExtPrebid.Targeting.PreferDeals,
   859  		}
   860  	}
   861  
   862  	return nil
   863  }
   864  
   865  // getDebugInfo returns the boolean flags that allow for debug information in bidResponse.Ext, the SeatBid.httpcalls slice, and
   866  // also sets the debugLog information
   867  func getDebugInfo(test int8, requestExtPrebid *openrtb_ext.ExtRequestPrebid, accountDebugFlag bool, debugLog *DebugLog) (bool, bool, *DebugLog) {
   868  	requestDebugAllow := parseRequestDebugValues(test, requestExtPrebid)
   869  	debugLog = setDebugLogValues(accountDebugFlag, debugLog)
   870  
   871  	responseDebugAllow := (requestDebugAllow && accountDebugFlag) || debugLog.DebugEnabledOrOverridden
   872  	accountDebugAllow := (requestDebugAllow && accountDebugFlag) || (debugLog.DebugEnabledOrOverridden && accountDebugFlag)
   873  
   874  	return responseDebugAllow, accountDebugAllow, debugLog
   875  }
   876  
   877  // setDebugLogValues initializes the DebugLog if nil. It also sets the value of the debugInfo flag
   878  // used in HoldAuction
   879  func setDebugLogValues(accountDebugFlag bool, debugLog *DebugLog) *DebugLog {
   880  	if debugLog == nil {
   881  		debugLog = &DebugLog{}
   882  	}
   883  
   884  	debugLog.Enabled = debugLog.DebugEnabledOrOverridden || accountDebugFlag
   885  	return debugLog
   886  }
   887  
   888  func parseRequestDebugValues(test int8, requestExtPrebid *openrtb_ext.ExtRequestPrebid) bool {
   889  	return test == 1 || (requestExtPrebid != nil && requestExtPrebid.Debug)
   890  }
   891  
   892  func getExtBidAdjustmentFactors(requestExtPrebid *openrtb_ext.ExtRequestPrebid) map[string]float64 {
   893  	if requestExtPrebid != nil {
   894  		return requestExtPrebid.BidAdjustmentFactors
   895  	}
   896  	return nil
   897  }
   898  
   899  func applyFPD(fpd *firstpartydata.ResolvedFirstPartyData, bidReq *openrtb2.BidRequest) {
   900  	if fpd.Site != nil {
   901  		bidReq.Site = fpd.Site
   902  	}
   903  	if fpd.App != nil {
   904  		bidReq.App = fpd.App
   905  	}
   906  	if fpd.User != nil {
   907  		//BuyerUID is a value obtained between fpd extraction and fpd application.
   908  		//BuyerUID needs to be set back to fpd before applying this fpd to final bidder request
   909  		if bidReq.User != nil && len(bidReq.User.BuyerUID) > 0 {
   910  			fpd.User.BuyerUID = bidReq.User.BuyerUID
   911  		}
   912  		bidReq.User = fpd.User
   913  	}
   914  }
   915  
   916  func buildBidResponseRequest(req *openrtb2.BidRequest,
   917  	bidderImpResponses stored_responses.BidderImpsWithBidResponses,
   918  	aliases map[string]string,
   919  	bidderImpReplaceImpID stored_responses.BidderImpReplaceImpID) map[openrtb_ext.BidderName]BidderRequest {
   920  
   921  	bidderToBidderResponse := make(map[openrtb_ext.BidderName]BidderRequest)
   922  
   923  	for bidderName, impResps := range bidderImpResponses {
   924  		resolvedBidder := resolveBidder(string(bidderName), aliases)
   925  		bidderToBidderResponse[bidderName] = BidderRequest{
   926  			BidRequest:            req,
   927  			BidderCoreName:        resolvedBidder,
   928  			BidderName:            bidderName,
   929  			BidderStoredResponses: impResps,
   930  			ImpReplaceImpId:       bidderImpReplaceImpID[string(resolvedBidder)],
   931  			BidderLabels:          metrics.AdapterLabels{Adapter: resolvedBidder},
   932  		}
   933  	}
   934  	return bidderToBidderResponse
   935  }
   936  
   937  func mergeBidderRequests(allBidderRequests []BidderRequest, bidderNameToBidderReq map[openrtb_ext.BidderName]BidderRequest) []BidderRequest {
   938  	if len(allBidderRequests) == 0 && len(bidderNameToBidderReq) == 0 {
   939  		return allBidderRequests
   940  	}
   941  	if len(allBidderRequests) == 0 && len(bidderNameToBidderReq) > 0 {
   942  		for _, v := range bidderNameToBidderReq {
   943  			allBidderRequests = append(allBidderRequests, v)
   944  		}
   945  		return allBidderRequests
   946  	} else if len(allBidderRequests) > 0 && len(bidderNameToBidderReq) > 0 {
   947  		//merge bidder requests with real imps and imps with stored resp
   948  		for bn, br := range bidderNameToBidderReq {
   949  			found := false
   950  			for i, ar := range allBidderRequests {
   951  				if ar.BidderName == bn {
   952  					//bidder req with real imps and imps with stored resp
   953  					allBidderRequests[i].BidderStoredResponses = br.BidderStoredResponses
   954  					found = true
   955  					break
   956  				}
   957  			}
   958  			if !found {
   959  				//bidder req with stored bid responses only
   960  				br.BidRequest.Imp = nil // to indicate this bidder request has bidder responses only
   961  				allBidderRequests = append(allBidderRequests, br)
   962  			}
   963  		}
   964  	}
   965  	return allBidderRequests
   966  }
   967  
   968  func setLegacyGDPRFromGPP(r *openrtb2.BidRequest, gpp gpplib.GppContainer) {
   969  	if r.Regs != nil && r.Regs.GDPR == nil {
   970  		if r.Regs.GPPSID != nil {
   971  			// Set to 0 unless SID exists
   972  			regs := *r.Regs
   973  			regs.GDPR = ptrutil.ToPtr[int8](0)
   974  			for _, id := range r.Regs.GPPSID {
   975  				if id == int8(gppConstants.SectionTCFEU2) {
   976  					regs.GDPR = ptrutil.ToPtr[int8](1)
   977  				}
   978  			}
   979  			r.Regs = &regs
   980  		}
   981  	}
   982  
   983  	if r.User == nil || len(r.User.Consent) == 0 {
   984  		for _, sec := range gpp.Sections {
   985  			if sec.GetID() == gppConstants.SectionTCFEU2 {
   986  				var user openrtb2.User
   987  				if r.User == nil {
   988  					user = openrtb2.User{}
   989  				} else {
   990  					user = *r.User
   991  				}
   992  				user.Consent = sec.GetValue()
   993  				r.User = &user
   994  			}
   995  		}
   996  	}
   997  
   998  }
   999  func setLegacyUSPFromGPP(r *openrtb2.BidRequest, gpp gpplib.GppContainer) {
  1000  	if r.Regs == nil {
  1001  		return
  1002  	}
  1003  
  1004  	if len(r.Regs.USPrivacy) > 0 || r.Regs.GPPSID == nil {
  1005  		return
  1006  	}
  1007  	for _, sid := range r.Regs.GPPSID {
  1008  		if sid == int8(gppConstants.SectionUSPV1) {
  1009  			for _, sec := range gpp.Sections {
  1010  				if sec.GetID() == gppConstants.SectionUSPV1 {
  1011  					regs := *r.Regs
  1012  					regs.USPrivacy = sec.GetValue()
  1013  					r.Regs = &regs
  1014  				}
  1015  			}
  1016  		}
  1017  	}
  1018  
  1019  }
  1020  
  1021  func WrapJSONInData(data []byte) []byte {
  1022  	res := make([]byte, 0, len(data))
  1023  	res = append(res, []byte(`{"data":`)...)
  1024  	res = append(res, data...)
  1025  	res = append(res, []byte(`}`)...)
  1026  	return res
  1027  }
  1028  
  1029  func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
  1030  	mType := bid.MType
  1031  	var bidType openrtb_ext.BidType
  1032  	if mType > 0 {
  1033  		switch mType {
  1034  		case openrtb2.MarkupBanner:
  1035  			bidType = openrtb_ext.BidTypeBanner
  1036  		case openrtb2.MarkupVideo:
  1037  			bidType = openrtb_ext.BidTypeVideo
  1038  		case openrtb2.MarkupAudio:
  1039  			bidType = openrtb_ext.BidTypeAudio
  1040  		case openrtb2.MarkupNative:
  1041  			bidType = openrtb_ext.BidTypeNative
  1042  		default:
  1043  			return bidType, fmt.Errorf("Failed to parse bid mType for impression \"%s\"", bid.ImpID)
  1044  		}
  1045  	} else {
  1046  		var err error
  1047  		bidType, err = getPrebidMediaTypeForBid(bid)
  1048  		if err != nil {
  1049  			return bidType, err
  1050  		}
  1051  	}
  1052  	return bidType, nil
  1053  }
  1054  
  1055  func getPrebidMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
  1056  	var err error
  1057  	var bidType openrtb_ext.BidType
  1058  
  1059  	if bid.Ext != nil {
  1060  		var bidExt openrtb_ext.ExtBid
  1061  		err = json.Unmarshal(bid.Ext, &bidExt)
  1062  		if err == nil && bidExt.Prebid != nil {
  1063  			if bidType, err = openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)); err == nil {
  1064  				return bidType, nil
  1065  			}
  1066  		}
  1067  	}
  1068  
  1069  	errMsg := fmt.Sprintf("Failed to parse bid mediatype for impression \"%s\"", bid.ImpID)
  1070  	if err != nil {
  1071  		errMsg = fmt.Sprintf("%s, %s", errMsg, err.Error())
  1072  	}
  1073  
  1074  	return bidType, &errortypes.BadServerResponse{
  1075  		Message: errMsg,
  1076  	}
  1077  }
  1078  
  1079  func applyBidAdjustmentToFloor(allBidderRequests []BidderRequest, bidAdjustmentFactors map[string]float64) {
  1080  
  1081  	if len(bidAdjustmentFactors) == 0 {
  1082  		return
  1083  	}
  1084  
  1085  	for _, bidderRequest := range allBidderRequests {
  1086  		bidAdjustment := 1.0
  1087  
  1088  		if bidAdjustemntValue, ok := bidAdjustmentFactors[string(bidderRequest.BidderName)]; ok {
  1089  			bidAdjustment = bidAdjustemntValue
  1090  		}
  1091  
  1092  		if bidAdjustment != 1.0 {
  1093  			for index, imp := range bidderRequest.BidRequest.Imp {
  1094  				imp.BidFloor = imp.BidFloor / bidAdjustment
  1095  				bidderRequest.BidRequest.Imp[index] = imp
  1096  			}
  1097  		}
  1098  	}
  1099  }