Skip to content
wais.go 60 KiB
Newer Older
Simon Pastor's avatar
Simon Pastor committed
	//check all that applies
	for subsId, subInfo := range assocStaSubscriptionInfoMap {
		if subInfo == nil {
			break
		}
		sub := subInfo.Subscription
Simon Pastor's avatar
Simon Pastor committed
		match := false
Simon Pastor's avatar
Simon Pastor committed
		sendingNotificationAllowed := true
Simon Pastor's avatar
Simon Pastor committed

		if sub != nil {
Simon Pastor's avatar
Simon Pastor committed
			if !subInfo.NotificationCheckReady {
Simon Pastor's avatar
Simon Pastor committed
				match = true
			}

			if match {
				if sub.NotificationEvent != nil {
					match = false
					switch sub.NotificationEvent.Trigger {
Simon Pastor's avatar
Simon Pastor committed
					case 1:
						if len(staMacIds) >= int(sub.NotificationEvent.Threshold) {
							match = true
						}
Simon Pastor's avatar
Simon Pastor committed
					case 2:
						if len(staMacIds) <= int(sub.NotificationEvent.Threshold) {
							match = true
						}
					default:
					}
Simon Pastor's avatar
Simon Pastor committed
					//if the notification already triggered, do not send it again unless its a periodic event
					if match {
						if sub.NotificationPeriod == 0 {
							if subInfo.Triggered {
								sendingNotificationAllowed = false
							}
						}
					} else {
						// no match found for threshold, reste trigger
						assocStaSubscriptionInfoMap[subsId].Triggered = false
					}
Simon Pastor's avatar
Simon Pastor committed
			if match && sendingNotificationAllowed {

				assocStaSubscriptionInfoMap[subsId].Triggered = true

Simon Pastor's avatar
Simon Pastor committed
				subsIdStr := strconv.Itoa(subsId)
				log.Info("Sending WAIS notification ", sub.CallbackReference)

Simon Pastor's avatar
Simon Pastor committed
				var notif AssocStaNotification
Simon Pastor's avatar
Simon Pastor committed

				seconds := time.Now().Unix()
Simon Pastor's avatar
Simon Pastor committed
				var timeStamp TimeStamp
Simon Pastor's avatar
Simon Pastor committed
				timeStamp.Seconds = int32(seconds)

Simon Pastor's avatar
Simon Pastor committed
				notif.TimeStamp = &timeStamp
Simon Pastor's avatar
Simon Pastor committed
				notif.NotificationType = ASSOC_STA_NOTIFICATION
Simon Pastor's avatar
Simon Pastor committed
				var apId ApIdentity
Simon Pastor's avatar
Simon Pastor committed
				notif.ApId = &apId

				for _, staMacId := range staMacIds {
Simon Pastor's avatar
Simon Pastor committed
					var staId StaIdentity
Simon Pastor's avatar
Simon Pastor committed
					staId.MacId = staMacId
					notif.StaId = append(notif.StaId, staId)
				}

Simon Pastor's avatar
Simon Pastor committed
				sendAssocStaNotification(sub.CallbackReference, notif)
Simon Pastor's avatar
Simon Pastor committed
				log.Info("Assoc Sta Notification" + "(" + subsIdStr + ")")
Simon Pastor's avatar
Simon Pastor committed
				assocStaSubscriptionInfoMap[subsId].NextTts = subInfo.Subscription.NotificationPeriod
				assocStaSubscriptionInfoMap[subsId].NotificationCheckReady = false
Simon Pastor's avatar
Simon Pastor committed
func checkStaDataRateNotificationRegisteredSubscriptions(staId *StaIdentity, dataRateDl int32, dataRateUl int32, needMutex bool) {
	if needMutex {
		mutex.Lock()
		defer mutex.Unlock()
	}
	for subsId, subInfo := range staDataRateSubscriptionInfoMap {
		if subInfo == nil {
			break
		}
		sub := subInfo.Subscription
Simon Pastor's avatar
Simon Pastor committed
			if !subInfo.NotificationCheckReady {
			notifToSend := false
			var staDataRateList []StaDataRate
			for _, subStaId := range sub.StaId {
				//check to match every values and at least one when its an array
				if staId.MacId != subStaId.MacId {
					continue
				}
				if staId.Aid != subStaId.Aid {
					continue
				}
Simon Pastor's avatar
Simon Pastor committed

Simon Pastor's avatar
Simon Pastor committed
				sendingNotificationAllowed := true
				for _, ssid := range subStaId.Ssid {
					match = false
					//can only have one ssid at a time
					if ssid == staId.Ssid[0] {
						match = true
						break
					}
				}
				if match {
					for _, ipAddress := range subStaId.IpAddress {
						match = false
						//can only have one ip address
						if ipAddress == staId.IpAddress[0] {
							match = true
							break
						}
					}
				}
				if match {
					if sub.NotificationEvent != nil {
						match = false
						switch sub.NotificationEvent.Trigger {
Simon Pastor's avatar
Simon Pastor committed
						case 1:
							if dataRateDl >= sub.NotificationEvent.DownlinkRateThreshold {
								match = true
							}
Simon Pastor's avatar
Simon Pastor committed
						case 2:
							if dataRateDl <= sub.NotificationEvent.DownlinkRateThreshold {
								match = true
							}
Simon Pastor's avatar
Simon Pastor committed
						case 3:
							if dataRateUl >= sub.NotificationEvent.UplinkRateThreshold {
								match = true
							}
Simon Pastor's avatar
Simon Pastor committed
						case 4:
							if dataRateUl <= sub.NotificationEvent.UplinkRateThreshold {
Simon Pastor's avatar
Simon Pastor committed
						case 5:
							if dataRateDl >= sub.NotificationEvent.DownlinkRateThreshold && dataRateUl >= sub.NotificationEvent.UplinkRateThreshold {
								match = true
							}
Simon Pastor's avatar
Simon Pastor committed
						case 6:
							if dataRateDl <= sub.NotificationEvent.DownlinkRateThreshold && dataRateUl <= sub.NotificationEvent.UplinkRateThreshold {
								match = true
							}
Simon Pastor's avatar
Simon Pastor committed
						case 7:
							if dataRateDl >= sub.NotificationEvent.DownlinkRateThreshold || dataRateUl >= sub.NotificationEvent.UplinkRateThreshold {
								match = true
							}
Simon Pastor's avatar
Simon Pastor committed
						case 8:
							if dataRateDl <= sub.NotificationEvent.DownlinkRateThreshold || dataRateUl <= sub.NotificationEvent.UplinkRateThreshold {
								match = true
							}
						default:
						}
Simon Pastor's avatar
Simon Pastor committed
						//if the notification already triggered, do not send it again unless its a periodic event
						if match {
							if sub.NotificationPeriod == 0 {
								if subInfo.Triggered {
									sendingNotificationAllowed = false
								}
							}
						} else {
							// no match found for threshold, reste trigger
							staDataRateSubscriptionInfoMap[subsId].Triggered = false
						}
Simon Pastor's avatar
Simon Pastor committed
				if match && sendingNotificationAllowed {
					var staDataRate StaDataRate
					staDataRate.StaId = staId
					staDataRate.StaLastDataDownlinkRate = dataRateDl
					staDataRate.StaLastDataUplinkRate = dataRateUl
					staDataRateList = append(staDataRateList, staDataRate)
					notifToSend = true
				}
			}

			if notifToSend {
Simon Pastor's avatar
Simon Pastor committed

Simon Pastor's avatar
Simon Pastor committed
				staDataRateSubscriptionInfoMap[subsId].Triggered = true

				subsIdStr := strconv.Itoa(subsId)
				log.Info("Sending WAIS notification ", sub.CallbackReference)

				var notif StaDataRateNotification

				seconds := time.Now().Unix()
				var timeStamp TimeStamp
				timeStamp.Seconds = int32(seconds)

				notif.TimeStamp = &timeStamp
				notif.NotificationType = STA_DATA_RATE_NOTIFICATION

Simon Pastor's avatar
Simon Pastor committed
				if len(staDataRateList) > 0 {
					notif.StaDataRate = staDataRateList
				}
				sendStaDataRateNotification(sub.CallbackReference, notif)
				log.Info("Sta Data Rate Notification" + "(" + subsIdStr + ")")
Simon Pastor's avatar
Simon Pastor committed
				staDataRateSubscriptionInfoMap[subsId].NextTts = subInfo.Subscription.NotificationPeriod
				staDataRateSubscriptionInfoMap[subsId].NotificationCheckReady = false
			}
		}
	}
}

func sendTestNotification(notifyUrl string, linkType *LinkType) {

	var notification TestNotification
	notification.NotificationType = TEST_NOTIFICATION

	link := new(ExpiryNotificationLinks)
	link.Subscription = linkType
	notification.Links = link

	startTime := time.Now()
	jsonNotif, err := json.Marshal(notification)
	if err != nil {
		log.Error(err.Error())
	}

	resp, err := http.Post(notifyUrl, "application/json", bytes.NewBuffer(jsonNotif))
	duration := float64(time.Since(startTime).Microseconds()) / 1000.0
	_ = httpLog.LogTx(notifyUrl, "POST", string(jsonNotif), resp, startTime)
	if err != nil {
		log.Error(err)
		met.ObserveNotification(sandboxName, serviceName, notifTest, notifyUrl, nil, duration)
		return
	}
	met.ObserveNotification(sandboxName, serviceName, notifTest, notifyUrl, resp, duration)
	defer resp.Body.Close()
}

Simon Pastor's avatar
Simon Pastor committed
func sendAssocStaNotification(notifyUrl string, notification AssocStaNotification) {
Simon Pastor's avatar
Simon Pastor committed
	startTime := time.Now()
Simon Pastor's avatar
Simon Pastor committed
	jsonNotif, err := json.Marshal(notification)
	if err != nil {
		log.Error(err.Error())
	}
Simon Pastor's avatar
Simon Pastor committed

Simon Pastor's avatar
Simon Pastor committed
	resp, err := http.Post(notifyUrl, "application/json", bytes.NewBuffer(jsonNotif))
	duration := float64(time.Since(startTime).Microseconds()) / 1000.0
Simon Pastor's avatar
Simon Pastor committed
	_ = httpLog.LogTx(notifyUrl, "POST", string(jsonNotif), resp, startTime)
	if err != nil {
		log.Error(err)
		met.ObserveNotification(sandboxName, serviceName, notifAssocSta, notifyUrl, nil, duration)
Simon Pastor's avatar
Simon Pastor committed
		return
	}
	met.ObserveNotification(sandboxName, serviceName, notifAssocSta, notifyUrl, resp, duration)
Simon Pastor's avatar
Simon Pastor committed
	defer resp.Body.Close()
func sendStaDataRateNotification(notifyUrl string, notification StaDataRateNotification) {
	startTime := time.Now()
	jsonNotif, err := json.Marshal(notification)
	if err != nil {
		log.Error(err.Error())
	}

	resp, err := http.Post(notifyUrl, "application/json", bytes.NewBuffer(jsonNotif))
	duration := float64(time.Since(startTime).Microseconds()) / 1000.0
	_ = httpLog.LogTx(notifyUrl, "POST", string(jsonNotif), resp, startTime)
	if err != nil {
		log.Error(err)
		met.ObserveNotification(sandboxName, serviceName, notifStaDataRate, notifyUrl, nil, duration)
		return
	}
	met.ObserveNotification(sandboxName, serviceName, notifStaDataRate, notifyUrl, resp, duration)
	defer resp.Body.Close()
}

Simon Pastor's avatar
Simon Pastor committed
func sendExpiryNotification(notifyUrl string, notification ExpiryNotification) {
Simon Pastor's avatar
Simon Pastor committed
	startTime := time.Now()
	jsonNotif, err := json.Marshal(notification)
	if err != nil {
		log.Error(err.Error())
	}
Simon Pastor's avatar
Simon Pastor committed
	resp, err := http.Post(notifyUrl, "application/json", bytes.NewBuffer(jsonNotif))
	duration := float64(time.Since(startTime).Microseconds()) / 1000.0
Simon Pastor's avatar
Simon Pastor committed
	_ = httpLog.LogTx(notifyUrl, "POST", string(jsonNotif), resp, startTime)
	if err != nil {
		log.Error(err)
		met.ObserveNotification(sandboxName, serviceName, notifExpiry, notifyUrl, nil, duration)
Simon Pastor's avatar
Simon Pastor committed
		return
	}
	met.ObserveNotification(sandboxName, serviceName, notifExpiry, notifyUrl, resp, duration)
Simon Pastor's avatar
Simon Pastor committed
	defer resp.Body.Close()
Simon Pastor's avatar
Simon Pastor committed
}

func subscriptionsGET(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	vars := mux.Vars(r)
	subIdParamStr := vars["subscriptionId"]

Simon Pastor's avatar
Simon Pastor committed
	jsonRespDB, _ := rc.JSONGetEntry(baseKey+"subscriptions:"+subIdParamStr, ".")

	if jsonRespDB == "" {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	var subscriptionCommon SubscriptionCommon
	err := json.Unmarshal([]byte(jsonRespDB), &subscriptionCommon)
	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var jsonResponse []byte
	switch subscriptionCommon.SubscriptionType {
	case ASSOC_STA_SUBSCRIPTION:
		var subscription AssocStaSubscription
		err = json.Unmarshal([]byte(jsonRespDB), &subscription)
		if err != nil {
			log.Error(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		jsonResponse, err = json.Marshal(subscription)
	case STA_DATA_RATE_SUBSCRIPTION:
		var subscription StaDataRateSubscription
		err = json.Unmarshal([]byte(jsonRespDB), &subscription)
		if err != nil {
			log.Error(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

Simon Pastor's avatar
Simon Pastor committed
		jsonResponse, err = json.Marshal(subscription)
Simon Pastor's avatar
Simon Pastor committed
	case MEASUREMENT_REPORT_SUBSCRIPTION:
		w.WriteHeader(http.StatusNotImplemented)
		return
Simon Pastor's avatar
Simon Pastor committed
	default:
		log.Error("Unknown subscription type")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(jsonResponse))
}

func isSubscriptionIdRegisteredAssocSta(subsIdStr string) bool {
Simon Pastor's avatar
Simon Pastor committed
	subsId, _ := strconv.Atoi(subsIdStr)
	mutex.Lock()
	defer mutex.Unlock()
	if assocStaSubscriptionInfoMap[subsId] != nil {
Simon Pastor's avatar
Simon Pastor committed
		return true
	} else {
		return false
	}
}

Simon Pastor's avatar
Simon Pastor committed
/*
func isSubscriptionIdRegisteredStaDataRate(subsIdStr string) bool {
	subsId, _ := strconv.Atoi(subsIdStr)
	mutex.Lock()
	defer mutex.Unlock()
	if staDataRateSubscriptionInfoMap[subsId] != nil {
Simon Pastor's avatar
Simon Pastor committed
*/
Simon Pastor's avatar
Simon Pastor committed
func registerAssocSta(subscription *AssocStaSubscription, subsIdStr string) {
Simon Pastor's avatar
Simon Pastor committed
	subsId, _ := strconv.Atoi(subsIdStr)
	mutex.Lock()
	defer mutex.Unlock()

Simon Pastor's avatar
Simon Pastor committed
	//immediate trigger of the subscription
	assocStaSubscriptionInfo := AssocStaSubscriptionInfo{0 /*subscription.NotificationPeriod*/, false, subscription, false}
	assocStaSubscriptionInfoMap[subsId] = &assocStaSubscriptionInfo
Simon Pastor's avatar
Simon Pastor committed
	if subscription.ExpiryDeadline != nil {
		//get current list of subscription meant to expire at this time
		intList := subscriptionExpiryMap[int(subscription.ExpiryDeadline.Seconds)]
		intList = append(intList, subsId)
		subscriptionExpiryMap[int(subscription.ExpiryDeadline.Seconds)] = intList
	}

	log.Info("New registration: ", subsId, " type: ", subscription.SubscriptionType)
	if subscription.RequestTestNotification {
		sendTestNotification(subscription.CallbackReference, subscription.Links.Self)
	}
}

Simon Pastor's avatar
Simon Pastor committed
/*
func registerStaDataRate(subscription *StaDataRateSubscription, subsIdStr string) {
	subsId, _ := strconv.Atoi(subsIdStr)
	mutex.Lock()
	defer mutex.Unlock()

Simon Pastor's avatar
Simon Pastor committed
	//immediate trigger of the subscription
Simon Pastor's avatar
Simon Pastor committed
	staDataRateSubscriptionInfo := StaDataRateSubscriptionInfo{0, false, subscription, false}
	staDataRateSubscriptionInfoMap[subsId] = &staDataRateSubscriptionInfo
	if subscription.ExpiryDeadline != nil {
		//get current list of subscription meant to expire at this time
		intList := subscriptionExpiryMap[int(subscription.ExpiryDeadline.Seconds)]
		intList = append(intList, subsId)
		subscriptionExpiryMap[int(subscription.ExpiryDeadline.Seconds)] = intList
	}

	log.Info("New registration: ", subsId, " type: ", subscription.SubscriptionType)
	if subscription.RequestTestNotification {
		sendTestNotification(subscription.CallbackReference, subscription.Links.Self)
	}
Simon Pastor's avatar
Simon Pastor committed
*/
Simon Pastor's avatar
Simon Pastor committed
func deregisterAssocSta(subsIdStr string, mutexTaken bool) {
Simon Pastor's avatar
Simon Pastor committed
	subsId, _ := strconv.Atoi(subsIdStr)
	if !mutexTaken {
		mutex.Lock()
		defer mutex.Unlock()
	}
	assocStaSubscriptionInfoMap[subsId] = nil
Simon Pastor's avatar
Simon Pastor committed
	log.Info("Deregistration: ", subsId, " type: ", assocStaSubscriptionType)
}

func deregisterStaDataRate(subsIdStr string, mutexTaken bool) {
	subsId, _ := strconv.Atoi(subsIdStr)
	if !mutexTaken {
		mutex.Lock()
		defer mutex.Unlock()
	}
	staDataRateSubscriptionInfoMap[subsId] = nil
	log.Info("Deregistration: ", subsId, " type: ", staDataRateSubscriptionType)
}

Simon Pastor's avatar
Simon Pastor committed
func subscriptionsPOST(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

Simon Pastor's avatar
Simon Pastor committed
	var subscriptionCommon SubscriptionCommon
	bodyBytes, _ := ioutil.ReadAll(r.Body)
	err := json.Unmarshal(bodyBytes, &subscriptionCommon)
	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
Simon Pastor's avatar
Simon Pastor committed
	//extract common body part
	subscriptionType := subscriptionCommon.SubscriptionType
	//mandatory parameter
	if subscriptionCommon.CallbackReference == "" {
		log.Error("Mandatory CallbackReference parameter not present")
		http.Error(w, "Mandatory CallbackReference parameter not present", http.StatusBadRequest)
		return
	}

Simon Pastor's avatar
Simon Pastor committed
	//new subscription id
Simon Pastor's avatar
Simon Pastor committed
	newSubsId := nextSubscriptionIdAvailable
	nextSubscriptionIdAvailable++
	subsIdStr := strconv.Itoa(newSubsId)
Simon Pastor's avatar
Simon Pastor committed
	link := new(AssocStaSubscriptionLinks)
	self := new(LinkType)
	self.Href = hostUrl.String() + basePath + "subscriptions/" + subsIdStr
	link.Self = self

	var jsonResponse []byte

	switch subscriptionType {
	case ASSOC_STA_SUBSCRIPTION:
		var subscription AssocStaSubscription
		err = json.Unmarshal(bodyBytes, &subscription)
		if err != nil {
			log.Error(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		subscription.Links = link

		//make sure subscription is valid for mandatory parameters
		if subscription.NotificationPeriod == 0 && subscription.NotificationEvent == nil {
			log.Error("Either or Both NotificationPeriod or NotificationEvent shall be present")
			http.Error(w, "Either or Both NotificationPeriod or NotificationEvent shall be present", http.StatusBadRequest)
			return
		}

		if subscription.NotificationEvent != nil {
Simon Pastor's avatar
Simon Pastor committed
			if subscription.NotificationEvent.Trigger <= 0 && subscription.NotificationEvent.Trigger > 2 {
				log.Error("Mandatory Notification Event Trigger not valid")
				http.Error(w, "Mandatory Notification Event Trigger not valid", http.StatusBadRequest)
				return
			}
		}
		if subscription.ApId == nil {
			log.Error("Mandatory ApId missing")
			http.Error(w, "Mandatory ApId missing", http.StatusBadRequest)
			return
Simon Pastor's avatar
Simon Pastor committed
		} else {
			if subscription.ApId.Bssid == "" {
				log.Error("Mandatory ApId Bssid missing")
				http.Error(w, "Mandatory ApId Bssid missing", http.StatusBadRequest)
				return
			}
Simon Pastor's avatar
Simon Pastor committed
		//registration
		_ = rc.JSONSetEntry(baseKey+"subscriptions:"+subsIdStr, ".", convertAssocStaSubscriptionToJson(&subscription))
		registerAssocSta(&subscription, subsIdStr)

		jsonResponse, err = json.Marshal(subscription)
	case STA_DATA_RATE_SUBSCRIPTION:
Simon Pastor's avatar
Simon Pastor committed
		nextSubscriptionIdAvailable--
		w.WriteHeader(http.StatusNotImplemented)
		return
		/* TBD when traffic is available
		var subscription StaDataRateSubscription
		err = json.Unmarshal(bodyBytes, &subscription)
		if err != nil {
			log.Error(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		subscription.Links = link

		//make sure subscription is valid for mandatory parameters
		if subscription.NotificationPeriod == 0 && subscription.NotificationEvent == nil {
			log.Error("Either or Both NotificationPeriod or NotificationEvent shall be present")
			http.Error(w, "Either or Both NotificationPeriod or NotificationEvent shall be present", http.StatusBadRequest)
			return
		}

		if subscription.NotificationEvent != nil {
Simon Pastor's avatar
Simon Pastor committed
			if subscription.NotificationEvent.Trigger <= 0 && subscription.NotificationEvent.Trigger > 8 {
				log.Error("Mandatory Notification Event Trigger not valid")
				http.Error(w, "Mandatory Notification Event Trigger not valid", http.StatusBadRequest)
				return
			}
		}
		if subscription.StaId == nil {
			log.Error("Mandatory StaId missing")
			http.Error(w, "Mandatory StaId missing", http.StatusBadRequest)
			return
Simon Pastor's avatar
Simon Pastor committed
		} else {
			for _, staId := range subscription.StaId {
				if staId.MacId == "" {
					log.Error("Mandatory StaId MacId missing")
					http.Error(w, "Mandatory StaId MacId missing", http.StatusBadRequest)
					return
				}
			}
		//registration
		_ = rc.JSONSetEntry(baseKey+"subscriptions:"+subsIdStr, ".", convertStaDataRateSubscriptionToJson(&subscription))
		registerStaDataRate(&subscription, subsIdStr)

Simon Pastor's avatar
Simon Pastor committed
		jsonResponse, err = json.Marshal(subscription)
Simon Pastor's avatar
Simon Pastor committed
		*/
Simon Pastor's avatar
Simon Pastor committed
	case MEASUREMENT_REPORT_SUBSCRIPTION:
		nextSubscriptionIdAvailable--
		w.WriteHeader(http.StatusNotImplemented)
		return
Simon Pastor's avatar
Simon Pastor committed
	default:
		nextSubscriptionIdAvailable--
		w.WriteHeader(http.StatusBadRequest)
		return
	}
Simon Pastor's avatar
Simon Pastor committed

	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, string(jsonResponse))

}

func subscriptionsPUT(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	vars := mux.Vars(r)
	subIdParamStr := vars["subscriptionId"]

Simon Pastor's avatar
Simon Pastor committed
	var subscriptionCommon SubscriptionCommon
	bodyBytes, _ := ioutil.ReadAll(r.Body)
	err := json.Unmarshal(bodyBytes, &subscriptionCommon)
	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	//extract common body part
	subscriptionType := subscriptionCommon.SubscriptionType

	//mandatory parameter
	if subscriptionCommon.CallbackReference == "" {
		log.Error("Mandatory CallbackReference parameter not present")
		http.Error(w, "Mandatory CallbackReference parameter not present", http.StatusBadRequest)
		return
	}

Simon Pastor's avatar
Simon Pastor committed
	link := subscriptionCommon.Links
	if link == nil || link.Self == nil {
		log.Error("Mandatory Link parameter not present")
		http.Error(w, "Mandatory Link parameter not present", http.StatusBadRequest)
Simon Pastor's avatar
Simon Pastor committed
		return
	}

	selfUrl := strings.Split(link.Self.Href, "/")
	subsIdStr := selfUrl[len(selfUrl)-1]

	if subsIdStr != subIdParamStr {
		log.Error("SubscriptionId in endpoint and in body not matching")
		http.Error(w, "SubscriptionId in endpoint and in body not matching", http.StatusBadRequest)
Simon Pastor's avatar
Simon Pastor committed
		return
	}

	alreadyRegistered := false
	var jsonResponse []byte

	switch subscriptionType {
	case ASSOC_STA_SUBSCRIPTION:
		var subscription AssocStaSubscription
		err = json.Unmarshal(bodyBytes, &subscription)
		if err != nil {
			log.Error(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
Simon Pastor's avatar
Simon Pastor committed

Simon Pastor's avatar
Simon Pastor committed
		//make sure subscription is valid for mandatory parameters
		if subscription.NotificationPeriod == 0 && subscription.NotificationEvent == nil {
			log.Error("Either or Both NotificationPeriod or NotificationEvent shall be present")
			http.Error(w, "Either or Both NotificationPeriod or NotificationEvent shall be present", http.StatusBadRequest)
			return
		}

		if subscription.NotificationEvent != nil {
			if subscription.NotificationEvent.Trigger <= 0 && subscription.NotificationEvent.Trigger > 8 {
				log.Error("Mandatory Notification Event Trigger not valid")
				http.Error(w, "Mandatory Notification Event Trigger not valid", http.StatusBadRequest)
				return
			}
		}
		if subscription.ApId == nil {
			log.Error("Mandatory ApId missing")
			http.Error(w, "Mandatory ApId missing", http.StatusBadRequest)
			return
		} else {
			if subscription.ApId.Bssid == "" {
				log.Error("Mandatory ApId Bssid missing")
				http.Error(w, "Mandatory ApId Bssid missing", http.StatusBadRequest)
				return
			}
		}

Simon Pastor's avatar
Simon Pastor committed
		//only support one subscription
Simon Pastor's avatar
Simon Pastor committed
		if isSubscriptionIdRegisteredAssocSta(subsIdStr) {
			registerAssocSta(&subscription, subsIdStr)

			_ = rc.JSONSetEntry(baseKey+"subscriptions:"+subsIdStr, ".", convertAssocStaSubscriptionToJson(&subscription))
			alreadyRegistered = true
			jsonResponse, err = json.Marshal(subscription)
		}
Simon Pastor's avatar
Simon Pastor committed
		w.WriteHeader(http.StatusNotImplemented)
		return
		/* TBD when traffic is available

		var subscription StaDataRateSubscription
		err = json.Unmarshal(bodyBytes, &subscription)
		if err != nil {
			log.Error(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

Simon Pastor's avatar
Simon Pastor committed
		//make sure subscription is valid for mandatory parameters
		if subscription.NotificationPeriod == 0 && subscription.NotificationEvent == nil {
			log.Error("Either or Both NotificationPeriod or NotificationEvent shall be present")
			http.Error(w, "Either or Both NotificationPeriod or NotificationEvent shall be present", http.StatusBadRequest)
			return
		}

		if subscription.NotificationEvent != nil {
			if subscription.NotificationEvent.Trigger <= 0 && subscription.NotificationEvent.Trigger > 8 {
				log.Error("Mandatory Notification Event Trigger not valid")
				http.Error(w, "Mandatory Notification Event Trigger not valid", http.StatusBadRequest)
				return
			}
		}
		if subscription.StaId == nil {
			log.Error("Mandatory StaId missing")
			http.Error(w, "Mandatory StaId missing", http.StatusBadRequest)
			return
		} else {
			for _, staId := range subscription.StaId {
				if staId.MacId == "" {
					log.Error("Mandatory StaId MacId missing")
					http.Error(w, "Mandatory StaId MacId missing", http.StatusBadRequest)
					return
				}
			}
		}

		//only support one subscription
		if isSubscriptionIdRegisteredStaDataRate(subsIdStr) {
			registerStaDataRate(&subscription, subsIdStr)

			_ = rc.JSONSetEntry(baseKey+"subscriptions:"+subsIdStr, ".", convertStaDataRateSubscriptionToJson(&subscription))
			alreadyRegistered = true
			jsonResponse, err = json.Marshal(subscription)
		}
Simon Pastor's avatar
Simon Pastor committed
		*/
Simon Pastor's avatar
Simon Pastor committed
	case MEASUREMENT_REPORT_SUBSCRIPTION:
		w.WriteHeader(http.StatusNotImplemented)
		return
Simon Pastor's avatar
Simon Pastor committed
	default:
		w.WriteHeader(http.StatusBadRequest)
		return
	}
Simon Pastor's avatar
Simon Pastor committed

Simon Pastor's avatar
Simon Pastor committed
	if alreadyRegistered {
		if err != nil {
			log.Error(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
Simon Pastor's avatar
Simon Pastor committed
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, string(jsonResponse))
	} else {
		w.WriteHeader(http.StatusNotFound)
	}
func delSubscription(keyPrefix string, subsId string, mutexTaken bool) error {
Simon Pastor's avatar
Simon Pastor committed

	err := rc.JSONDelEntry(keyPrefix+":"+subsId, ".")
Simon Pastor's avatar
Simon Pastor committed
	deregisterAssocSta(subsId, mutexTaken)
	deregisterStaDataRate(subsId, mutexTaken)
Simon Pastor's avatar
Simon Pastor committed
	return err
}

func subscriptionsDELETE(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	vars := mux.Vars(r)

Simon Pastor's avatar
Simon Pastor committed
	subIdParamStr := vars["subscriptionId"]
	jsonRespDB, _ := rc.JSONGetEntry(baseKey+"subscriptions:"+subIdParamStr, ".")
	if jsonRespDB == "" {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	err := delSubscription(baseKey+"subscriptions", subIdParamStr, false)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
Simon Pastor's avatar
Simon Pastor committed

Simon Pastor's avatar
Simon Pastor committed
	w.WriteHeader(http.StatusNoContent)
Simon Pastor's avatar
Simon Pastor committed
}

func populateApInfo(key string, jsonInfo string, response interface{}) error {
Simon Pastor's avatar
Simon Pastor committed
	resp := response.(*ApInfoResp)
Simon Pastor's avatar
Simon Pastor committed
	if resp == nil {
		return errors.New("Response not defined")
	}

	// Retrieve user info from DB
	var apInfoComplete ApInfoComplete
	err := json.Unmarshal([]byte(jsonInfo), &apInfoComplete)
	if err != nil {
		return err
	}

Simon Pastor's avatar
Simon Pastor committed
	//timeStamp is optional, commenting the code
	//seconds := time.Now().Unix()
	//var timeStamp TimeStamp
	//timeStamp.Seconds = int32(seconds)
Simon Pastor's avatar
Simon Pastor committed

	var apInfo ApInfo
Simon Pastor's avatar
Simon Pastor committed
	//apInfo.TimeStamp = &timeStamp
Simon Pastor's avatar
Simon Pastor committed

	apInfo.ApId = &apInfoComplete.ApId
Simon Pastor's avatar
Simon Pastor committed

	var bssLoad BssLoad
	bssLoad.StaCount = int32(len(apInfoComplete.StaMacIds))
	bssLoad.ChannelUtilization = 0
	bssLoad.AvailAdmCap = 0
	apInfo.BssLoad = &bssLoad

	var apLocation ApLocation
	var geoLocation GeoLocation
Simon Pastor's avatar
Simon Pastor committed
	if apInfoComplete.ApLocation.Geolocation != nil {
		geoLocation.Lat = apInfoComplete.ApLocation.Geolocation.Lat
		geoLocation.Long = apInfoComplete.ApLocation.Geolocation.Long
Simon Pastor's avatar
Simon Pastor committed
		geoLocation.Datum = 1
Simon Pastor's avatar
Simon Pastor committed
		apLocation.Geolocation = &geoLocation
Simon Pastor's avatar
Simon Pastor committed
		apInfo.ApLocation = &apLocation
	}

Simon Pastor's avatar
Simon Pastor committed
	resp.ApInfoList = append(resp.ApInfoList, apInfo)
func populateApInfoCompleteList(key string, jsonInfo string, response interface{}) error {
	resp := response.(*ApInfoCompleteResp)
	// Retrieve ap info from DB
	var apInfoComplete ApInfoComplete
	err := json.Unmarshal([]byte(jsonInfo), &apInfoComplete)
	if err != nil {
		return err
	}

	resp.ApInfoCompleteList = append(resp.ApInfoCompleteList, apInfoComplete)
	return nil
}

Simon Pastor's avatar
Simon Pastor committed
func apInfoGET(w http.ResponseWriter, r *http.Request) {

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

Simon Pastor's avatar
Simon Pastor committed
	var response ApInfoResp
	//initialise array to make sure Marshal processes it properly if it is empty
	response.ApInfoList = make([]ApInfo, 0)
Simon Pastor's avatar
Simon Pastor committed

	//loop through each AP
	keyName := baseKey + "AP:*"
	err := rc.ForEachJSONEntry(keyName, populateApInfo, &response)
	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

Simon Pastor's avatar
Simon Pastor committed
	jsonResponse, err := json.Marshal(response.ApInfoList)
Simon Pastor's avatar
Simon Pastor committed
	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
Simon Pastor's avatar
Simon Pastor committed
	fmt.Fprintf(w, string(jsonResponse))
}

func populateStaData(key string, jsonInfo string, response interface{}) error {
Simon Pastor's avatar
Simon Pastor committed
	resp := response.(*StaInfoResp)
Simon Pastor's avatar
Simon Pastor committed
	if resp == nil {
		return errors.New("Response not defined")
	}

	// Add STA info to reponse (ignore if not associated to a wifi AP)
	staData := convertJsonToStaData(jsonInfo)
	if staData.StaInfo.ApAssociated != nil {
Simon Pastor's avatar
Simon Pastor committed
		//timeStamp is optional, commenting the code
		//seconds := time.Now().Unix()
		//var timeStamp TimeStamp
		//timeStamp.Seconds = int32(seconds)
		//staInfo.TimeStamp = &timeStamp
Simon Pastor's avatar
Simon Pastor committed
		//do not show an empty object in the response since 0 do not show up in the json
		if staData.StaInfo.StaDataRate != nil {
			if staData.StaInfo.StaDataRate.StaId == nil && staData.StaInfo.StaDataRate.StaLastDataDownlinkRate == 0 && staData.StaInfo.StaDataRate.StaLastDataUplinkRate == 0 {
				staData.StaInfo.StaDataRate = nil
			}
		}
		resp.StaInfoList = append(resp.StaInfoList, *staData.StaInfo)
Simon Pastor's avatar
Simon Pastor committed
	}
	return nil
Simon Pastor's avatar
Simon Pastor committed

/*
func populateStaDataList(key string, jsonInfo string, response interface{}) error {
        resp := response.(*StaDataList)
	var staData StaData
	err := json.Unmarshal([]byte(jsonInfo), &staData)
	if err != nil {
		return err
	}

	resp.StaDataList = append(resp.StaDataList, staData)
	return nil
}
*/
Simon Pastor's avatar
Simon Pastor committed
func staInfoGET(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
Simon Pastor's avatar
Simon Pastor committed
	var response StaInfoResp
	//initialise array to make sure Marshal processes it properly if it is empty
	response.StaInfoList = make([]StaInfo, 0)
	// Loop through each STA
Simon Pastor's avatar
Simon Pastor committed
	keyName := baseKey + "UE:*"
	err := rc.ForEachJSONEntry(keyName, populateStaData, &response)
Simon Pastor's avatar
Simon Pastor committed
	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

Simon Pastor's avatar
Simon Pastor committed
	jsonResponse, err := json.Marshal(response.StaInfoList)
Simon Pastor's avatar
Simon Pastor committed
	if err != nil {
		log.Error(err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(jsonResponse))
}

func createSubscriptionLinkList(subType string) *SubscriptionLinkList {

	subscriptionLinkList := new(SubscriptionLinkList)

Simon Pastor's avatar
Simon Pastor committed
	link := new(SubscriptionLinkListLinks)
	self := new(LinkType)
	self.Href = hostUrl.String() + basePath + "subscriptions"
Simon Pastor's avatar
Simon Pastor committed
	link.Self = self
Simon Pastor's avatar
Simon Pastor committed
	subscriptionLinkList.Links = link

	//loop through all different types of subscription

	mutex.Lock()
	defer mutex.Unlock()

Simon Pastor's avatar
Simon Pastor committed
	if subType == "" || subType == assocStaSubscriptionType {
		//loop through assocSta map
		for _, assocStaSubscriptionInfo := range assocStaSubscriptionInfoMap {
			if assocStaSubscriptionInfo != nil {
				var subscription SubscriptionLinkListSubscription
				subscription.Href = assocStaSubscriptionInfo.Subscription.Links.Self.Href
				subscription.SubscriptionType = ASSOC_STA_SUBSCRIPTION
				subscriptionLinkList.Subscription = append(subscriptionLinkList.Subscription, subscription)
			}
		}
	}
	if subType == "" || subType == staDataRateSubscriptionType {
		//loop through assocSta map
		for _, staDataRateSubscriptionInfo := range staDataRateSubscriptionInfoMap {
			if staDataRateSubscriptionInfo != nil {
				var subscription SubscriptionLinkListSubscription
				subscription.Href = staDataRateSubscriptionInfo.Subscription.Links.Self.Href
				subscription.SubscriptionType = STA_DATA_RATE_SUBSCRIPTION
				subscriptionLinkList.Subscription = append(subscriptionLinkList.Subscription, subscription)
Simon Pastor's avatar
Simon Pastor committed
	//no other maps to go through

	return subscriptionLinkList
}

func subscriptionLinkListSubscriptionsGET(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

Simon Pastor's avatar
Simon Pastor committed
	u, _ := url.Parse(r.URL.String())
	log.Info("url: ", u.RequestURI())
	q := u.Query()
	subType := q.Get("subscription_type")

	validQueryParams := []string{"subscription_type"}
	validQueryParamValues := []string{"assoc_sta", "sta_data_rate", "measure_report"}

	//look for all query parameters to reject if any invalid ones
	found := false
	for queryParam, values := range q {
		found = false
		for _, validQueryParam := range validQueryParams {
			if queryParam == validQueryParam {
				found = true
				break
			}
		}
		if !found {
			log.Error("Query param not valid: ", queryParam)
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		for _, validQueryParamValue := range validQueryParamValues {