MQTTPersistenceDefault.c 21 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. /*******************************************************************************
  2. * Copyright (c) 2009, 2020 IBM Corp.
  3. *
  4. * All rights reserved. This program and the accompanying materials
  5. * are made available under the terms of the Eclipse Public License v2.0
  6. * and Eclipse Distribution License v1.0 which accompany this distribution.
  7. *
  8. * The Eclipse Public License is available at
  9. * https://www.eclipse.org/legal/epl-2.0/
  10. * and the Eclipse Distribution License is available at
  11. * http://www.eclipse.org/org/documents/edl-v10.php.
  12. *
  13. * Contributors:
  14. * Ian Craggs - initial API and implementation and/or initial documentation
  15. * Ian Craggs - async client updates
  16. * Ian Craggs - fix for bug 484496
  17. * Ian Craggs - fix for issue 285
  18. *******************************************************************************/
  19. /**
  20. * @file
  21. * \brief A file system based persistence implementation.
  22. *
  23. * A directory is specified when the MQTT client is created. When the persistence is then
  24. * opened (see ::Persistence_open), a sub-directory is made beneath the base for this
  25. * particular client ID and connection key. This allows one persistence base directory to
  26. * be shared by multiple clients.
  27. *
  28. */
  29. #if !defined(NO_PERSISTENCE)
  30. #include "OsWrapper.h"
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <errno.h>
  34. #if defined(_WIN32) || defined(_WIN64)
  35. #include <direct.h>
  36. /* Windows doesn't have strtok_r, so remap it to strtok */
  37. #define strtok_r( A, B, C ) strtok( A, B )
  38. #define snprintf _snprintf
  39. int keysWin32(char *, char ***, int *);
  40. int clearWin32(char *);
  41. int containskeyWin32(char *, char *);
  42. #else
  43. #include <sys/stat.h>
  44. #include <dirent.h>
  45. #include <unistd.h>
  46. int keysUnix(char *, char ***, int *);
  47. int clearUnix(char *);
  48. int containskeyUnix(char *, char *);
  49. #endif
  50. #include "MQTTClientPersistence.h"
  51. #include "MQTTPersistenceDefault.h"
  52. #include "StackTrace.h"
  53. #include "Heap.h"
  54. /** Create persistence directory for the client: context/clientID-serverURI.
  55. * See ::Persistence_open
  56. */
  57. int pstopen(void **handle, const char* clientID, const char* serverURI, void* context)
  58. {
  59. int rc = 0;
  60. char *dataDir = context;
  61. char *clientDir;
  62. char *pToken = NULL;
  63. char *save_ptr = NULL;
  64. char *pCrtDirName = NULL;
  65. char *pTokDirName = NULL;
  66. char *perserverURI = NULL, *ptraux;
  67. size_t alloclen = 0;
  68. FUNC_ENTRY;
  69. /* Note that serverURI=address:port, but ":" not allowed in Windows directories */
  70. if ((perserverURI = malloc(strlen(serverURI) + 1)) == NULL)
  71. {
  72. rc = PAHO_MEMORY_ERROR;
  73. goto exit;
  74. }
  75. strcpy(perserverURI, serverURI);
  76. while ((ptraux = strstr(perserverURI, ":")) != NULL)
  77. *ptraux = '-' ;
  78. /* consider '/' + '-' + '\0' */
  79. alloclen = strlen(dataDir) + strlen(clientID) + strlen(perserverURI) + 3;
  80. clientDir = malloc(alloclen);
  81. if (!clientDir)
  82. {
  83. free(perserverURI);
  84. rc = PAHO_MEMORY_ERROR;
  85. goto exit;
  86. }
  87. if (snprintf(clientDir, alloclen, "%s/%s-%s", dataDir, clientID, perserverURI) >= alloclen)
  88. {
  89. free(clientDir);
  90. free(perserverURI);
  91. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  92. goto exit;
  93. }
  94. /* create clientDir directory */
  95. /* pCrtDirName - holds the directory name we are currently trying to create. */
  96. /* This gets built up level by level untipwdl the full path name is created.*/
  97. /* pTokDirName - holds the directory name that gets used by strtok. */
  98. if ((pCrtDirName = (char*)malloc(strlen(clientDir) + 1)) == NULL)
  99. {
  100. free(clientDir);
  101. free(perserverURI);
  102. rc = PAHO_MEMORY_ERROR;
  103. goto exit;
  104. }
  105. if ((pTokDirName = (char*)malloc( strlen(clientDir) + 1 )) == NULL)
  106. {
  107. free(pCrtDirName);
  108. free(clientDir);
  109. free(perserverURI);
  110. rc = PAHO_MEMORY_ERROR;
  111. goto exit;
  112. }
  113. strcpy( pTokDirName, clientDir );
  114. /* If first character is directory separator, make sure it's in the created directory name #285 */
  115. if (*pTokDirName == '/' || *pTokDirName == '\\')
  116. {
  117. *pCrtDirName = *pTokDirName;
  118. pToken = strtok_r( pTokDirName + 1, "\\/", &save_ptr );
  119. strcpy( pCrtDirName + 1, pToken );
  120. }
  121. else
  122. {
  123. pToken = strtok_r( pTokDirName, "\\/", &save_ptr );
  124. strcpy( pCrtDirName, pToken );
  125. }
  126. rc = pstmkdir( pCrtDirName );
  127. pToken = strtok_r( NULL, "\\/", &save_ptr );
  128. while ( (pToken != NULL) && (rc == 0) )
  129. {
  130. /* Append the next directory level and try to create it */
  131. strcat( pCrtDirName, "/" );
  132. strcat( pCrtDirName, pToken );
  133. rc = pstmkdir( pCrtDirName );
  134. pToken = strtok_r( NULL, "\\/", &save_ptr );
  135. }
  136. *handle = clientDir;
  137. free(pTokDirName);
  138. free(pCrtDirName);
  139. free(perserverURI);
  140. exit:
  141. FUNC_EXIT_RC(rc);
  142. return rc;
  143. }
  144. /** Function to create a directory.
  145. * Returns 0 on success or if the directory already exists.
  146. */
  147. int pstmkdir( char *pPathname )
  148. {
  149. int rc = 0;
  150. FUNC_ENTRY;
  151. #if defined(_WIN32) || defined(_WIN64)
  152. if ( _mkdir( pPathname ) != 0 )
  153. {
  154. #else
  155. /* Create a directory with read, write and execute access for the owner and read access for the group */
  156. #if !defined(_WRS_KERNEL)
  157. if ( mkdir( pPathname, S_IRWXU | S_IRGRP ) != 0 )
  158. #else
  159. if ( mkdir( pPathname ) != 0 )
  160. #endif /* !defined(_WRS_KERNEL) */
  161. {
  162. #endif
  163. if ( errno != EEXIST )
  164. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  165. }
  166. FUNC_EXIT_RC(rc);
  167. return rc;
  168. }
  169. /** Write wire message to the client persistence directory.
  170. * See ::Persistence_put
  171. */
  172. int pstput(void* handle, char* key, int bufcount, char* buffers[], int buflens[])
  173. {
  174. int rc = 0;
  175. char *clientDir = handle;
  176. char *file;
  177. FILE *fp;
  178. size_t bytesWritten = 0,
  179. bytesTotal = 0;
  180. int i;
  181. size_t alloclen = 0;
  182. FUNC_ENTRY;
  183. if (clientDir == NULL)
  184. {
  185. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  186. goto exit;
  187. }
  188. /* consider '/' + '\0' */
  189. alloclen = strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2;
  190. file = malloc(alloclen);
  191. if (!file)
  192. {
  193. rc = PAHO_MEMORY_ERROR;
  194. goto exit;
  195. }
  196. if (snprintf(file, alloclen, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION) >= alloclen)
  197. {
  198. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  199. goto free_exit;
  200. }
  201. fp = fopen(file, "wb");
  202. if ( fp != NULL )
  203. {
  204. for(i=0; i<bufcount; i++)
  205. {
  206. bytesTotal += buflens[i];
  207. bytesWritten += fwrite(buffers[i], sizeof(char), buflens[i], fp );
  208. }
  209. fclose(fp);
  210. fp = NULL;
  211. } else
  212. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  213. if (bytesWritten != bytesTotal)
  214. {
  215. pstremove(handle, key);
  216. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  217. }
  218. free_exit:
  219. free(file);
  220. exit:
  221. FUNC_EXIT_RC(rc);
  222. return rc;
  223. };
  224. /** Retrieve a wire message from the client persistence directory.
  225. * See ::Persistence_get
  226. */
  227. int pstget(void* handle, char* key, char** buffer, int* buflen)
  228. {
  229. int rc = 0;
  230. FILE *fp = NULL;
  231. char *clientDir = handle;
  232. char *filename = NULL;
  233. char *buf = NULL;
  234. unsigned long fileLen = 0;
  235. unsigned long bytesRead = 0;
  236. size_t alloclen = 0;
  237. FUNC_ENTRY;
  238. if (clientDir == NULL)
  239. {
  240. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  241. goto exit;
  242. }
  243. /* consider '/' + '\0' */
  244. alloclen = strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2;
  245. filename = malloc(alloclen);
  246. if (!filename)
  247. {
  248. rc = PAHO_MEMORY_ERROR;
  249. goto exit;
  250. }
  251. if (snprintf(filename, alloclen, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION) >= alloclen)
  252. {
  253. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  254. free(filename);
  255. goto exit;
  256. }
  257. fp = fopen(filename, "rb");
  258. free(filename);
  259. if (fp != NULL)
  260. {
  261. fseek(fp, 0, SEEK_END);
  262. fileLen = ftell(fp);
  263. fseek(fp, 0, SEEK_SET);
  264. if ((buf = (char *)malloc(fileLen)) == NULL)
  265. {
  266. rc = PAHO_MEMORY_ERROR;
  267. goto exit;
  268. }
  269. bytesRead = (int)fread(buf, sizeof(char), fileLen, fp);
  270. *buffer = buf;
  271. *buflen = bytesRead;
  272. if ( bytesRead != fileLen )
  273. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  274. fclose(fp);
  275. } else
  276. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  277. /* the caller must free buf */
  278. exit:
  279. FUNC_EXIT_RC(rc);
  280. return rc;
  281. }
  282. /** Delete a persisted message from the client persistence directory.
  283. * See ::Persistence_remove
  284. */
  285. int pstremove(void* handle, char* key)
  286. {
  287. int rc = 0;
  288. char *clientDir = handle;
  289. char *file;
  290. size_t alloclen = 0;
  291. FUNC_ENTRY;
  292. if (clientDir == NULL)
  293. {
  294. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  295. goto exit;
  296. }
  297. /* consider '/' + '\0' */
  298. /* consider '/' + '\0' */
  299. alloclen = strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2;
  300. file = malloc(alloclen);
  301. if (!file)
  302. {
  303. rc = PAHO_MEMORY_ERROR;
  304. goto exit;
  305. }
  306. if (snprintf(file, alloclen, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION) >= alloclen)
  307. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  308. else
  309. {
  310. #if defined(_WIN32) || defined(_WIN64)
  311. if ( _unlink(file) != 0 )
  312. {
  313. #else
  314. if ( unlink(file) != 0 )
  315. {
  316. #endif
  317. if ( errno != ENOENT )
  318. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  319. }
  320. }
  321. free(file);
  322. exit:
  323. FUNC_EXIT_RC(rc);
  324. return rc;
  325. }
  326. /** Delete client persistence directory (if empty).
  327. * See ::Persistence_close
  328. */
  329. int pstclose(void* handle)
  330. {
  331. int rc = 0;
  332. char *clientDir = handle;
  333. FUNC_ENTRY;
  334. if (clientDir == NULL)
  335. {
  336. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  337. goto exit;
  338. }
  339. #if defined(_WIN32) || defined(_WIN64)
  340. if ( _rmdir(clientDir) != 0 )
  341. {
  342. #else
  343. if ( rmdir(clientDir) != 0 )
  344. {
  345. #endif
  346. if ( errno != ENOENT && errno != ENOTEMPTY )
  347. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  348. }
  349. free(clientDir);
  350. exit:
  351. FUNC_EXIT_RC(rc);
  352. return rc;
  353. }
  354. /** Returns whether if a wire message is persisted in the client persistence directory.
  355. * See ::Persistence_containskey
  356. */
  357. int pstcontainskey(void *handle, char *key)
  358. {
  359. int rc = 0;
  360. char *clientDir = handle;
  361. FUNC_ENTRY;
  362. if (clientDir == NULL)
  363. {
  364. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  365. goto exit;
  366. }
  367. #if defined(_WIN32) || defined(_WIN64)
  368. rc = containskeyWin32(clientDir, key);
  369. #else
  370. rc = containskeyUnix(clientDir, key);
  371. #endif
  372. exit:
  373. FUNC_EXIT_RC(rc);
  374. return rc;
  375. }
  376. #if defined(_WIN32) || defined(_WIN64)
  377. int containskeyWin32(char *dirname, char *key)
  378. {
  379. int notFound = MQTTCLIENT_PERSISTENCE_ERROR;
  380. int fFinished = 0;
  381. char *filekey, *ptraux;
  382. #if defined(_WIN32) || defined(_WIN64)
  383. #define DIRSIZE MAX_PATH+1
  384. #else
  385. const size_t DIRSIZE = MAX_PATH+1;
  386. #endif
  387. char dir[DIRSIZE];
  388. WIN32_FIND_DATAA FileData;
  389. HANDLE hDir;
  390. FUNC_ENTRY;
  391. if (snprintf(dir, DIRSIZE, "%s/*", dirname) >= DIRSIZE)
  392. goto exit;
  393. hDir = FindFirstFileA(dir, &FileData);
  394. if (hDir != INVALID_HANDLE_VALUE)
  395. {
  396. while (!fFinished)
  397. {
  398. if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
  399. {
  400. if ((filekey = malloc(strlen(FileData.cFileName) + 1)) == NULL)
  401. {
  402. notFound = PAHO_MEMORY_ERROR;
  403. goto exit;
  404. }
  405. strcpy(filekey, FileData.cFileName);
  406. ptraux = strstr(filekey, MESSAGE_FILENAME_EXTENSION);
  407. if ( ptraux != NULL )
  408. *ptraux = '\0' ;
  409. if(strcmp(filekey, key) == 0)
  410. {
  411. notFound = 0;
  412. fFinished = 1;
  413. }
  414. free(filekey);
  415. }
  416. if (!FindNextFileA(hDir, &FileData))
  417. {
  418. if (GetLastError() == ERROR_NO_MORE_FILES)
  419. fFinished = 1;
  420. }
  421. }
  422. FindClose(hDir);
  423. }
  424. exit:
  425. FUNC_EXIT_RC(notFound);
  426. return notFound;
  427. }
  428. #else
  429. int containskeyUnix(char *dirname, char *key)
  430. {
  431. int notFound = MQTTCLIENT_PERSISTENCE_ERROR;
  432. char *filekey, *ptraux;
  433. DIR *dp = NULL;
  434. struct dirent *dir_entry;
  435. struct stat stat_info;
  436. FUNC_ENTRY;
  437. if((dp = opendir(dirname)) != NULL)
  438. {
  439. while((dir_entry = readdir(dp)) != NULL && notFound)
  440. {
  441. const size_t allocsize = strlen(dirname) + strlen(dir_entry->d_name) + 2;
  442. char* filename = malloc(allocsize);
  443. if (!filename)
  444. {
  445. notFound = PAHO_MEMORY_ERROR;
  446. goto exit;
  447. }
  448. if (snprintf(filename, allocsize, "%s/%s", dirname, dir_entry->d_name) >= allocsize)
  449. {
  450. free(filename);
  451. notFound = MQTTCLIENT_PERSISTENCE_ERROR;
  452. goto exit;
  453. }
  454. lstat(filename, &stat_info);
  455. free(filename);
  456. if(S_ISREG(stat_info.st_mode))
  457. {
  458. if ((filekey = malloc(strlen(dir_entry->d_name) + 1)) == NULL)
  459. {
  460. notFound = PAHO_MEMORY_ERROR;
  461. goto exit;
  462. }
  463. strcpy(filekey, dir_entry->d_name);
  464. ptraux = strstr(filekey, MESSAGE_FILENAME_EXTENSION);
  465. if ( ptraux != NULL )
  466. *ptraux = '\0' ;
  467. if(strcmp(filekey, key) == 0)
  468. notFound = 0;
  469. free(filekey);
  470. }
  471. }
  472. }
  473. exit:
  474. if (dp)
  475. closedir(dp);
  476. FUNC_EXIT_RC(notFound);
  477. return notFound;
  478. }
  479. #endif
  480. /** Delete all the persisted message in the client persistence directory.
  481. * See ::Persistence_clear
  482. */
  483. int pstclear(void *handle)
  484. {
  485. int rc = 0;
  486. char *clientDir = handle;
  487. FUNC_ENTRY;
  488. if (clientDir == NULL)
  489. {
  490. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  491. goto exit;
  492. }
  493. #if defined(_WIN32) || defined(_WIN64)
  494. rc = clearWin32(clientDir);
  495. #else
  496. rc = clearUnix(clientDir);
  497. #endif
  498. exit:
  499. FUNC_EXIT_RC(rc);
  500. return rc;
  501. }
  502. #if defined(_WIN32) || defined(_WIN64)
  503. int clearWin32(char *dirname)
  504. {
  505. int rc = 0;
  506. int fFinished = 0;
  507. char *file;
  508. char dir[DIRSIZE];
  509. WIN32_FIND_DATAA FileData;
  510. HANDLE hDir;
  511. FUNC_ENTRY;
  512. if (snprintf(dir, DIRSIZE, "%s/*", dirname) >= DIRSIZE)
  513. {
  514. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  515. goto exit;
  516. }
  517. hDir = FindFirstFileA(dir, &FileData);
  518. if (hDir != INVALID_HANDLE_VALUE)
  519. {
  520. while (!fFinished)
  521. {
  522. if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
  523. {
  524. size_t allocsize = strlen(dirname) + strlen(FileData.cFileName) + 2;
  525. file = malloc(allocsize);
  526. if (!file)
  527. {
  528. rc = PAHO_MEMORY_ERROR;
  529. goto exit;
  530. }
  531. if (snprintf(file, allocsize, "%s/%s", dirname, FileData.cFileName) >= allocsize)
  532. {
  533. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  534. free(file);
  535. goto exit;
  536. }
  537. rc = remove(file);
  538. free(file);
  539. if ( rc != 0 )
  540. {
  541. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  542. break;
  543. }
  544. }
  545. if (!FindNextFileA(hDir, &FileData))
  546. {
  547. if (GetLastError() == ERROR_NO_MORE_FILES)
  548. fFinished = 1;
  549. }
  550. }
  551. FindClose(hDir);
  552. } else
  553. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  554. exit:
  555. FUNC_EXIT_RC(rc);
  556. return rc;
  557. }
  558. #else
  559. int clearUnix(char *dirname)
  560. {
  561. int rc = 0;
  562. DIR *dp;
  563. struct dirent *dir_entry;
  564. struct stat stat_info;
  565. FUNC_ENTRY;
  566. if((dp = opendir(dirname)) != NULL)
  567. {
  568. while((dir_entry = readdir(dp)) != NULL && rc == 0)
  569. {
  570. if (lstat(dir_entry->d_name, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
  571. {
  572. if (remove(dir_entry->d_name) != 0 && errno != ENOENT)
  573. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  574. }
  575. }
  576. closedir(dp);
  577. } else
  578. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  579. FUNC_EXIT_RC(rc);
  580. return rc;
  581. }
  582. #endif
  583. /** Returns the keys (file names w/o the extension) in the client persistence directory.
  584. * See ::Persistence_keys
  585. */
  586. int pstkeys(void *handle, char ***keys, int *nkeys)
  587. {
  588. int rc = 0;
  589. char *clientDir = handle;
  590. FUNC_ENTRY;
  591. if (clientDir == NULL)
  592. {
  593. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  594. goto exit;
  595. }
  596. #if defined(_WIN32) || defined(_WIN64)
  597. rc = keysWin32(clientDir, keys, nkeys);
  598. #else
  599. rc = keysUnix(clientDir, keys, nkeys);
  600. #endif
  601. exit:
  602. FUNC_EXIT_RC(rc);
  603. return rc;
  604. }
  605. #if defined(_WIN32) || defined(_WIN64)
  606. int keysWin32(char *dirname, char ***keys, int *nkeys)
  607. {
  608. int rc = 0;
  609. char **fkeys = NULL;
  610. int nfkeys = 0;
  611. char dir[DIRSIZE];
  612. WIN32_FIND_DATAA FileData;
  613. HANDLE hDir;
  614. int fFinished = 0;
  615. char *ptraux;
  616. int i;
  617. FUNC_ENTRY;
  618. if (snprintf(dir, DIRSIZE, "%s/*", dirname) >= DIRSIZE)
  619. {
  620. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  621. goto exit;
  622. }
  623. /* get number of keys */
  624. hDir = FindFirstFileA(dir, &FileData);
  625. if (hDir != INVALID_HANDLE_VALUE)
  626. {
  627. while (!fFinished)
  628. {
  629. if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
  630. nfkeys++;
  631. if (!FindNextFileA(hDir, &FileData))
  632. {
  633. if (GetLastError() == ERROR_NO_MORE_FILES)
  634. fFinished = 1;
  635. }
  636. }
  637. FindClose(hDir);
  638. } else
  639. {
  640. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  641. goto exit;
  642. }
  643. if (nfkeys != 0)
  644. {
  645. if ((fkeys = (char **)malloc(nfkeys * sizeof(char *))) == NULL)
  646. {
  647. rc = PAHO_MEMORY_ERROR;
  648. goto exit;
  649. }
  650. }
  651. /* copy the keys */
  652. hDir = FindFirstFileA(dir, &FileData);
  653. if (hDir != INVALID_HANDLE_VALUE)
  654. {
  655. fFinished = 0;
  656. i = 0;
  657. while (!fFinished)
  658. {
  659. if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
  660. {
  661. if ((fkeys[i] = malloc(strlen(FileData.cFileName) + 1)) == NULL)
  662. {
  663. rc = PAHO_MEMORY_ERROR;
  664. goto exit;
  665. }
  666. strcpy(fkeys[i], FileData.cFileName);
  667. ptraux = strstr(fkeys[i], MESSAGE_FILENAME_EXTENSION);
  668. if ( ptraux != NULL )
  669. *ptraux = '\0' ;
  670. i++;
  671. }
  672. if (!FindNextFileA(hDir, &FileData))
  673. {
  674. if (GetLastError() == ERROR_NO_MORE_FILES)
  675. fFinished = 1;
  676. }
  677. }
  678. FindClose(hDir);
  679. } else
  680. {
  681. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  682. goto exit;
  683. }
  684. *nkeys = nfkeys;
  685. *keys = fkeys;
  686. /* the caller must free keys */
  687. exit:
  688. FUNC_EXIT_RC(rc);
  689. return rc;
  690. }
  691. #else
  692. int keysUnix(char *dirname, char ***keys, int *nkeys)
  693. {
  694. int rc = 0;
  695. char **fkeys = NULL;
  696. int nfkeys = 0;
  697. char *ptraux;
  698. int i;
  699. DIR *dp = NULL;
  700. struct dirent *dir_entry;
  701. struct stat stat_info;
  702. FUNC_ENTRY;
  703. /* get number of keys */
  704. if((dp = opendir(dirname)) != NULL)
  705. {
  706. while((dir_entry = readdir(dp)) != NULL)
  707. {
  708. size_t allocsize = strlen(dirname)+strlen(dir_entry->d_name)+2;
  709. char* temp = malloc(allocsize);
  710. if (!temp)
  711. {
  712. rc = PAHO_MEMORY_ERROR;
  713. goto exit;
  714. }
  715. if (snprintf(temp, allocsize, "%s/%s", dirname, dir_entry->d_name) >= allocsize)
  716. {
  717. free(temp);
  718. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  719. goto exit;
  720. }
  721. if (lstat(temp, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
  722. nfkeys++;
  723. free(temp);
  724. }
  725. closedir(dp);
  726. dp = NULL;
  727. } else
  728. {
  729. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  730. goto exit;
  731. }
  732. if (nfkeys != 0)
  733. {
  734. if ((fkeys = (char **)malloc(nfkeys * sizeof(char *))) == NULL)
  735. {
  736. rc = PAHO_MEMORY_ERROR;
  737. goto exit;
  738. }
  739. /* copy the keys */
  740. if((dp = opendir(dirname)) != NULL)
  741. {
  742. i = 0;
  743. while((dir_entry = readdir(dp)) != NULL)
  744. {
  745. size_t allocsize = strlen(dirname)+strlen(dir_entry->d_name)+2;
  746. char* temp = malloc(allocsize);
  747. if (!temp)
  748. {
  749. free(fkeys);
  750. rc = PAHO_MEMORY_ERROR;
  751. goto exit;
  752. }
  753. if (snprintf(temp, allocsize, "%s/%s", dirname, dir_entry->d_name) >= allocsize)
  754. {
  755. free(temp);
  756. free(fkeys);
  757. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  758. goto exit;
  759. }
  760. if (lstat(temp, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
  761. {
  762. if ((fkeys[i] = malloc(strlen(dir_entry->d_name) + 1)) == NULL)
  763. {
  764. free(temp);
  765. free(fkeys);
  766. rc = PAHO_MEMORY_ERROR;
  767. goto exit;
  768. }
  769. strcpy(fkeys[i], dir_entry->d_name);
  770. ptraux = strstr(fkeys[i], MESSAGE_FILENAME_EXTENSION);
  771. if ( ptraux != NULL )
  772. *ptraux = '\0' ;
  773. i++;
  774. }
  775. free(temp);
  776. }
  777. } else
  778. {
  779. rc = MQTTCLIENT_PERSISTENCE_ERROR;
  780. goto exit;
  781. }
  782. }
  783. *nkeys = nfkeys;
  784. *keys = fkeys;
  785. /* the caller must free keys */
  786. exit:
  787. if (dp)
  788. closedir(dp);
  789. FUNC_EXIT_RC(rc);
  790. return rc;
  791. }
  792. #endif
  793. #if defined(UNIT_TESTS)
  794. int main (int argc, char *argv[])
  795. {
  796. #define MSTEM "m-"
  797. #define NMSGS 10
  798. #define NBUFS 4
  799. #define NDEL 2
  800. #define RC !rc ? "(Success)" : "(Failed) "
  801. int rc;
  802. char *handle;
  803. char *perdir = ".";
  804. const char *clientID = "TheUTClient";
  805. const char *serverURI = "127.0.0.1:1883";
  806. char *stem = MSTEM;
  807. int msgId, i;
  808. int nm[NDEL] = {5 , 8}; /* msgIds to get and remove */
  809. char *key;
  810. char **keys;
  811. int nkeys;
  812. char *buffer, *buff;
  813. int buflen;
  814. int nbufs = NBUFS;
  815. char *bufs[NBUFS] = {"m0", "mm1", "mmm2" , "mmmm3"}; /* message content */
  816. int buflens[NBUFS];
  817. for(i=0;i<nbufs;i++)
  818. buflens[i]=strlen(bufs[i]);
  819. /* open */
  820. /* printf("Persistence directory : %s\n", perdir); */
  821. rc = pstopen((void**)&handle, clientID, serverURI, perdir);
  822. printf("%s Persistence directory for client %s : %s\n", RC, clientID, handle);
  823. /* put */
  824. for(msgId=0;msgId<NMSGS;msgId++)
  825. {
  826. key = malloc(PERSISTENCE_MAX_KEY_LENGTH + 1);
  827. sprintf(key, "%s%d", stem, msgId);
  828. rc = pstput(handle, key, nbufs, bufs, buflens);
  829. printf("%s Adding message %s\n", RC, key);
  830. free(key);
  831. }
  832. /* keys ,ie, list keys added */
  833. rc = pstkeys(handle, &keys, &nkeys);
  834. printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
  835. for(i=0;i<nkeys;i++)
  836. printf("%13s\n", keys[i]);
  837. if (keys !=NULL)
  838. free(keys);
  839. /* containskey */
  840. for(i=0;i<NDEL;i++)
  841. {
  842. key = malloc(PERSISTENCE_MAX_KEY_LENGTH + 1);
  843. sprintf(key, "%s%d", stem, nm[i]);
  844. rc = pstcontainskey(handle, key);
  845. printf("%s Message %s is persisted ?\n", RC, key);
  846. free(key);
  847. }
  848. /* get && remove*/
  849. for(i=0;i<NDEL;i++)
  850. {
  851. key = malloc(PERSISTENCE_MAX_KEY_LENGTH + 1);
  852. sprintf(key, "%s%d", stem, nm[i]);
  853. rc = pstget(handle, key, &buffer, &buflen);
  854. buff = malloc(buflen+1);
  855. memcpy(buff, buffer, buflen);
  856. buff[buflen] = '\0';
  857. printf("%s Retrieving message %s : %s\n", RC, key, buff);
  858. rc = pstremove(handle, key);
  859. printf("%s Removing message %s\n", RC, key);
  860. free(key);
  861. free(buff);
  862. free(buffer);
  863. }
  864. /* containskey */
  865. for(i=0;i<NDEL;i++)
  866. {
  867. key = malloc(PERSISTENCE_MAX_KEY_LENGTH + 1);
  868. sprintf(key, "%s%d", stem, nm[i]);
  869. rc = pstcontainskey(handle, key);
  870. printf("%s Message %s is persisted ?\n", RC, key);
  871. free(key);
  872. }
  873. /* keys ,ie, list keys added */
  874. rc = pstkeys(handle, &keys, &nkeys);
  875. printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
  876. for(i=0;i<nkeys;i++)
  877. printf("%13s\n", keys[i]);
  878. if (keys != NULL)
  879. free(keys);
  880. /* close -> it will fail, since client persistence directory is not empty */
  881. rc = pstclose(&handle);
  882. printf("%s Closing client persistence directory for client %s\n", RC, clientID);
  883. /* clear */
  884. rc = pstclear(handle);
  885. printf("%s Deleting all persisted messages in %s\n", RC, handle);
  886. /* keys ,ie, list keys added */
  887. rc = pstkeys(handle, &keys, &nkeys);
  888. printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
  889. for(i=0;i<nkeys;i++)
  890. printf("%13s\n", keys[i]);
  891. if ( keys != NULL )
  892. free(keys);
  893. /* close */
  894. rc = pstclose(&handle);
  895. printf("%s Closing client persistence directory for client %s\n", RC, clientID);
  896. }
  897. #endif
  898. #endif /* NO_PERSISTENCE */