summaryrefslogtreecommitdiff
path: root/src/agent/serviceadapter/sa_elements.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/agent/serviceadapter/sa_elements.c')
-rw-r--r--src/agent/serviceadapter/sa_elements.c766
1 files changed, 766 insertions, 0 deletions
diff --git a/src/agent/serviceadapter/sa_elements.c b/src/agent/serviceadapter/sa_elements.c
new file mode 100644
index 0000000..8f38723
--- /dev/null
+++ b/src/agent/serviceadapter/sa_elements.c
@@ -0,0 +1,766 @@
+/*
+ * oma-dm-agent
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*sync-agent*/
+#include <sync_agent.h>
+
+/*dm-agent*/
+#include "common/dm_common.h"
+#include "common/util/util.h"
+#include "serviceadapter/protocolbinder/syncml_def.h"
+#include "serviceadapter/sa_elements.h"
+#include "serviceadapter/sa_elements_internal.h"
+#include "serviceadapter/sa_session_internal.h"
+#include "serviceadapter/sa_command.h"
+#include "mo-handler/dm_mo_common.h"
+
+#ifndef OMADM_AGENT_LOG
+#undef LOG_TAG
+#define LOG_TAG "OMA_DM_SA"
+#endif
+
+DM_ERROR create_hmac(char *auth_name, char *auth_type, char *mac, Session ** session)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((auth_name) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "auth_name is NULL!!");
+ retvm_if((auth_type) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "auth_type is NULL!!");
+ retvm_if((mac) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "mac is NULL!!");
+
+ if ((*session)->reqhmacinfo == NULL) {
+ (*session)->reqhmacinfo = (Hmac *) calloc(1, sizeof(Hmac));
+ if ((*session)->reqhmacinfo == NULL) {
+ ret = COMMON_ERR_ALLOC;
+ goto error;
+ }
+ }
+
+ if ((*session)->reqhmacinfo->username != NULL) {
+ str_free(&(*session)->reqhmacinfo->username);
+ }
+ if ((*session)->reqhmacinfo->authtype != NULL) {
+ str_free(&(*session)->reqhmacinfo->authtype);
+ }
+ if ((*session)->reqhmacinfo->mac != NULL) {
+ str_free(&(*session)->reqhmacinfo->mac);
+ }
+
+ (*session)->reqhmacinfo->username = strdup(auth_name);
+ (*session)->reqhmacinfo->authtype = strdup(auth_type);
+ (*session)->reqhmacinfo->mac = strdup(mac);
+
+ _DEBUG_INFO("user name : %s", auth_name);
+ _DEBUG_INFO("auth type : %s", auth_type);
+ _DEBUG_INFO("mac : %s", mac);
+
+ _DEBUG_INFO(" end : %d", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+ error:
+ _DEBUG_INFO(" end error : %d", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+}
+
+DM_ERROR create_location(const char *locURI, const char *locName, Location ** pLocation)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((locURI) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "locURI is NULL!!");
+ _DEBUG_INFO("start with locURI = %s, locName = %s\n", locURI, locName);
+
+ *pLocation = (Location *) calloc(1, sizeof(Location) + 1);
+ if (*pLocation == NULL) {
+ ret = COMMON_ERR_ALLOC;
+ goto error;
+ }
+ (*pLocation)->locURI = g_strdup(locURI);
+
+ if (locName != NULL) {
+ (*pLocation)->locName = g_strdup(locName);
+ }
+
+ _DEBUG_INFO("end");
+ _EXTERN_FUNC_EXIT;
+ return ret;
+
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+}
+
+Location *dup_location(Location * pLocation)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((pLocation) == NULL, NULL, "pLocation is NULL!!");
+
+ Location *location = NULL;
+ ret = create_location(pLocation->locURI, pLocation->locName, &location);
+ if (ret != DM_OK)
+ goto error;
+
+ _EXTERN_FUNC_EXIT;
+ return location;
+
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return NULL;
+
+}
+
+char *get_location_locname(Location * location)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retvm_if((location) == NULL, NULL, "location is NULL!!");
+
+ _EXTERN_FUNC_EXIT;
+
+ return location->locName;
+}
+
+char *get_location_locuri(Location * location)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retvm_if((location) == NULL, NULL, "location is NULL!!");
+
+ _EXTERN_FUNC_EXIT;
+
+ return location->locURI;
+}
+
+void free_location(Location * loc)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retm_if((loc) == NULL, "loc is NULL!!");
+
+ if (loc->locURI != NULL) {
+ _DEBUG_INFO(" loc->locURI = %s\n", loc->locURI);
+ free(loc->locURI);
+ loc->locURI = NULL;
+ }
+
+ if (loc->locName != NULL) {
+ _DEBUG_INFO(" loc->locName = %s\n", loc->locName);
+ free(loc->locName);
+ loc->locName = NULL;
+ }
+ if (loc != NULL) {
+ free(loc);
+ loc = NULL;
+ }
+ _EXTERN_FUNC_EXIT;
+}
+
+void free_cred(Cred * cred)
+{
+ _EXTERN_FUNC_ENTER;
+
+ if (cred == NULL)
+ return;
+
+ if (cred->data != NULL) {
+ free(cred->data);
+ cred->data = NULL;
+ }
+
+ if (cred->username != NULL) {
+ free(cred->username);
+ cred->username = NULL;
+ }
+
+ if (cred->password != NULL) {
+ free(cred->password);
+ cred->password = NULL;
+ }
+ if (cred != NULL) {
+ free(cred);
+ cred = NULL;
+ }
+
+ _EXTERN_FUNC_EXIT;
+}
+
+DM_ERROR create_auth_cred(char *userName, char *pwd, AuthType authType, FormatType formatType, char *data, Cred ** cred)
+{
+
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((userName) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "userName is NULL!!");
+ retvm_if((pwd) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "pwd is NULL!!");
+
+ _DEBUG_INFO(" user : %s , pwd : %s, authtype : %d,. formattype : %d, cred : %s\n", userName, pwd, authType, formatType, data);
+
+ if (strlen(userName) == 0 || strlen(pwd) == 0) {
+ ret = COMMON_ERR_INTERNAL_NOT_DEFINED;
+ goto error;
+ }
+
+ if (data != NULL) {
+ *cred = (Cred *) calloc(1, sizeof(Cred));
+ if (*cred == NULL) {
+ ret = COMMON_ERR_ALLOC;
+ goto error;
+ }
+
+ (*cred)->type = authType;
+ (*cred)->format = formatType;
+ (*cred)->username = strdup(userName);
+ (*cred)->password = strdup(pwd);
+
+ (*cred)->data = strdup(data);
+ } else if (data == NULL && authType == AUTH_TYPE_HMAC) {
+ /* hmac */
+ } else {
+ /*error */
+ ret = COMMON_ERR_IS_NULL;
+ }
+
+ _EXTERN_FUNC_EXIT;
+ return ret;
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+
+}
+
+Cred *create_credwithdata(AuthType authType, char *data)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((data) == NULL, NULL, "data is NULL!!");
+
+ Cred *cred = (Cred *) calloc(1, sizeof(Cred));
+ if (cred == NULL) {
+ ret = COMMON_ERR_ALLOC;
+ goto error;
+ }
+
+ cred->type = authType;
+ cred->data = strdup(data);
+
+ _EXTERN_FUNC_EXIT;
+ return cred;
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return NULL;
+
+}
+
+void set_credformattype(Cred * cred, FormatType formatType)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retm_if((cred) == NULL, "cred is NULL!!");
+
+ cred->format = formatType;
+
+ _EXTERN_FUNC_EXIT;
+}
+
+DM_ERROR check_server_cred(Cred * hdrCred, Session * session) //Cred *sessionCred)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((hdrCred) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "hdrCred is NULL!!");
+ retvm_if((session) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "session is NULL!!");
+
+ char *server_id;
+ char *server_pwd;
+ char *sourceUrl;
+ char *targetUrl;
+ int isBase64;
+ char *nextNonce;
+ char *authType;
+
+ ret = get_server_dmacc(session->pServer_id, &server_id, &server_pwd, &sourceUrl, &targetUrl, &isBase64, &nextNonce, &authType);
+ if (ret != DM_OK)
+ goto error;
+ _DEBUG_INFO("packet server pw data : %s", hdrCred->data);
+ _DEBUG_INFO("server pw : %s", server_pwd);
+ if (hdrCred->data != NULL) {
+ if (strcmp(hdrCred->data, server_pwd) == 0) {
+ ret = DM_OK;
+ } else {
+ ret = DM_ERR_UNAUTHORIZED;
+ }
+ } else {
+ //do nothing
+ }
+ str_free(&server_id);
+ str_free(&server_pwd);
+ str_free(&sourceUrl);
+ str_free(&targetUrl);
+ str_free(&nextNonce);
+ str_free(&authType);
+
+ _EXTERN_FUNC_EXIT;
+ return ret;
+ error:
+ _DEBUG_INFO(" end error : %d", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+}
+
+DM_ERROR create_chal(FormatType format, AuthType type, char *nextnonce, Chal ** pChal)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retvm_if((nextnonce) == NULL, COMMON_ERR_INTERNAL_NO_MEMORY, "nextnonce is NULL!!");
+ _DEBUG_INFO("start with format = %d, type = %d Nextnonce = %s\n", format, type, nextnonce);
+
+ DM_ERROR ret = DM_OK;
+
+ *pChal = (Chal *) calloc(1, sizeof(Chal));
+ if (*pChal == NULL) {
+ ret = COMMON_ERR_ALLOC;
+ goto error;
+ }
+
+ (*pChal)->format = format;
+ (*pChal)->type = type;
+ if (type == AUTH_TYPE_MD5 || type == AUTH_TYPE_HMAC) {
+ (*pChal)->nonce_plain = g_strdup(nextnonce);
+ }
+
+ _EXTERN_FUNC_EXIT;
+ return ret;
+
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+}
+
+void free_chal(Chal * pChal)
+{
+ _EXTERN_FUNC_ENTER;
+ if (pChal == NULL) {
+ _EXTERN_FUNC_EXIT;
+ return;
+ }
+
+ pChal->format = 0;
+ pChal->type = 0;
+ pChal->nonce_length = 0;
+
+ if (pChal->nonce_plain != NULL) {
+ free(pChal->nonce_plain);
+ pChal->nonce_plain = NULL;
+ }
+
+ if (pChal->nonce_b64 != NULL) {
+ free(pChal->nonce_b64);
+ pChal->nonce_b64 = NULL;
+ }
+
+ if (pChal != NULL) {
+ free(pChal);
+ pChal = NULL;
+ }
+
+ _EXTERN_FUNC_EXIT;
+}
+
+Chal *dup_chal(Chal * pChal)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retvm_if((pChal) == NULL, NULL, "pChal is NULL!!");
+
+ Chal *temp = (Chal *) calloc(1, sizeof(Chal));
+ if (temp == NULL) {
+ _EXTERN_FUNC_EXIT;
+ return 0;
+ }
+
+ temp->type = pChal->type;
+ temp->format = pChal->format;
+
+ if (pChal->nonce_b64 != NULL)
+ temp->nonce_b64 = strdup(pChal->nonce_b64);
+
+ if (pChal->nonce_length != 0)
+ temp->nonce_length = pChal->nonce_length;
+
+ if (pChal->nonce_plain != NULL)
+ temp->nonce_plain = strdup(pChal->nonce_plain);
+
+ _EXTERN_FUNC_EXIT;
+ return temp;
+}
+
+void free_hmac(Hmac * hmac)
+{
+ _EXTERN_FUNC_ENTER;
+
+ if (hmac != NULL) {
+ str_free(&(hmac->authtype));
+ str_free(&(hmac->username));
+ str_free(&(hmac->mac));
+
+ free(hmac);
+ hmac = NULL;
+ }
+
+ _EXTERN_FUNC_EXIT;
+}
+
+AuthType converttoauthtype(char *authType)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retvm_if((authType) == NULL, AUTH_TYPE_UNKNOWN, "authType is NULL!!");
+
+ if (strcmp(authType, ELEMENT_AUTH_BASIC) == 0) {
+ _EXTERN_FUNC_EXIT;
+ return AUTH_TYPE_BASIC;
+ } else if (strcmp(authType, ELEMENT_AUTH_MD5) == 0) {
+ _EXTERN_FUNC_EXIT;
+ return AUTH_TYPE_MD5;
+ } else if (strcmp(authType, ELEMENT_AUTH_HMAC) == 0) {
+ _EXTERN_FUNC_EXIT;
+ return AUTH_TYPE_HMAC;
+ }
+
+ _EXTERN_FUNC_EXIT;
+ return AUTH_TYPE_UNKNOWN;
+}
+
+FormatType converttoformattype(char *formatType)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retvm_if((formatType) == NULL, FORMAT_TYPE_UNKNOWN, "formatType is NULL!!");
+
+ if (strcmp(formatType, ELEMENT_FORMAT_BASE64) == 0) {
+ _EXTERN_FUNC_EXIT;
+ return FORMAT_TYPE_BASE64;
+ }
+
+ _EXTERN_FUNC_EXIT;
+ return FORMAT_TYPE_UNKNOWN;
+}
+
+DM_ERROR construct_Item(char *sourceUrl, const char *format, const char *contenttype, const char *data, unsigned int size, int moreData, Item ** pItem)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((sourceUrl) == NULL, COMMON_ERR_INTERNAL_NOT_DEFINED, "sourceUrl is NULL!!");
+
+ Location *pLocation = NULL;
+
+ ret = create_item_data((char *)data, size, pItem);
+ if (ret != DM_OK)
+ goto error;
+
+ if (contenttype != NULL)
+ (*pItem)->contenttype = g_strdup(contenttype);
+
+ if (format != NULL)
+ (*pItem)->format = g_strdup(format);
+
+ (*pItem)->moreData = moreData;
+
+ ret = create_location(sourceUrl, NULL, &pLocation);
+ if (ret != DM_OK)
+ goto error;
+
+ set_itemsource((*pItem), pLocation);
+
+ _EXTERN_FUNC_EXIT;
+ return ret;
+
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+}
+
+Item *create_Item()
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ Item *item = (Item *) calloc(1, sizeof(Item));
+ if (item == NULL) {
+ ret = COMMON_ERR_ALLOC;
+ goto error;
+ }
+
+ item->dataType = ITEM_UNKNOWN;
+
+ _EXTERN_FUNC_EXIT;
+ return item;
+
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return NULL;
+}
+
+DM_ERROR create_item_data(char *data, unsigned int size, Item ** pItem)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ (*pItem) = create_Item();
+ if ((*pItem) == NULL) {
+ ret = COMMON_ERR_INTERNAL_NO_MEMORY;
+ goto error;
+ }
+
+ if (data != NULL) {
+ (*pItem)->private.data = strdup(data);
+ }
+
+ (*pItem)->dataType = ITEM_DATA;
+ (*pItem)->size = size;
+
+ _EXTERN_FUNC_EXIT;
+ return ret;
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+}
+
+void set_itemtarget(Item * item, Location * target)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retm_if((item) == NULL, "item is NULL!!");
+
+ item->target = target;
+
+ _EXTERN_FUNC_EXIT;
+}
+
+void set_itemsource(Item * item, Location * source)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retm_if((item) == NULL, "item is NULL!!");
+
+ item->source = source;
+
+ _EXTERN_FUNC_EXIT;
+}
+
+void free_Item(Item * item)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retm_if((item) == NULL, "item is NULL!!");
+
+ if (item->source != NULL) {
+ free_location(item->source);
+ item->source = NULL;
+ }
+
+ if (item->target != NULL) {
+ free_location(item->target);
+ item->target = NULL;
+ }
+
+ switch (item->dataType) {
+ case ITEM_DATA:
+ if (item->private.data != NULL) {
+ free(item->private.data);
+ item->private.data = NULL;
+ }
+ break;
+ case ITEM_UNKNOWN:
+ //noting to free
+ break;
+ }
+
+ if (item->contenttype != NULL) {
+ free(item->contenttype);
+ item->contenttype = NULL;
+ }
+
+ if (item->format != NULL) {
+ free(item->format);
+ item->format = NULL;
+ }
+
+ free(item);
+ item = NULL;
+
+ _EXTERN_FUNC_EXIT;
+}
+
+DM_ERROR create_syncml(SyncHdr * syncHdr, GList * status, GList * commands, int isFinal, SyncML ** pSyncML)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((syncHdr) == NULL, COMMON_ERR_INTERNAL_NOT_DEFINED, "syncHdr is NULL!!");
+
+ *pSyncML = (SyncML *) calloc(1, sizeof(SyncML));
+ if (*pSyncML == NULL) {
+ ret = COMMON_ERR_ALLOC;
+ goto error;
+ }
+
+ (*pSyncML)->hdr = syncHdr;
+ (*pSyncML)->status = status;
+ (*pSyncML)->commands = commands;
+ (*pSyncML)->final = isFinal;
+
+ _EXTERN_FUNC_EXIT;
+ return ret;
+ error:
+ _DEBUG_INFO(" error : %d", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+
+}
+
+void free_syncml(SyncML * syncML)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retm_if((syncML) == NULL, "syncML is NULL!!");
+
+ free_synchdr(syncML->hdr);
+ syncML->hdr = NULL;
+
+ free_statuses(syncML->status);
+ syncML->status = NULL;
+
+ free_commands(syncML->commands);
+ syncML->commands = NULL;
+
+ if (syncML != NULL) {
+ free(syncML);
+ syncML = NULL;
+ }
+
+ _EXTERN_FUNC_EXIT;
+}
+
+DM_ERROR create_syncml_hdr(Session * session, SyncHdr ** pSyncHdr)
+{
+ _EXTERN_FUNC_ENTER;
+
+ DM_ERROR ret = DM_OK;
+
+ retvm_if((session) == NULL, COMMON_ERR_INTERNAL_NOT_DEFINED, "session is NULL!!");
+
+ if (session->protocolVersion == VERSION_UNKNOWN) {
+ ret = COMMON_ERR_INTERNAL_NOT_DEFINED;
+ goto error;
+ }
+
+ if (session->protocolType != PROTOCOL_TYPE_DM) {
+ ret = COMMON_ERR_INTERNAL_NOT_DEFINED;
+ goto error;
+ }
+
+ if (session->source == NULL) {
+ ret = COMMON_ERR_INTERNAL_NOT_DEFINED;
+ goto error;
+ }
+
+ if (session->target == NULL) {
+ ret = COMMON_ERR_INTERNAL_NOT_DEFINED;
+ goto error;
+ }
+
+ *pSyncHdr = (SyncHdr *) calloc(1, sizeof(SyncHdr));
+
+ if (*pSyncHdr == NULL) {
+ ret = COMMON_ERR_ALLOC;
+ goto error;
+ }
+
+ (*pSyncHdr)->version = session->protocolVersion;
+ (*pSyncHdr)->protocol = session->protocolType;
+ (*pSyncHdr)->target = session->target; //don't free free in session free section
+ (*pSyncHdr)->source = session->source; //don't free free in session free section
+
+ if (session->cred != NULL)
+ (*pSyncHdr)->cred = session->cred; //don't free free in session free section
+
+ (*pSyncHdr)->sessionID = strdup(session->sessionID); //free
+ (*pSyncHdr)->messageID = ++session->msgID;
+
+ (*pSyncHdr)->maxmsgsize = session->sourceMaxMsgSize;
+ (*pSyncHdr)->maxobjsize = session->sourceMaxObjSize;
+
+ _EXTERN_FUNC_EXIT;
+ return ret;
+
+ error:
+ _DEBUG_INFO(" error : %d\n", ret);
+ _EXTERN_FUNC_EXIT;
+ return ret;
+}
+
+void free_synchdr(SyncHdr * syncHdr)
+{
+ _EXTERN_FUNC_ENTER;
+
+ retm_if((syncHdr) == NULL, "syncHdr is NULL!!");
+
+ if (syncHdr->sessionID != NULL) {
+ free(syncHdr->sessionID);
+ syncHdr->sessionID = NULL;
+ }
+
+ if (syncHdr->responseURI != NULL) {
+ free(syncHdr->responseURI);
+ syncHdr->responseURI = NULL;
+ }
+
+ syncHdr->source = NULL;
+ syncHdr->target = NULL;
+ syncHdr->cred = NULL;
+
+ if (syncHdr != NULL) {
+ free(syncHdr);
+ syncHdr = NULL;
+ }
+
+ _EXTERN_FUNC_EXIT;
+}