*
******************************************************************/
+#include "iotivity_config.h"
#include "caadapterutils.h"
#include <string.h>
#include "oic_malloc.h"
#include <errno.h>
-#ifndef WITH_ARDUINO
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
+#endif
+#if defined(HAVE_WINSOCK2_H) && defined(HAVE_WS2TCPIP_H)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <jni.h>
#endif
-#define CA_ADAPTER_UTILS_TAG "CA_ADAPTER_UTILS"
+#define CA_ADAPTER_UTILS_TAG "OIC_CA_ADAP_UTILS"
#ifdef __ANDROID__
/**
* @brief pointer to store context for android callback interface
*/
static jobject g_Context = NULL;
+static jobject g_Activity = NULL;
#endif
-void CALogPDUData(coap_pdu_t *pdu)
-{
- VERIFY_NON_NULL_VOID(pdu, CA_ADAPTER_UTILS_TAG, "pdu");
- OIC_LOG_V(DEBUG, CA_ADAPTER_UTILS_TAG, "PDU Maker - payload : %s", pdu->data);
-
- OIC_LOG_V(DEBUG, CA_ADAPTER_UTILS_TAG, "PDU Maker - type : %d", pdu->hdr->type);
-
- OIC_LOG_V(DEBUG, CA_ADAPTER_UTILS_TAG, "PDU Maker - code : %d", pdu->hdr->code);
-
- OIC_LOG_V(DEBUG, CA_ADAPTER_UTILS_TAG, "PDU Maker - id : %d", pdu->hdr->id);
-
- OIC_LOG_V(DEBUG, CA_ADAPTER_UTILS_TAG, "PDU Maker - token : %s", pdu->hdr->token);
-}
-CAEndpoint_t *CAAdapterCreateEndpoint(CATransportFlags_t flags,
- CATransportAdapter_t adapter,
- const char *address,
- uint16_t port)
-{
- VERIFY_NON_NULL_RET(address, CA_ADAPTER_UTILS_TAG, "Endpoint is NULL", NULL);
- CAEndpoint_t *info = (CAEndpoint_t *)OICCalloc(1, sizeof(CAEndpoint_t));
- if (NULL == info)
- {
- OIC_LOG(ERROR, CA_ADAPTER_UTILS_TAG, "Memory allocation failed !");
- return NULL;
- }
-
- OICStrcpy(info->addr, sizeof(info->addr), address);
- info->addr[MAX_ADDR_STR_SIZE_CA - 1] = '\0';
- info->flags = flags;
- info->adapter = adapter;
- info->port = port;
-
- return info;
-}
-
-CAEndpoint_t *CAAdapterCreateLocalEndpoint(CATransportFlags_t flags,
- CATransportAdapter_t adapter, const char *address)
-{
- return CAAdapterCreateEndpoint(flags, adapter, address, 0);
-}
-
-CAResult_t CACreateEndpoint(CATransportFlags_t flags,
- CATransportAdapter_t adapter,
- const CAURI_t uri,
- uint16_t port,
- CAEndpoint_t **object)
-{
- VERIFY_NON_NULL_RET(object, CA_ADAPTER_UTILS_TAG, "Endpoint is NULL", CA_STATUS_INVALID_PARAM);
- CAEndpoint_t *endpoint = CAAdapterCreateEndpoint(flags, adapter, uri, port);
- if (!endpoint)
- {
- return CA_STATUS_FAILED;
- }
- *object = endpoint;
- return CA_STATUS_OK;
-}
-
-
-CAEndpoint_t *CAAdapterCloneEndpoint(const CAEndpoint_t *endpoint)
-{
- VERIFY_NON_NULL_RET(endpoint, CA_ADAPTER_UTILS_TAG, "endpoint is NULL", NULL);
-
- CAEndpoint_t *info = (CAEndpoint_t *)OICCalloc(1, sizeof (CAEndpoint_t));
- if (NULL == info)
- {
- OIC_LOG(ERROR, CA_ADAPTER_UTILS_TAG, "Memory allocation failed !");
- return NULL;
- }
- *info = *endpoint;
-
- return info;
-}
-
-void CAAdapterFreeEndpoint(CAEndpoint_t *remoteEndpoint)
-{
- OICFree(remoteEndpoint);
-}
-
+#ifdef WITH_ARDUINO
CAResult_t CAParseIPv4AddressInternal(const char *ipAddrStr, uint8_t *ipAddr,
size_t ipAddrLen, uint16_t *port)
{
return CA_STATUS_FAILED;
}
-bool CAAdapterIsSameSubnet(const char *ipAddress1, const char *ipAddress2, const char *netMask)
-{
- VERIFY_NON_NULL_RET(ipAddress1, CA_ADAPTER_UTILS_TAG, "First address", false);
- VERIFY_NON_NULL_RET(ipAddress2, CA_ADAPTER_UTILS_TAG, "Second address", false);
- VERIFY_NON_NULL_RET(netMask, CA_ADAPTER_UTILS_TAG, "netMask", false);
-
- uint8_t ipList1[IPV4_ADDR_ONE_OCTECT_LEN] = { 0 };
- uint8_t ipList2[IPV4_ADDR_ONE_OCTECT_LEN] = { 0 };
- uint8_t maskList[IPV4_ADDR_ONE_OCTECT_LEN] = { 0 };
- CAResult_t ret = CA_STATUS_OK;
-
- /* Local Loopback Address */
- if (0 == strncmp(ipAddress1, "127.", 4) || 0 == strncmp(ipAddress2, "127.", 4))
- {
- return true;
- }
-
- uint16_t parsedPort = 0;
- ret = CAParseIPv4AddressInternal(ipAddress1, ipList1, sizeof(ipList1), &parsedPort);
- if (ret != CA_STATUS_OK)
- {
- OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG, "First ip address parse fail %d", ret);
- return false;
- }
-
- ret = CAParseIPv4AddressInternal(ipAddress2, ipList2, sizeof(ipList2), &parsedPort);
- if (ret != CA_STATUS_OK)
- {
- OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG, "Second ip address parse fail %d", ret);
- return false;
- }
-
- ret = CAParseIPv4AddressInternal(netMask, maskList, sizeof(maskList), &parsedPort);
- if (ret != CA_STATUS_OK)
- {
- OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG, "Net mask parse fail %d", ret);
- return false;
- }
-
- return ((ipList1[0] & maskList[0]) == (ipList2[0] & maskList[0])) && ((ipList1[1] & maskList[1])
- == (ipList2[1] & maskList[1]))
- && ((ipList1[2] & maskList[2]) == (ipList2[2] & maskList[2]))
- && ((ipList1[3] & maskList[3]) == (ipList2[3] & maskList[3]));
-}
-
-bool CAIsMulticastServerStarted(const u_arraylist_t *serverInfoList, const char *ipAddress,
- const char *multicastAddress, uint16_t port)
-{
- VERIFY_NON_NULL_RET(serverInfoList, CA_ADAPTER_UTILS_TAG, "serverInfoList is null", false);
- VERIFY_NON_NULL_RET(ipAddress, CA_ADAPTER_UTILS_TAG, "ipAddress is null", false);
- VERIFY_NON_NULL_RET(multicastAddress, CA_ADAPTER_UTILS_TAG, "multicastAddress is null", false);
-
- uint32_t listLength = u_arraylist_length(serverInfoList);
- for (uint32_t listIndex = 0; listIndex < listLength; listIndex++)
- {
- CAServerInfo_t *info = (CAServerInfo_t *) u_arraylist_get(serverInfoList, listIndex);
- if (!info)
- {
- OIC_LOG(ERROR, CA_ADAPTER_UTILS_TAG, "Info is NULL");
- return false;
- }
-
- if (info->isMulticastServer && (strncmp(info->endpoint.addr, multicastAddress,
- strlen(multicastAddress)) == 0)
- && (info->endpoint.port == port) && (strncmp(info->ifAddr, ipAddress, strlen(ipAddress)) == 0))
- {
- return info->isServerStarted;
- }
- }
- return false;
-}
-
-bool CAIsUnicastServerStarted(const u_arraylist_t *serverInfoList, const char *ipAddress,
- uint16_t port)
-{
- VERIFY_NON_NULL_RET(serverInfoList, CA_ADAPTER_UTILS_TAG, "serverInfoList is null", false);
- VERIFY_NON_NULL_RET(ipAddress, CA_ADAPTER_UTILS_TAG, "ipAddress is null", false);
-
- uint32_t listLength = u_arraylist_length(serverInfoList);
- for (uint32_t listIndex = 0; listIndex < listLength; listIndex++)
- {
- CAServerInfo_t *info = (CAServerInfo_t *) u_arraylist_get(serverInfoList, listIndex);
- if (!info)
- {
- continue;
- }
-
- if (!info->isMulticastServer && (strncmp(info->endpoint.addr, ipAddress,
- strlen(ipAddress)) == 0)
- && (info->endpoint.port == port))
- {
- return info->isServerStarted;
- }
- }
- return false;
-}
-
-uint16_t CAGetServerPort(const u_arraylist_t *serverInfoList, const char *ipAddress, bool isSecured)
-{
- VERIFY_NON_NULL_RET(serverInfoList, CA_ADAPTER_UTILS_TAG, "serverInfoList is null", 0);
- VERIFY_NON_NULL_RET(ipAddress, CA_ADAPTER_UTILS_TAG, "ipAddress is null", 0);
-
- uint32_t listLength = u_arraylist_length(serverInfoList);
- for (uint32_t listIndex = 0; listIndex < listLength; listIndex++)
- {
- CAServerInfo_t *info = (CAServerInfo_t *) u_arraylist_get(serverInfoList, listIndex);
- if (!info)
- {
- continue;
- }
- bool ifSecured = info->endpoint.flags & CA_SECURE;
- if ((strncmp(info->endpoint.addr, ipAddress, strlen(ipAddress)) == 0) &&
- (ifSecured == isSecured))
- {
- return info->endpoint.port;
- }
- }
-
- return 0;
-}
-
-int CAGetSocketFdForUnicastServer(const u_arraylist_t *serverInfoList,
- bool isMulticast, const CAEndpoint_t *endpoint)
-{
- VERIFY_NON_NULL_RET(serverInfoList, CA_ADAPTER_UTILS_TAG, "serverInfoList is null", -1);
- VERIFY_NON_NULL_RET(endpoint, CA_ADAPTER_UTILS_TAG, "endpoint is null", -1);
-
- bool isSecured = (endpoint->flags & CA_SECURE) != 0;
-
- uint32_t listLength = u_arraylist_length(serverInfoList);
-
- for (uint32_t listIndex = 0; listIndex < listLength; listIndex++)
- {
- CAServerInfo_t *info = (CAServerInfo_t *) u_arraylist_get(serverInfoList, listIndex);
- if (!info)
- {
- continue;
- }
-
- if (!CAAdapterIsSameSubnet(info->endpoint.addr, endpoint->addr, info->subNetMask))
- {
- continue;
- }
-
- bool ifSecured = info->endpoint.flags & CA_SECURE;
- if (!info->isMulticastServer && (ifSecured == isSecured))
- {
- OIC_LOG_V(DEBUG, CA_ADAPTER_UTILS_TAG,
- "CAGetSocketFdForServer found socket [%d]", info->socketFd);
- return info->socketFd;
- }
-
- }
-
- OIC_LOG(ERROR, CA_ADAPTER_UTILS_TAG,
- "CAGetSocketFdForServer socket fd is not found");
- return -1;
-}
-
-CAResult_t CAAddServerInfo(u_arraylist_t *serverInfoList, CAServerInfo_t *info)
-{
- VERIFY_NON_NULL(serverInfoList, CA_ADAPTER_UTILS_TAG, "serverInfoList is null");
- VERIFY_NON_NULL(info, CA_ADAPTER_UTILS_TAG, "info is null");
-
- CAResult_t result = u_arraylist_add(serverInfoList, (void *) info);
- if (CA_STATUS_OK != result)
- {
- OIC_LOG(ERROR, CA_ADAPTER_UTILS_TAG, "u_arraylist_add failed!");
- }
- return result;
-}
-
-void CARemoveServerInfo(u_arraylist_t *serverInfoList, int sockFd)
-{
- VERIFY_NON_NULL_VOID(serverInfoList, CA_ADAPTER_UTILS_TAG, "serverInfoList is null");
-
- uint32_t listLength = u_arraylist_length(serverInfoList);
- for (uint32_t listIndex = 0; listIndex < listLength;)
- {
- CAServerInfo_t *info = (CAServerInfo_t *) u_arraylist_get(serverInfoList, listIndex);
- if (!info)
- {
- listIndex++;
- continue;
- }
-
- if (info->socketFd == sockFd)
- {
- if (u_arraylist_remove(serverInfoList, listIndex))
- {
- OICFree(info);
- listLength--;
- }
- else
- {
- OIC_LOG(ERROR, CA_ADAPTER_UTILS_TAG, "u_arraylist_remove failed!");
- break;
- }
- }
- else
- {
- listIndex++;
- }
- }
-}
-
-void CAClearNetInterfaceInfoList(u_arraylist_t *infoList)
-{
- uint32_t listLength = u_arraylist_length(infoList);
- for (uint32_t listIndex = 0; listIndex < listLength; listIndex++)
- {
- CANetInfo_t *netInfo = (CANetInfo_t *) u_arraylist_get(infoList, listIndex);
- if (!netInfo)
- {
- continue;
- }
- OICFree(netInfo);
- }
- u_arraylist_free(&infoList);
-}
-
-void CAClearServerInfoList(u_arraylist_t *serverInfoList)
-{
- uint32_t listLength = u_arraylist_length(serverInfoList);
- for (uint32_t listIndex = 0; listIndex < listLength; listIndex++)
- {
- CAServerInfo_t *serverInfo = (CAServerInfo_t *) u_arraylist_get(serverInfoList, listIndex);
- if (!serverInfo)
- {
- continue;
- }
- OICFree(serverInfo);
- }
- u_arraylist_free(&serverInfoList);
-}
-
-#ifndef WITH_ARDUINO
+#else // not with_arduino
/*
* These two conversion functions return void because errors can't happen
* (because of NI_NUMERIC), and there's nothing to do if they do happen.
*/
-void CAConvertAddrToName(const struct sockaddr_storage *sockAddr, char *host, uint16_t *port)
+void CAConvertAddrToName(const struct sockaddr_storage *sockAddr, socklen_t sockAddrLen,
+ char *host, uint16_t *port)
{
VERIFY_NON_NULL_VOID(sockAddr, CA_ADAPTER_UTILS_TAG, "sockAddr is null");
VERIFY_NON_NULL_VOID(host, CA_ADAPTER_UTILS_TAG, "host is null");
VERIFY_NON_NULL_VOID(port, CA_ADAPTER_UTILS_TAG, "port is null");
int r = getnameinfo((struct sockaddr *)sockAddr,
- sizeof (struct sockaddr_storage),
- host, CA_IPADDR_SIZE,
+ sockAddrLen,
+ host, MAX_ADDR_STR_SIZE_CA,
NULL, 0,
NI_NUMERICHOST|NI_NUMERICSERV);
if (r)
{
+#if defined(EAI_SYSTEM)
if (EAI_SYSTEM == r)
{
OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG,
- "getaddrinfo failed: errno %s", strerror(errno));
+ "getnameinfo failed: errno %s", strerror(errno));
}
else
{
OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG,
- "getaddrinfo failed: %s", gai_strerror(r));
+ "getnameinfo failed: %s", gai_strerror(r));
}
+#elif defined(_WIN32)
+ OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG,
+ "getnameinfo failed: errno %i", WSAGetLastError());
+#else
+ OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG,
+ "getnameinfo failed: %s", gai_strerror(r));
+#endif
return;
}
*port = ntohs(((struct sockaddr_in *)sockAddr)->sin_port); // IPv4 and IPv6
VERIFY_NON_NULL_VOID(host, CA_ADAPTER_UTILS_TAG, "host is null");
VERIFY_NON_NULL_VOID(sockaddr, CA_ADAPTER_UTILS_TAG, "sockaddr is null");
- struct addrinfo *addrs;
- struct addrinfo hints = { 0 };
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_flags = AI_NUMERICHOST;
+ struct addrinfo *addrs = NULL;
+ struct addrinfo hints = { .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_flags = AI_NUMERICHOST };
int r = getaddrinfo(host, NULL, &hints, &addrs);
if (r)
{
+ if (NULL != addrs)
+ {
+ freeaddrinfo(addrs);
+ }
+#if defined(EAI_SYSTEM)
if (EAI_SYSTEM == r)
{
OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG,
OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG,
"getaddrinfo failed: %s", gai_strerror(r));
}
+#elif defined(_WIN32)
+ OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG,
+ "getaddrinfo failed: errno %i", WSAGetLastError());
+#else
+ OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG,
+ "getaddrinfo failed: %s", gai_strerror(r));
+#endif
return;
}
// assumption: in this case, getaddrinfo will only return one addrinfo
if (!context)
{
- OIC_LOG(DEBUG, CA_ADAPTER_UTILS_TAG, "context is null");
-
+ OIC_LOG(ERROR, CA_ADAPTER_UTILS_TAG, "context is null");
+ return;
}
- g_Context = (*env)->NewGlobalRef(env, context);
+ if (!g_Context)
+ {
+ g_Context = (*env)->NewGlobalRef(env, context);
+ }
+ else
+ {
+ OIC_LOG(INFO, CA_ADAPTER_UTILS_TAG, "context is already set");
+ }
}
void CANativeJNISetJavaVM(JavaVM *jvm)
{
return g_jvm;
}
+
+void CANativeSetActivity(JNIEnv *env, jobject activity)
+{
+ OIC_LOG_V(DEBUG, CA_ADAPTER_UTILS_TAG, "CANativeSetActivity");
+
+ if (!activity)
+ {
+ OIC_LOG(ERROR, CA_ADAPTER_UTILS_TAG, "activity is null");
+ return;
+ }
+
+ if (!g_Activity)
+ {
+ g_Activity = (*env)->NewGlobalRef(env, activity);
+ }
+ else
+ {
+ OIC_LOG(INFO, CA_ADAPTER_UTILS_TAG, "activity is already set");
+ }
+}
+
+jobject *CANativeGetActivity()
+{
+ return g_Activity;
+}
+
+jmethodID CAGetJNIMethodID(JNIEnv *env, const char* className,
+ const char* methodName,
+ const char* methodFormat)
+{
+ VERIFY_NON_NULL_RET(env, CA_ADAPTER_UTILS_TAG, "env", NULL);
+ VERIFY_NON_NULL_RET(className, CA_ADAPTER_UTILS_TAG, "className", NULL);
+ VERIFY_NON_NULL_RET(methodName, CA_ADAPTER_UTILS_TAG, "methodName", NULL);
+ VERIFY_NON_NULL_RET(methodFormat, CA_ADAPTER_UTILS_TAG, "methodFormat", NULL);
+
+ jclass jni_cid = (*env)->FindClass(env, className);
+ if (!jni_cid)
+ {
+ OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG, "jni_cid [%s] is null", className);
+ CACheckJNIException(env);
+ return NULL;
+ }
+
+ jmethodID jni_midID = (*env)->GetMethodID(env, jni_cid, methodName, methodFormat);
+ if (!jni_midID)
+ {
+ OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG, "jni_midID [%s] is null", methodName);
+ CACheckJNIException(env);
+ (*env)->DeleteLocalRef(env, jni_cid);
+ return NULL;
+ }
+
+ (*env)->DeleteLocalRef(env, jni_cid);
+ return jni_midID;
+}
+
+bool CACheckJNIException(JNIEnv *env)
+{
+ if ((*env)->ExceptionCheck(env))
+ {
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ return true;
+ }
+ return false;
+}
+
+void CADeleteGlobalReferences(JNIEnv *env)
+{
+ if (g_Context)
+ {
+ (*env)->DeleteGlobalRef(env, g_Context);
+ g_Context = NULL;
+ }
+
+ if (g_Activity)
+ {
+ (*env)->DeleteGlobalRef(env, g_Activity);
+ g_Activity = NULL;
+ }
+}
#endif
+#ifndef WITH_ARDUINO
+void CALogAdapterStateInfo(CATransportAdapter_t adapter, CANetworkStatus_t state)
+{
+ OIC_LOG(DEBUG, CA_ADAPTER_UTILS_TAG, "CALogAdapterStateInfo");
+ OIC_LOG(DEBUG, ANALYZER_TAG, "=================================================");
+ CALogAdapterTypeInfo(adapter);
+ if (CA_INTERFACE_UP == state)
+ {
+ OIC_LOG(INFO, ANALYZER_TAG, "adapter status is changed to CA_INTERFACE_UP");
+ }
+ else
+ {
+ OIC_LOG(INFO, ANALYZER_TAG, "adapter status is changed to CA_INTERFACE_DOWN");
+ }
+ OIC_LOG(DEBUG, ANALYZER_TAG, "=================================================");
+}
+
+void CALogSendStateInfo(CATransportAdapter_t adapter,
+ const char *addr, uint16_t port, ssize_t sentLen,
+ bool isSuccess, const char* message)
+{
+ OIC_LOG(DEBUG, CA_ADAPTER_UTILS_TAG, "CALogSendStateInfo");
+ OIC_LOG(DEBUG, ANALYZER_TAG, "=================================================");
+
+ if (true == isSuccess)
+ {
+ OIC_LOG_V(INFO, ANALYZER_TAG, "Send Success, sent length = [%d]", sentLen);
+ }
+ else
+ {
+ OIC_LOG_V(INFO, ANALYZER_TAG, "Send Failure, error message = [%s]",
+ message != NULL ? message : "no message");
+ }
+
+ CALogAdapterTypeInfo(adapter);
+ OIC_LOG_V(INFO, ANALYZER_TAG, "Address = [%s]:[%d]", addr, port);
+ OIC_LOG(DEBUG, ANALYZER_TAG, "=================================================");
+
+ // samsung log
+ if (true == isSuccess)
+ {
+ OIC_LOG_V(INFO, CA_ADAPTER_UTILS_TAG, "| Analyzer(Retail) | %02x", 1);
+ }
+ else
+ {
+ OIC_LOG_V(INFO, CA_ADAPTER_UTILS_TAG, "| Analyzer(Retail) | %02x", 0);
+ }
+}
+
+void CALogAdapterTypeInfo(CATransportAdapter_t adapter)
+{
+ switch(adapter)
+ {
+ case CA_ADAPTER_IP:
+ OIC_LOG(INFO, ANALYZER_TAG, "Transport Type = [OC_ADAPTER_IP]");
+ break;
+ case CA_ADAPTER_TCP:
+ OIC_LOG(INFO, ANALYZER_TAG, "Transport Type = [OC_ADAPTER_TCP]");
+ break;
+ case CA_ADAPTER_GATT_BTLE:
+ OIC_LOG(INFO, ANALYZER_TAG, "Transport Type = [OC_ADAPTER_GATT_BTLE]");
+ break;
+ case CA_ADAPTER_RFCOMM_BTEDR:
+ OIC_LOG(INFO, ANALYZER_TAG, "Transport Type = [OC_ADAPTER_RFCOMM_BTEDR]");
+ break;
+ default:
+ OIC_LOG_V(INFO, ANALYZER_TAG, "Transport Type = [%d]", adapter);
+ break;
+ }
+}
+#endif