<commit_before>package pubnative

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"github.com/mxmCherry/openrtb"
	"github.com/prebid/prebid-server/adapters"
	"github.com/prebid/prebid-server/config"
	"github.com/prebid/prebid-server/errortypes"
	"github.com/prebid/prebid-server/openrtb_ext"
)

type PubnativeAdapter struct {
	URI string
}

func (a *PubnativeAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
	impCount := len(request.Imp)
	requestData := make([]*adapters.RequestData, 0, impCount)
	errs := []error{}

	headers := http.Header{}
	headers.Add("Content-Type", "application/json;charset=utf-8")
	headers.Add("Accept", "application/json")

	err := checkRequest(request)
	if err != nil {
		errs = append(errs, err)
		return nil, errs
	}

	for _, imp := range request.Imp {
		requestCopy := *request
		var bidderExt adapters.ExtImpBidder
		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
			errs = append(errs, err)
			continue
		}

		var pubnativeExt openrtb_ext.ExtImpPubnative
		if err := json.Unmarshal(bidderExt.Bidder, &pubnativeExt); err != nil {
			errs = append(errs, err)
			continue
		}

		err := convertImpression(&imp)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		requestCopy.Imp = []openrtb.Imp{imp}
		reqJSON, err := json.Marshal(&requestCopy)
		if err != nil {
			errs = append(errs, err)
			return nil, errs
		}

		queryParams := url.Values{}
		queryParams.Add("apptoken", pubnativeExt.AppAuthToken)
		queryParams.Add("zoneid", strconv.Itoa(pubnativeExt.ZoneID))
		queryString := queryParams.Encode()

		reqData := &adapters.RequestData{
			Method:  "POST",
			Uri:     fmt.Sprintf("%s?%s", a.URI, queryString),
			Body:    reqJSON,
			Headers: headers,
		}

		requestData = append(requestData, reqData)
	}
	return requestData, errs
}

func checkRequest(request *openrtb.BidRequest) error {
	if request.Device == nil || len(request.Device.OS) == 0 {
		return &errortypes.BadInput{
			Message: "Impression is missing device OS information",
		}
	}

	return nil
}

func convertImpression(imp *openrtb.Imp) error {
	if imp.Banner == nil && imp.Video == nil && imp.Native == nil {
		return &errortypes.BadInput{
			Message: "Pubnative only supports banner, video or native ads.",
		}
	}
	if imp.Banner != nil {
		err := convertBanner(imp.Banner)
		if err != nil {
			return err
		}
	}

	return nil
}

// make sure that banner has openrtb 2.3-compatible size information
func convertBanner(banner *openrtb.Banner) error {
	if banner.W == nil || banner.H == nil || *banner.W == 0 || *banner.H == 0 {
		if len(banner.Format) > 0 {
			f := banner.Format[0]
			banner.W = &f.W
			banner.H = &f.H
		} else {
			return &errortypes.BadInput{
				Message: "Size information missing for banner",
			}
		}
	}
	return nil
}

func (a *PubnativeAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
	if response.StatusCode == http.StatusNoContent {
		return nil, nil
	}

	if response.StatusCode == http.StatusBadRequest {
		return nil, []error{&errortypes.BadInput{
			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
		}}
	}

	if response.StatusCode != http.StatusOK {
		return nil, []error{&errortypes.BadServerResponse{
			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
		}}
	}

	var parsedResponse openrtb.BidResponse
	if err := json.Unmarshal(response.Body, &parsedResponse); err != nil {
		return nil, []error{&errortypes.BadServerResponse{
			Message: err.Error(),
		}}<commit_msg>Pubnative: Fix Shared Memory Overwriting (#1760)<commit_after>package pubnative

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"github.com/mxmCherry/openrtb"
	"github.com/prebid/prebid-server/adapters"
	"github.com/prebid/prebid-server/config"
	"github.com/prebid/prebid-server/errortypes"
	"github.com/prebid/prebid-server/openrtb_ext"
)

type PubnativeAdapter struct {
	URI string
}

func (a *PubnativeAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
	impCount := len(request.Imp)
	requestData := make([]*adapters.RequestData, 0, impCount)
	errs := []error{}

	headers := http.Header{}
	headers.Add("Content-Type", "application/json;charset=utf-8")
	headers.Add("Accept", "application/json")

	err := checkRequest(request)
	if err != nil {
		errs = append(errs, err)
		return nil, errs
	}

	for _, imp := range request.Imp {
		requestCopy := *request
		var bidderExt adapters.ExtImpBidder
		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
			errs = append(errs, err)
			continue
		}

		var pubnativeExt openrtb_ext.ExtImpPubnative
		if err := json.Unmarshal(bidderExt.Bidder, &pubnativeExt); err != nil {
			errs = append(errs, err)
			continue
		}

		err := convertImpression(&imp)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		requestCopy.Imp = []openrtb.Imp{imp}
		reqJSON, err := json.Marshal(&requestCopy)
		if err != nil {
			errs = append(errs, err)
			return nil, errs
		}

		queryParams := url.Values{}
		queryParams.Add("apptoken", pubnativeExt.AppAuthToken)
		queryParams.Add("zoneid", strconv.Itoa(pubnativeExt.ZoneID))
		queryString := queryParams.Encode()

		reqData := &adapters.RequestData{
			Method:  "POST",
			Uri:     fmt.Sprintf("%s?%s", a.URI, queryString),
			Body:    reqJSON,
			Headers: headers,
		}

		requestData = append(requestData, reqData)
	}
	return requestData, errs
}

func checkRequest(request *openrtb.BidRequest) error {
	if request.Device == nil || len(request.Device.OS) == 0 {
		return &errortypes.BadInput{
			Message: "Impression is missing device OS information",
		}
	}

	return nil
}

func convertImpression(imp *openrtb.Imp) error {
	if imp.Banner == nil && imp.Video == nil && imp.Native == nil {
		return &errortypes.BadInput{
			Message: "Pubnative only supports banner, video or native ads.",
		}
	}
	if imp.Banner != nil {
		bannerCopy, err := convertBanner(imp.Banner)
		if err != nil {
			return err
		}
		imp.Banner = bannerCopy
	}

	return nil
}

// make sure that banner has openrtb 2.3-compatible size information
func convertBanner(banner *openrtb.Banner) (*openrtb.Banner, error) {
	if banner.W == nil || banner.H == nil || *banner.W == 0 || *banner.H == 0 {
		if len(banner.Format) > 0 {
			f := banner.Format[0]

			bannerCopy := *banner

			bannerCopy.W = openrtb.Uint64Ptr(f.W)
			bannerCopy.H = openrtb.Uint64Ptr(f.H)

			return &bannerCopy, nil
		} else {
			return nil, &errortypes.BadInput{
				Message: "Size information missing for banner",
			}
		}
	}
	return banner, nil
}

func (a *PubnativeAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
	if response.StatusCode == http.StatusNoContent {
		return nil, nil
	}

	if response.StatusCode == http.StatusBadRequest {
		return nil, []error{&errortypes.BadInput{
			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
		}}
	}

	if response.StatusCode != http.StatusOK {
		return nil, []error{&errortypes.BadServerResponse{
			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
		}}
	}

	var parsedResponse openrtb.BidResponse
	if err := json.Unmarshal(response.Body, &parsedResponse); err != nil {
		return nil, []error{&errortypes.BadServerResponse{
			Message: err.Error(),
		}}
{'fraction_non_alphanumeric': 0.1375968992248062, 'fraction_numerical': 0.003875968992248062, 'mean_word_length': 4.086070959264126, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5605475', 'n_tokens_mistral': 2962, 'n_tokens_neox': 2702, 'n_words': 612}