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