1 /******************************************************************
3 * Copyright 2015 Samsung Electronics All Rights Reserved.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 *****************************************************************/
27 #include "platform_features.h"
30 #include "oic_malloc.h"
31 #include "oic_string.h"
32 #include "ocprovisioningmanager.h"
33 #include "oxmjustworks.h"
34 #include "oxmrandompin.h"
35 #include "securevirtualresourcetypes.h"
36 #include "srmutility.h"
44 // declaration(s) for provisioning client using C-level provisioning API
45 // user input definition for main loop on provisioning client
46 #define _10_DISCOV_ALL_DEVS_ 10
47 #define _11_DISCOV_UNOWN_DEVS_ 11
48 #define _12_DISCOV_OWN_DEVS_ 12
49 #define _20_REGIST_DEVS_ 20
50 #define _30_PROVIS_PAIR_DEVS_ 30
51 #define _31_PROVIS_CRED_ 31
52 #define _32_PROVIS_ACL_ 32
53 #define _33_PROVIS_DP_ 33
54 #define _34_CHECK_LINK_STATUS_ 34
55 #define _40_UNLINK_PAIR_DEVS_ 40
56 #define _50_REMOVE_SELEC_DEV_ 50
57 #define _60_GET_CRED_ 60
58 #define _61_GET_ACL_ 61
59 #define _99_EXIT_PRVN_CLT_ 99
61 #define ACL_RESRC_MAX_NUM 16
62 #define ACL_RESRC_ARRAY_SIZE 3 //This value is used only for sample (not OCF spec)
63 #define ACL_RESRC_MAX_LEN 128
64 #define ACL_PEMISN_CNT 5
65 #define DISCOVERY_TIMEOUT 10 // 10 sec
66 #define CALLBACK_TIMEOUT 60 // 1 min
67 #define TAG "provisioningclient"
69 static const char* ACL_PEMISN[5] = {"CREATE", "READ", "WRITE", "DELETE", "NOTIFY"};
70 static const char* SVR_DB_FILE_NAME = "oic_svr_db_client.dat";
71 // '_' for separaing from the same constant variable in |srmresourcestrings.c|
72 static const char* PRVN_DB_FILE_NAME = "oic_prvn_mng.db";
73 static const OicSecPrm_t SUPPORTED_PRMS[1] =
78 // |g_ctx| means provision manager application context and
79 // the following, includes |un/own_list|, could be variables, which |g_ctx| has,
80 // for accessing all function(s) for these, they are declared on global domain
81 static const char* g_ctx = "Provision Manager Client Application Context";
82 static char* g_svr_fname;
83 static char* g_prvn_fname;
84 static OCProvisionDev_t* g_own_list;
85 static OCProvisionDev_t* g_unown_list;
87 static int g_unown_cnt;
90 // function declaration(s) for calling them before implementing
91 static OicSecAcl_t* createAcl(const int);
92 static OicSecPdAcl_t* createPdAcl(const int);
93 static OCProvisionDev_t* getDevInst(const OCProvisionDev_t*, const int);
94 static int printDevList(const OCProvisionDev_t*);
95 static size_t printUuidList(const OCUuidList_t*);
96 static int printResultList(const OCProvisionResult_t*, const int);
97 static void printUuid(const OicUuid_t*);
98 static FILE* fopen_prvnMng(const char*, const char*);
99 static int waitCallbackRet(void);
100 static int selectTwoDiffNum(int*, int*, const int, const char*);
102 // callback function(s) for provisioning client using C-level provisioning API
103 static void ownershipTransferCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
107 OIC_LOG_V(INFO, TAG, "Ownership Transfer SUCCEEDED - ctx: %s", (char*) ctx);
111 OIC_LOG_V(ERROR, TAG, "Ownership Transfer FAILED - ctx: %s", (char*) ctx);
112 printResultList((const OCProvisionResult_t*) arr, nOfRes);
117 static void provisionPairwiseCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
121 OIC_LOG_V(INFO, TAG, "Provision Pairwise SUCCEEDED - ctx: %s", (char*) ctx);
125 OIC_LOG_V(ERROR, TAG, "Provision Pairwise FAILED - ctx: %s", (char*) ctx);
126 printResultList((const OCProvisionResult_t*) arr, nOfRes);
131 static void provisionCredCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
135 OIC_LOG_V(INFO, TAG, "Provision Credential SUCCEEDED - ctx: %s", (char*) ctx);
139 OIC_LOG_V(ERROR, TAG, "Provision Credential FAILED - ctx: %s", (char*) ctx);
140 printResultList((const OCProvisionResult_t*) arr, nOfRes);
145 static void provisionAclCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
149 OIC_LOG_V(INFO, TAG, "Provision ACL SUCCEEDED - ctx: %s", (char*) ctx);
153 OIC_LOG_V(ERROR, TAG, "Provision ACL FAILED - ctx: %s", (char*) ctx);
154 printResultList((const OCProvisionResult_t*) arr, nOfRes);
159 static void getCredCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
163 OIC_LOG_V(INFO, TAG, "getCredCB SUCCEEDED - ctx: %s", (char*) ctx);
167 OIC_LOG_V(ERROR, TAG, "getCredCB FAILED - ctx: %s", (char*) ctx);
168 printResultList((const OCProvisionResult_t*) arr, nOfRes);
173 static void getAclCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
177 OIC_LOG_V(INFO, TAG, "getAclCB SUCCEEDED - ctx: %s", (char*) ctx);
181 OIC_LOG_V(ERROR, TAG, "getAclCB FAILED - ctx: %s", (char*) ctx);
182 printResultList((const OCProvisionResult_t*) arr, nOfRes);
187 static void provisionDPCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
191 OIC_LOG_V(INFO, TAG, "Provision Direct-Pairing SUCCEEDED - ctx: %s", (char*) ctx);
195 OIC_LOG_V(ERROR, TAG, "Provision Direct-Pairing FAILED - ctx: %s", (char*) ctx);
196 printResultList((const OCProvisionResult_t*) arr, nOfRes);
201 static void unlinkDevicesCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
205 OIC_LOG_V(INFO, TAG, "Unlink Devices SUCCEEDED - ctx: %s", (char*) ctx);
209 OIC_LOG_V(ERROR, TAG, "Unlink Devices FAILED - ctx: %s", (char*) ctx);
210 printResultList((const OCProvisionResult_t*) arr, nOfRes);
215 static void removeDeviceCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
219 OIC_LOG_V(INFO, TAG, "Remove Device SUCCEEDED - ctx: %s", (char*) ctx);
223 OIC_LOG_V(ERROR, TAG, "Remove Device FAILED - ctx: %s", (char*) ctx);
224 printResultList((const OCProvisionResult_t*) arr, nOfRes);
229 static void inputPinCB(char* pin, size_t len)
231 if(!pin || OXM_RANDOM_PIN_SIZE>=len)
233 OIC_LOG(ERROR, TAG, "inputPinCB invalid parameters");
237 printf(" > INPUT PIN: ");
238 for(int ret=0; 1!=ret; )
240 ret = scanf("%8s", pin);
241 for( ; 0x20<=getchar(); ); // for removing overflow garbages
242 // '0x20<=code' is character region
246 // function(s) for provisioning client using C-level provisioning API
247 static int initProvisionClient(void)
249 // initialize persistent storage for SVR DB
250 static OCPersistentStorage pstStr =
252 .open = fopen_prvnMng,
258 if(OC_STACK_OK != OCRegisterPersistentStorageHandler(&pstStr))
260 OIC_LOG(ERROR, TAG, "OCRegisterPersistentStorageHandler error");
264 // initialize OC stack and provisioning manager
265 if(OC_STACK_OK != OCInit(NULL, 0, OC_CLIENT_SERVER))
267 OIC_LOG(ERROR, TAG, "OCStack init error");
271 if (access(PRVN_DB_FILE_NAME, F_OK) != -1)
273 printf("************************************************************\n");
274 printf("************Provisioning DB file already exists.************\n");
275 printf("************************************************************\n");
279 printf("*************************************************************\n");
280 printf("************No provisioning DB file, creating new************\n");
281 printf("*************************************************************\n");
284 if(OC_STACK_OK != OCInitPM(PRVN_DB_FILE_NAME))
286 OIC_LOG(ERROR, TAG, "OC_PM init error");
290 // register callback function(s) to each OxM
291 OTMCallbackData_t otmcb =
293 .loadSecretCB = LoadSecretJustWorksCallback,
294 .createSecureSessionCB = CreateSecureSessionJustWorksCallback,
295 .createSelectOxmPayloadCB = CreateJustWorksSelectOxmPayload,
296 .createOwnerTransferPayloadCB = CreateJustWorksOwnerTransferPayload
298 if(OC_STACK_OK != OCSetOwnerTransferCallbackData(OIC_JUST_WORKS, &otmcb))
300 OIC_LOG(ERROR, TAG, "OCSetOwnerTransferCallbackData error: OIC_JUST_WORKS");
303 otmcb.loadSecretCB = InputPinCodeCallback;
304 otmcb.createSecureSessionCB = CreateSecureSessionRandomPinCallback;
305 otmcb.createSelectOxmPayloadCB = CreatePinBasedSelectOxmPayload;
306 otmcb.createOwnerTransferPayloadCB = CreatePinBasedOwnerTransferPayload;
307 if(OC_STACK_OK != OCSetOwnerTransferCallbackData(OIC_RANDOM_DEVICE_PIN, &otmcb))
309 OIC_LOG(ERROR, TAG, "OCSetOwnerTransferCallbackData error: OIC_RANDOM_DEVICE_PIN");
312 SetInputPinCB(inputPinCB);
317 static int discoverAllDevices(void)
319 // delete un/owned device lists before updating them
322 OCDeleteDiscoveredDevices(g_own_list);
327 OCDeleteDiscoveredDevices(g_unown_list);
331 // call |OCGetDevInfoFromNetwork| API actually
332 printf(" Discovering All Un/Owned Devices on Network..\n");
333 if(OC_STACK_OK != OCGetDevInfoFromNetwork(DISCOVERY_TIMEOUT, &g_own_list, &g_unown_list))
335 OIC_LOG(ERROR, TAG, "OCGetDevInfoFromNetwork API error");
339 // display the discovered un/owned lists
340 printf(" > Discovered Owned Devices\n");
341 g_own_cnt = printDevList(g_own_list);
342 printf(" > Discovered Unowned Devices\n");
343 g_unown_cnt = printDevList(g_unown_list);
349 static int discoverUnownedDevices(void)
351 // delete unowned device list before updating it
354 OCDeleteDiscoveredDevices(g_unown_list);
358 // call |OCDiscoverUnownedDevices| API actually
359 printf(" Discovering Only Unowned Devices on Network..\n");
360 if(OC_STACK_OK != OCDiscoverUnownedDevices(DISCOVERY_TIMEOUT, &g_unown_list))
362 OIC_LOG(ERROR, TAG, "OCDiscoverUnownedDevices API error");
366 // display the discovered unowned list
367 printf(" > Discovered Unowned Devices\n");
368 g_unown_cnt = printDevList(g_unown_list);
373 static int discoverOwnedDevices(void)
375 // delete owned device list before updating it
378 OCDeleteDiscoveredDevices(g_own_list);
382 // call |OCDiscoverOwnedDevices| API actually
383 printf(" Discovering Only Owned Devices on Network..\n");
384 if(OC_STACK_OK != OCDiscoverOwnedDevices(DISCOVERY_TIMEOUT, &g_own_list))
386 OIC_LOG(ERROR, TAG, "OCDiscoverOwnedDevices API error");
390 // display the discovered owned list
391 printf(" > Discovered Owned Devices\n");
392 g_own_cnt = printDevList(g_own_list);
397 static int registerDevices(void)
399 // check |unown_list| for registering devices
400 if(!g_unown_list || 0>=g_unown_cnt)
402 printf(" > Unowned Device List, to Register Devices, is Empty\n");
403 printf(" > Please Discover Unowned Devices first, with [10|11] Menu\n");
404 return 0; // normal case
407 // call |OCDoOwnershipTransfer| API actually
408 // calling this API with callback actually acts like blocking
409 // for error checking, the return value saved and printed
411 printf(" Registering All Discovered Unowned Devices..\n");
412 OCStackResult rst = OCDoOwnershipTransfer((void*) g_ctx, g_unown_list, ownershipTransferCB);
413 if(OC_STACK_OK != rst)
415 OIC_LOG_V(ERROR, TAG, "OCDoOwnershipTransfer API error: %d", rst);
418 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
420 OIC_LOG(ERROR, TAG, "OCProvisionCredentials callback error");
424 // display the registered result
425 printf(" > Registered Discovered Unowned Devices\n");
426 printf(" > Please Discover Owned Devices for the Registered Result, with [10|12] Menu\n");
431 static int provisionPairwise(void)
433 // check |own_list| for provisioning pairwise devices
434 if(!g_own_list || 2>g_own_cnt)
436 printf(" > Owned Device List, to Provision the Pairwise, is Empty\n");
437 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
438 return 0; // normal case
441 // select two devices for provisioning pairwise devices
442 int dev_num[2] = {0};
443 if(selectTwoDiffNum(&(dev_num[0]), &(dev_num[1]), g_own_cnt, "for Linking Devices"))
445 OIC_LOG(ERROR, TAG, "selectTwoDiffNum error return");
446 return -1; // not need to 'goto' |ERROR| before allocating |acl|
449 // create ACL(s) for each selected device
450 OicSecAcl_t* acl[2] = {0};
451 for(int i=0; 2>i; ++i)
453 acl[i] = createAcl(dev_num[i]);
456 OIC_LOG(ERROR, TAG, "createAcl error return");
461 // call |OCProvisionPairwiseDevices| API actually
462 // calling this API with callback actually acts like blocking
463 // for error checking, the return value saved and printed
465 printf(" Provisioning Selected Pairwise Devices..\n");
467 OCProvisionPairwiseDevices((void*) g_ctx,
468 SYMMETRIC_PAIR_WISE_KEY, OWNER_PSK_LENGTH_128,
469 getDevInst((const OCProvisionDev_t*) g_own_list, dev_num[0]), acl[0],
470 getDevInst((const OCProvisionDev_t*) g_own_list, dev_num[1]), acl[1],
471 provisionPairwiseCB);
472 if(OC_STACK_OK != rst)
474 OIC_LOG_V(ERROR, TAG, "OCProvisionPairwiseDevices API error: %d", rst);
477 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
479 OIC_LOG(ERROR, TAG, "OCProvisionCredentials callback error");
482 OCDeleteACLList(acl[0]);
483 OCDeleteACLList(acl[1]);
485 // display the pairwise-provisioned result
486 printf(" > Provisioned Selected Pairwise Devices\n");
487 printf(" > Please Check Device's Status for the Linked Result, with [33] Menu\n");
492 OCDeleteACLList(acl[0]);
493 OCDeleteACLList(acl[1]);
497 static int provisionCred(void)
499 // check |own_list| for provisioning pairwise credentials
500 if(!g_own_list || 2>g_own_cnt)
502 printf(" > Owned Device List, to Provision Credentials, is Empty\n");
503 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
504 return 0; // normal case
507 // select two devices for provisioning pairwise credentials
508 int dev_num[2] = {0};
509 if(selectTwoDiffNum(&(dev_num[0]), &(dev_num[1]), g_own_cnt, "for Linking CRED(s)"))
511 OIC_LOG(ERROR, TAG, "selectTwoDiffNum error return");
515 printf(" Select PSK length..\n");
516 printf(" 1 - 128bit(Default)\n");
517 printf(" 2 - 256bit\n");
520 for(int ret=0; 1!=ret; )
522 ret = scanf("%d",&sizeOption);
523 for( ; 0x20<=getchar(); ); // for removing overflow garbages
524 // '0x20<=code' is character region
532 size = OWNER_PSK_LENGTH_128;
537 size = OWNER_PSK_LENGTH_256;
542 size = OWNER_PSK_LENGTH_128;
548 // call |OCProvisionCredentials| API actually
549 // calling this API with callback actually acts like blocking
550 // for error checking, the return value saved and printed
552 printf(" Provisioning Selected Pairwise Credentials..\n");
554 OCProvisionCredentials((void*) g_ctx,
555 SYMMETRIC_PAIR_WISE_KEY, size,
556 getDevInst((const OCProvisionDev_t*) g_own_list, dev_num[0]),
557 getDevInst((const OCProvisionDev_t*) g_own_list, dev_num[1]),
559 if(OC_STACK_OK != rst)
561 OIC_LOG_V(ERROR, TAG, "OCProvisionCredentials API error: %d", rst);
564 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
566 OIC_LOG(ERROR, TAG, "OCProvisionCredentials callback error");
570 // display the CRED-provisioned result
571 printf(" > Provisioned Selected Pairwise Crendentials\n");
572 printf(" > Please Check Device's Status for the Linked Result, with [33] Menu\n");
577 static int provisionAcl(void)
579 // check |own_list| for provisioning access control list
580 if(!g_own_list || 1>g_own_cnt)
582 printf(" > Owned Device List, to Provision ACL, is Empty\n");
583 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
584 return 0; // normal case
587 // select device for provisioning access control list
591 printf(" > Enter Device Number, for Provisioning ACL: ");
592 for(int ret=0; 1!=ret; )
594 ret = scanf("%d", &dev_num);
595 for( ; 0x20<=getchar(); ); // for removing overflow garbages
596 // '0x20<=code' is character region
598 if(0<dev_num && g_own_cnt>=dev_num)
602 printf(" Entered Wrong Number. Please Enter Again\n");
605 // create ACL for selected device
606 OicSecAcl_t* acl = NULL;
607 acl = createAcl(dev_num);
610 OIC_LOG(ERROR, TAG, "createAcl error return");
614 // call |OCProvisionACL| API actually
615 // calling this API with callback actually acts like blocking
616 // for error checking, the return value saved and printed
618 printf(" Provisioning Selected ACL..\n");
619 OCProvisionDev_t* dev = getDevInst((const OCProvisionDev_t*) g_own_list, dev_num);
622 OIC_LOG(ERROR, TAG, "provisionAcl: device instance empty");
625 OCStackResult rst = OCProvisionACL((void*) g_ctx, dev, acl, provisionAclCB);
626 if(OC_STACK_OK != rst)
628 OIC_LOG_V(ERROR, TAG, "OCProvisionACL API error: %d", rst);
631 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
633 OIC_LOG(ERROR, TAG, "OCProvisionCredentials callback error");
636 OCDeleteACLList(acl); // after here |acl| points nothing
638 // display the ACL-provisioned result
639 printf(" > Provisioned Selected ACL\n");
644 OCDeleteACLList(acl); // after here |acl| points nothing
648 static int provisionDirectPairing(void)
650 // check |own_list| for provisioning direct-pairing
651 if(!g_own_list || 1>g_own_cnt)
653 printf(" > Owned Device List, to Provision ACL, is Empty\n");
654 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
655 return 0; // normal case
658 // select device for provisioning direct-pairing
662 printf(" > Enter Device Number, for Provisioning Direct-Pairing: ");
663 for(int ret=0; 1!=ret; )
665 ret = scanf("%d", &dev_num);
666 for( ; 0x20<=getchar(); ); // for removing overflow garbages
667 // '0x20<=code' is character region
669 if(0<dev_num && g_own_cnt>=dev_num)
673 printf(" Entered Wrong Number. Please Enter Again\n");
676 // create Direct-Pairing Configuration(PIN, PDACL) for selected device
677 // TODO: default acl -> input from user !
679 memset(&pconf, 0, sizeof(OicSecPconf_t));
684 // set default supported PRM types
685 pconf.prmLen = sizeof(SUPPORTED_PRMS)/sizeof(OicSecPrm_t);
686 pconf.prm = (OicSecPrm_t *)OICCalloc(pconf.prmLen, sizeof(OicSecPrm_t));
689 for (size_t i=0; i<pconf.prmLen; i++)
691 pconf.prm[i] = SUPPORTED_PRMS[i];
696 OIC_LOG(ERROR, TAG, "create prm error return");
701 const char DP_DEFAULT_PIN[] = "00000000";
702 memcpy(pconf.pin.val, DP_DEFAULT_PIN, DP_PIN_LENGTH);
705 pconf.pdacls = createPdAcl(dev_num);
708 OIC_LOG(ERROR, TAG, "createPdAcl error return");
712 // call |OCProvisionDirectPairing| API actually
713 // calling this API with callback actually acts like blocking
714 // for error checking, the return value saved and printed
716 printf(" Atempt Direct-Pairing Provisioning (PIN : [%s])..\n", (char*)pconf.pin.val);
717 OCStackResult rst = OCProvisionDirectPairing((void*) g_ctx,
718 getDevInst((const OCProvisionDev_t*) g_own_list, dev_num),
719 &pconf, provisionDPCB);
720 if(OC_STACK_OK != rst)
722 OIC_LOG_V(ERROR, TAG, "OCProvisionDirectPairing API error: %d", rst);
723 if (OC_STACK_UNAUTHORIZED_REQ == rst)
725 OIC_LOG(ERROR, TAG, "Target Server NOT Support Direct-Pairing !!! (DPC == false)");
729 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
731 OIC_LOG(ERROR, TAG, "OCProvisionCredentials callback error");
734 OCDeletePdAclList(pconf.pdacls);
736 // display the PCONF-provisioned result
737 printf(" > SUCCESS to provision Direct-Pairing !!\n");
742 OCDeletePdAclList(pconf.pdacls); // after here |acl| points nothing
746 static int checkLinkedStatus(void)
748 // check |own_list| for checking selected link status on PRVN DB
749 if(!g_own_list || 1>g_own_cnt)
751 printf(" > Owned Device List, to Check Linked Status on PRVN DB, is Empty\n");
752 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
753 return 0; // normal case
756 // select device for checking selected link status on PRVN DB
760 printf(" > Enter Device Number, for Checking Linked Status on PRVN DB: ");
761 for(int ret=0; 1!=ret; )
763 ret = scanf("%d", &dev_num);
764 for( ; 0x20<=getchar(); ); // for removing overflow garbages
765 // '0x20<=code' is character region
767 if(0<dev_num && g_own_cnt>=dev_num)
771 printf(" Entered Wrong Number. Please Enter Again\n");
774 // call |OCGetLinkedStatus| API actually
775 printf(" Checking Selected Link Status on PRVN DB..\n");
776 OCUuidList_t* dvid_lst = NULL;
778 OCProvisionDev_t* dev = getDevInst((const OCProvisionDev_t*)g_own_list, dev_num);
779 if(!dev || !dev->doxm)
781 OIC_LOG(ERROR, TAG, "checkLinkedStatus: device instance empty");
787 &dev->doxm->deviceID,
788 &dvid_lst, &dvid_cnt)) // allow empty list
790 OIC_LOG(ERROR, TAG, "OCGetLinkedStatus API error");
794 // display the linked status result
795 printf(" > Checked Selected Link Status on PRVN DB\n");
796 if(!dvid_lst || !dvid_cnt) // |size_t| is unsigned
798 printf(" Linked Device List is Empty..\n");
799 return 0; // normal case
801 if(dvid_cnt != printUuidList((const OCUuidList_t*) dvid_lst))
803 OIC_LOG(ERROR, TAG, "printUuidList error return");
806 OCDeleteUuidList(dvid_lst);
811 OCDeleteUuidList(dvid_lst);
815 static int getCred(void)
817 // check |own_list| for checking selected link status on PRVN DB
818 if(!g_own_list || 1>g_own_cnt)
820 printf(" > Owned Device List, to Check Linked Status on PRVN DB, is Empty\n");
821 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
822 return 0; // normal case
825 // select device for checking selected link status on PRVN DB
829 printf(" > Enter Device Number, for Checking Linked Status on PRVN DB: ");
830 for(int ret=0; 1!=ret; )
832 ret = scanf("%d", &dev_num);
833 for( ; 0x20<=getchar(); ); // for removing overflow garbages
834 // '0x20<=code' is character region
836 if(0<dev_num && g_own_cnt>=dev_num)
840 printf(" Entered Wrong Number. Please Enter Again\n");
843 // call |getDevInst| API actually
844 // calling this API with callback actually acts like blocking
845 // for error checking, the return value saved and printed
847 OCProvisionDev_t* dev = getDevInst((const OCProvisionDev_t*) g_own_list, dev_num);
850 OIC_LOG(ERROR, TAG, "getDevInst: device instance empty");
853 OCStackResult rst = OCGetCredResource((void*) g_ctx, dev, getCredCB);
854 if(OC_STACK_OK != rst)
856 OIC_LOG_V(ERROR, TAG, "OCGetCred API error: %d", rst);
859 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
861 OIC_LOG(ERROR, TAG, "OCGetCredResource callback error");
865 // display the result of get credential
866 printf(" > Get Cred SUCCEEDED\n");
874 static int getAcl(void)
876 // check |own_list| for checking selected link status on PRVN DB
877 if(!g_own_list || 1>g_own_cnt)
879 printf(" > Owned Device List, to Check Linked Status on PRVN DB, is Empty\n");
880 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
881 return 0; // normal case
884 // select device for checking selected link status on PRVN DB
888 printf(" > Enter Device Number, for Checking Linked Status on PRVN DB: ");
889 for(int ret=0; 1!=ret; )
891 ret = scanf("%d", &dev_num);
892 for( ; 0x20<=getchar(); ); // for removing overflow garbages
893 // '0x20<=code' is character region
895 if(0<dev_num && g_own_cnt>=dev_num)
899 printf(" Entered Wrong Number. Please Enter Again\n");
902 // call |getDevInst| API actually
903 // calling this API with callback actually acts like blocking
904 // for error checking, the return value saved and printed
906 OCProvisionDev_t* dev = getDevInst((const OCProvisionDev_t*) g_own_list, dev_num);
909 OIC_LOG(ERROR, TAG, "getDevInst: device instance empty");
912 OCStackResult rst = OCGetACLResource((void*) g_ctx, dev, getAclCB);
913 if(OC_STACK_OK != rst)
915 OIC_LOG_V(ERROR, TAG, "OCGetACLResource API error: %d", rst);
919 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
921 OIC_LOG(ERROR, TAG, "OCGetACLResource callback error");
925 // display the result of get credential
926 printf(" > Get ACL SUCCEEDED\n");
934 static int unlinkPairwise(void)
936 // check |own_list| for unlinking pairwise devices
937 if(!g_own_list || 2>g_own_cnt)
939 printf(" > Owned Device List, to Unlink the Pairwise, is Empty\n");
940 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
941 return 0; // normal case
944 // select two devices for unlinking pairwise devices
945 int dev_num[2] = {0};
946 if(selectTwoDiffNum(&(dev_num[0]), &(dev_num[1]), g_own_cnt, "for Unlinking Devices"))
948 OIC_LOG(ERROR, TAG, "selectTwoDiffNum error return");
952 // call |OCUnlinkDevices| API actually
953 // calling this API with callback actually acts like blocking
954 // for error checking, the return value saved and printed
956 printf(" Unlinking Selected Pairwise Devices..\n");
958 OCUnlinkDevices((void*) g_ctx,
959 getDevInst((const OCProvisionDev_t*) g_own_list, dev_num[0]),
960 getDevInst((const OCProvisionDev_t*) g_own_list, dev_num[1]),
962 if(OC_STACK_OK != rst)
964 OIC_LOG_V(ERROR, TAG, "OCUnlinkDevices API error: %d", rst);
967 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
969 OIC_LOG(ERROR, TAG, "OCProvisionCredentials callback error");
973 // display the pairwise-unlinked result
974 printf(" > Unlinked Selected Pairwise Devices\n");
975 printf(" > Please Check Device's Status for the Unlinked Result, with [33] Menu\n");
980 static int removeDevice(void)
982 // check |own_list| for removing device
983 if(!g_own_list || 1>g_own_cnt)
985 printf(" > Owned Device List, to Remove Device, is Empty\n");
986 printf(" > Please Register Unowned Devices first, with [20] Menu\n");
987 return 0; // normal case
990 // select device for removing it
994 printf(" > Enter Device Number, for Removing Device: ");
995 for(int ret=0; 1!=ret; )
997 ret = scanf("%d", &dev_num);
998 for( ; 0x20<=getchar(); ); // for removing overflow garbages
999 // '0x20<=code' is character region
1001 if(0<dev_num && g_own_cnt>=dev_num)
1005 printf(" Entered Wrong Number. Please Enter Again\n");
1008 // call |OCRemoveDevice| API actually
1009 // calling this API with callback actually acts like blocking
1010 // for error checking, the return value saved and printed
1012 printf(" Removing Selected Owned Device..\n");
1014 OCRemoveDevice((void*) g_ctx, DISCOVERY_TIMEOUT,
1015 getDevInst((const OCProvisionDev_t*) g_own_list, dev_num), removeDeviceCB);
1016 if(OC_STACK_OK != rst)
1018 OIC_LOG_V(ERROR, TAG, "OCRemoveDevice API error: %d", rst);
1021 if(waitCallbackRet()) // input |g_doneCB| flag implicitly
1023 OIC_LOG(ERROR, TAG, "OCProvisionCredentials callback error");
1027 // display the removed result
1028 printf(" > Removed Selected Owned Device\n");
1029 printf(" > Please Discover Owned Devices for the Registered Result, with [10|12] Menu\n");
1034 static OicSecAcl_t* createAcl(const int dev_num)
1036 if(0>=dev_num || g_own_cnt<dev_num)
1038 OIC_LOG(ERROR, TAG, "createAcl invalid parameters");
1039 return NULL; // not need to 'goto' |ERROR| before allocating |acl|
1042 // allocate memory for |acl| struct
1043 printf(" **** Create ACL for the Selected Device[%d]\n", dev_num);
1044 OicSecAcl_t* acl = (OicSecAcl_t*) OICCalloc(1, sizeof(OicSecAcl_t));
1047 OIC_LOG(ERROR, TAG, "createAcl: OICCalloc error return");
1048 return NULL; // not need to 'goto' |ERROR| before allocating |acl|
1050 OicSecAce_t* ace = (OicSecAce_t*) OICCalloc(1, sizeof(OicSecAce_t));
1053 OIC_LOG(ERROR, TAG, "createAcl: OICCalloc error return");
1054 return NULL; // not need to 'goto' |ERROR| before allocating |acl|
1056 LL_APPEND(acl->aces, ace);
1058 // enter |subject| device number
1062 printf(" > [A] Enter Subject Device Number: ");
1063 for(int ret=0; 1!=ret; )
1065 ret = scanf("%d", &num);
1066 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1067 // '0x20<=code' is character region
1069 if(0<num && g_own_cnt>=num && dev_num!=num)
1073 printf(" Entered Wrong Number. Please Enter Again\n");
1076 OCProvisionDev_t* dev = getDevInst((const OCProvisionDev_t*)g_own_list, num);
1077 if(!dev || !dev->doxm)
1079 OIC_LOG(ERROR, TAG, "createAcl: device instance empty");
1082 memcpy(&ace->subjectuuid, &dev->doxm->deviceID, UUID_LENGTH);
1084 // enter number of |resources| in 'accessed' device
1087 printf(" > [B] Enter Number of Accessed Resources (under 16): ");
1088 // '16' is |ACL_RESRC_MAX_NUM|
1089 for(int ret=0; 1!=ret; )
1091 ret = scanf("%d", &num);
1092 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1093 // '0x20<=code' is character region
1095 if(0<num && ACL_RESRC_MAX_NUM>=num)
1099 printf(" Entered Wrong Number. Please Enter under 16 Again\n");
1100 // '16' is |ACL_RESRC_MAX_NUM|
1103 // enter actually each 'accessed' |resources| name
1104 printf(" Enter Each Accessed Resource Name (each under 128 char)\n");
1105 // '128' is ACL_RESRC_MAX_LEN
1107 char rsrc_in[ACL_RESRC_MAX_LEN+1] = {0}; // '1' for null termination
1108 for(int i = 0; num > i; ++i)
1110 OicSecRsrc_t* rsrc = (OicSecRsrc_t*)OICCalloc(1, sizeof(OicSecRsrc_t));
1113 OIC_LOG(ERROR, TAG, "createAcl: OICCalloc error return");
1117 printf(" Enter Accessed Resource[%d] Name: ", i+1);
1118 for(int ret=0; 1!=ret; )
1120 ret = scanf("%128s", rsrc_in); // '128' is ACL_RESRC_MAX_LEN
1121 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1122 // '0x20<=code' is character region
1124 size_t len = strlen(rsrc_in)+1; // '1' for null termination
1125 rsrc->href = (char*) OICCalloc(len, sizeof(char));
1128 OIC_LOG(ERROR, TAG, "createAcl: OICCalloc error return");
1131 OICStrcpy(rsrc->href, len, rsrc_in);
1136 printf(" Enter Number of resource type for [%s]: ", rsrc_in);
1137 for(int ret=0; 1!=ret; )
1139 ret = scanf("%d", &arrLen);
1140 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1141 // '0x20<=code' is character region
1143 if(0 < arrLen && ACL_RESRC_ARRAY_SIZE >= arrLen)
1147 printf(" Entered Wrong Number. Please Enter under %d Again\n", ACL_RESRC_ARRAY_SIZE);
1150 rsrc->typeLen = arrLen;
1151 rsrc->types = (char**)OICCalloc(arrLen, sizeof(char*));
1154 OIC_LOG(ERROR, TAG, "createAcl: OICCalloc error return");
1158 for(int i = 0; i < arrLen; i++)
1160 printf(" Enter ResourceType[%d] Name: ", i+1);
1161 for(int ret=0; 1!=ret; )
1163 ret = scanf("%128s", rsrc_in); // '128' is ACL_RESRC_MAX_LEN
1164 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1165 // '0x20<=code' is character region
1167 rsrc->types[i] = OICStrdup(rsrc_in);
1170 OIC_LOG(ERROR, TAG, "createAcl: OICStrdup error return");
1177 printf(" Enter Number of interface name for [%s]: ", rsrc_in);
1178 for(int ret=0; 1!=ret; )
1180 ret = scanf("%d", &arrLen);
1181 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1182 // '0x20<=code' is character region
1184 if(0 < arrLen && ACL_RESRC_ARRAY_SIZE >= arrLen)
1188 printf(" Entered Wrong Number. Please Enter under %d Again\n", ACL_RESRC_ARRAY_SIZE);
1191 rsrc->interfaceLen = arrLen;
1192 rsrc->interfaces = (char**)OICCalloc(arrLen, sizeof(char*));
1193 if(!rsrc->interfaces)
1195 OIC_LOG(ERROR, TAG, "createAcl: OICCalloc error return");
1199 for(int i = 0; i < arrLen; i++)
1201 printf(" Enter ResourceType[%d] Name: ", i+1);
1202 for(int ret=0; 1!=ret; )
1204 ret = scanf("%128s", rsrc_in); // '128' is ACL_RESRC_MAX_LEN
1205 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1206 // '0x20<=code' is character region
1208 rsrc->interfaces[i] = OICStrdup(rsrc_in);
1209 if(!rsrc->interfaces[i])
1211 OIC_LOG(ERROR, TAG, "createAcl: OICStrdup error return");
1216 LL_APPEND(ace->resources, rsrc);
1219 // enter |permission| for this access
1220 printf(" > [C] Enter Permission for This Access\n");
1221 uint16_t pmsn = PERMISSION_FULL_CONTROL; // default full permission
1222 uint16_t pmsn_msk = PERMISSION_CREATE; // default permission mask
1223 for(int i=0; ACL_PEMISN_CNT>i; ++i)
1228 printf(" Enter %s Permission (y/n): ", ACL_PEMISN[i]);
1229 for(int ret=0; 1!=ret; )
1231 ret = scanf("%c", &ans);
1232 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1233 // '0x20<=code' is character region
1235 if('y'==ans || 'Y'==ans || 'n'==ans|| 'N'==ans)
1237 ans &= ~0x20; // for masking lower case, 'y/n'
1240 printf(" Entered Wrong Answer. Please Enter 'y/n' Again\n");
1242 if('N' == ans) // masked lower case, 'n'
1248 ace->permission = pmsn;
1253 OCDeleteACLList(acl); // after here |acl| points nothing
1257 static OicSecPdAcl_t* createPdAcl(const int dev_num)
1259 if(0>=dev_num || g_own_cnt<dev_num)
1261 OIC_LOG(ERROR, TAG, "createAcl invalid parameters");
1262 return NULL; // not need to 'goto' |ERROR| before allocating |acl|
1265 // allocate memory for |pdacl| struct
1266 printf(" **** Create PDACL for the Selected Device[%d]\n", dev_num);
1267 OicSecPdAcl_t* pdAcl = (OicSecPdAcl_t*) OICCalloc(1, sizeof(OicSecPdAcl_t));
1270 OIC_LOG(ERROR, TAG, "createAcl: OICCalloc error return");
1271 return NULL; // not need to 'goto' |ERROR| before allocating |acl|
1275 // number of resources
1276 char rsrc_in[][ACL_RESRC_MAX_LEN+1] = {"*", "/rsrc/*"};
1277 pdAcl->resourcesLen = 1;
1280 int num = pdAcl->resourcesLen;
1281 pdAcl->resources = (char**) OICCalloc(num, sizeof(char*));
1282 if(!pdAcl->resources)
1284 OIC_LOG(ERROR, TAG, "createPdAcl: OICCalloc error return");
1287 for(int i=0; num>i; ++i)
1289 size_t len = strlen(rsrc_in[i])+1; // '1' for null termination
1290 char* rsrc = (char*) OICCalloc(len, sizeof(char));
1293 OIC_LOG(ERROR, TAG, "createPdAcl: OICCalloc error return");
1296 OICStrcpy(rsrc, len, rsrc_in[i]);
1297 pdAcl->resources[i] = rsrc; // after here, |rsrc| points nothing
1301 pdAcl->permission = PERMISSION_FULL_CONTROL;
1306 OCDeletePdAclList(pdAcl);
1310 static OCProvisionDev_t* getDevInst(const OCProvisionDev_t* dev_lst, const int dev_num)
1312 if(!dev_lst || 0>=dev_num)
1314 printf(" Device List is Empty..\n");
1318 OCProvisionDev_t* lst = (OCProvisionDev_t*) dev_lst;
1328 return NULL; // in here |lst| is always |NULL|
1331 static int printDevList(const OCProvisionDev_t* dev_lst)
1335 printf(" Device List is Empty..\n\n");
1339 OCProvisionDev_t* lst = (OCProvisionDev_t*) dev_lst;
1343 printf(" [%d] ", ++lst_cnt);
1344 printUuid((const OicUuid_t*) &lst->doxm->deviceID);
1353 static size_t printUuidList(const OCUuidList_t* uid_lst)
1357 printf(" Device List is Empty..\n\n");
1361 OCUuidList_t* lst = (OCUuidList_t*) uid_lst;
1365 printf(" [%zu] ", ++lst_cnt);
1366 printUuid((const OicUuid_t*) &lst->dev);
1375 static int printResultList(const OCProvisionResult_t* rslt_lst, const int rslt_cnt)
1377 if(!rslt_lst || 0>=rslt_cnt)
1379 printf(" Device List is Empty..\n\n");
1384 for( ; rslt_cnt>lst_cnt; ++lst_cnt)
1386 printf(" [%d] ", lst_cnt+1);
1387 printUuid((const OicUuid_t*) &rslt_lst[lst_cnt].deviceId);
1388 printf(" - result: %d\n", rslt_lst[lst_cnt].res);
1395 static void printUuid(const OicUuid_t* uid)
1397 for(int i=0; i<UUID_LENGTH; )
1399 printf("%02X", (*uid).id[i++]);
1400 if(i==4 || i==6 || i==8 || i==10) // canonical format for UUID has '8-4-4-4-12'
1407 static FILE* fopen_prvnMng(const char* path, const char* mode)
1409 (void)path; // unused |path| parameter
1411 // input |g_svr_db_fname| internally by force, not using |path| parameter
1412 // because |OCPersistentStorage::open| is called |OCPersistentStorage| internally
1413 // with its own |SVR_DB_FILE_NAME|
1414 return fopen(SVR_DB_FILE_NAME, mode);
1417 static int waitCallbackRet(void)
1419 for(int i=0; !g_doneCB && CALLBACK_TIMEOUT>i; ++i)
1422 if(OC_STACK_OK != OCProcess())
1424 OIC_LOG(ERROR, TAG, "OCStack process error");
1432 static int selectTwoDiffNum(int* a, int* b, const int max, const char* str)
1434 if(!a || !b || 2>max || !str)
1441 for(int i=0; 2>i; ++i)
1443 int* num = 0==i?a:b;
1446 printf(" > Enter Device[%d] Number, %s: ", i+1, str);
1447 for(int ret=0; 1!=ret; )
1449 ret = scanf("%d", num);
1450 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1451 // '0x20<=code' is character region
1453 if(0<*num && max>=*num)
1457 printf(" Entered Wrong Number. Please Enter Again\n");
1470 static void printMenu(void)
1472 printf("************************************************************\n");
1473 printf("****** OIC Provisioning Client with using C-level API ******\n");
1474 printf("************************************************************\n\n");
1476 printf("** [A] DISCOVER DEVICES ON NETWORK\n");
1477 printf("** 10. Discover All Un/Owned Devices on Network\n");
1478 printf("** 11. Discover Only Unowned Devices on Network\n");
1479 printf("** 12. Discover Only Owned Devices on Network\n\n");
1481 printf("** [B] REGISTER/OWN ALL DISCOVERED UNOWNED DEVICES\n");
1482 printf("** 20. Register/Own All Discovered Unowned Devices\n\n");
1484 printf("** [C] PROVISION/LINK PAIRWISE THINGS\n");
1485 printf("** 30. Provision/Link Pairwise Things\n");
1486 printf("** 31. Provision Credentials for Pairwise Things\n");
1487 printf("** 32. Provision the Selected Access Control List(ACL)\n");
1488 printf("** 33. Provision Direct-Pairing Configuration\n");
1489 printf("** 34. Check Linked Status of the Selected Device on PRVN DB\n\n");
1491 printf("** [D] UNLINK PAIRWISE THINGS\n");
1492 printf("** 40. Unlink Pairwise Things\n\n");
1494 printf("** [E] REMOVE THE SELECTED DEVICE\n");
1495 printf("** 50. Remove the Selected Device\n\n");
1497 printf("** [F] GET SECURITY RESOURCE FOR DEBUGGING ONLY\n");
1498 printf("** 60. Get the Credential resources of the Selected Device\n");
1499 printf("** 61. Get the ACL resources of the Selected Device\n\n");
1501 printf("** [F] EXIT PROVISIONING CLIENT\n");
1502 printf("** 99. Exit Provisionong Client\n\n");
1504 printf("************************************************************\n\n");
1507 #if 0 // Code for enabling path configuration for PDB and SVR DBf
1508 static void printUsage(void)
1511 printf("OIC Provisioning Client with using C-level API\n");
1512 printf("Usage: provisioningclient [option]...\n");
1514 printf(" -h print help for this provisioning client\n");
1515 printf(" -p=[prvn_db_file_path/name] input PRVN DB file path and name\n");
1516 printf(" if not exists, will load default DB file\n");
1517 printf(" (default: |oic_prvn_mng.db| on working dir)\n");
1518 printf(" (ex. -p=oic_prvn_mng.db)\n");
1519 printf(" -s=[svr_db_file_path/name] input SVR DB file path and name\n");
1520 printf(" if not exists, will load default DB file\n");
1521 printf(" (default: |oic_svr_db_client.json| on working dir)\n");
1522 printf(" (ex. -s=oic_svr_db_client.json)\n");
1527 // main function for provisioning client using C-level provisioning API
1530 // initialize provisioning client
1531 if(initProvisionClient())
1533 OIC_LOG(ERROR, TAG, "ProvisionClient init error");
1537 // main loop for provisioning manager
1543 printf(">> Enter Menu Number: ");
1544 for(int ret=0; 1!=ret; )
1546 ret = scanf("%d", &mn_num);
1547 for( ; 0x20<=getchar(); ); // for removing overflow garbages
1548 // '0x20<=code' is character region
1553 case _10_DISCOV_ALL_DEVS_:
1554 if(discoverAllDevices())
1556 OIC_LOG(ERROR, TAG, "_10_DISCOV_ALL_DEVS_: error");
1559 case _11_DISCOV_UNOWN_DEVS_:
1560 if(discoverUnownedDevices())
1562 OIC_LOG(ERROR, TAG, "_11_DISCOV_UNOWN_DEVS_: error");
1565 case _12_DISCOV_OWN_DEVS_:
1566 if(discoverOwnedDevices())
1568 OIC_LOG(ERROR, TAG, "_12_DISCOV_OWN_DEVS_: error");
1571 case _20_REGIST_DEVS_:
1572 if(registerDevices())
1574 OIC_LOG(ERROR, TAG, "_20_REGIST_DEVS_: error");
1577 case _30_PROVIS_PAIR_DEVS_:
1578 if(provisionPairwise())
1580 OIC_LOG(ERROR, TAG, "_30_PROVIS_PAIR_DEVS_: error");
1583 case _31_PROVIS_CRED_:
1586 OIC_LOG(ERROR, TAG, "_31_PROVIS_CRED_: error");
1589 case _32_PROVIS_ACL_:
1592 OIC_LOG(ERROR, TAG, "_32_PROVIS_ACL_: error");
1595 case _33_PROVIS_DP_:
1596 if(provisionDirectPairing())
1598 OIC_LOG(ERROR, TAG, "_33_PROVIS_DP_: error");
1601 case _34_CHECK_LINK_STATUS_:
1602 if(checkLinkedStatus())
1604 OIC_LOG(ERROR, TAG, "_34_CHECK_LINK_STATUS_: error");
1607 case _40_UNLINK_PAIR_DEVS_:
1608 if(unlinkPairwise())
1610 OIC_LOG(ERROR, TAG, "_40_UNLINK_PAIR_DEVS_: error");
1613 case _50_REMOVE_SELEC_DEV_:
1616 OIC_LOG(ERROR, TAG, "_50_REMOVE_SELEC_DEV_: error");
1622 OIC_LOG(ERROR, TAG, "_60_GET_CRED_: error");
1628 OIC_LOG(ERROR, TAG, "_61_GET_ACL_: error");
1631 case _99_EXIT_PRVN_CLT_:
1634 printf(">> Entered Wrong Number. Please Enter Again\n\n");
1640 if(OC_STACK_OK != OCStop())
1642 OIC_LOG(ERROR, TAG, "OCStack stop error");
1644 OCDeleteDiscoveredDevices(g_own_list); // after here |g_own_list| points nothing
1645 OCDeleteDiscoveredDevices(g_unown_list); // after here |g_unown_list| points nothing
1649 OICFree(g_svr_fname); // after here |g_svr_fname| points nothing
1653 OICFree(g_prvn_fname); // after here |g_prvn_fname| points nothing
1655 return 0; // always return normal case
1660 #endif //__cplusplus