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