Compare commits
276 Commits
2b738f6f43
...
mqtt_clien
| Author | SHA1 | Date | |
|---|---|---|---|
| feb86b15f8 | |||
| 767bb2b382 | |||
| 3e5714a5e1 | |||
| 6e4a64e64a | |||
| 40cdc90131 | |||
| d758cea974 | |||
| ed13838237 | |||
| 07ae277f9e | |||
| 46f86b6591 | |||
| 9588b48105 | |||
| 1739836a18 | |||
| 7195734765 | |||
| 8ecec6b914 | |||
| 9029bcf099 | |||
| 6f1660fd9e | |||
| f50a3abc8e | |||
| 8c15113cb0 | |||
| 4253acb72b | |||
| e22630b1bd | |||
| 563d9c5487 | |||
| 34bc322b9b | |||
| 6c3c763934 | |||
| 4d841787fc | |||
| 790246afea | |||
| 978e350722 | |||
| 1d76cacae2 | |||
| db954ffdaa | |||
| 8d6ae976a3 | |||
| e767fae934 | |||
| 5db97ca959 | |||
| daab41e41e | |||
| a61c8477c7 | |||
| 788ad8f2c0 | |||
| 78afc179c4 | |||
| 69ec4c9837 | |||
| 2368de6e93 | |||
| e5df76ab1d | |||
| fdec0e66a8 | |||
| 5f3baa5580 | |||
| 7083b2c32b | |||
| af02684dc5 | |||
| 2806086558 | |||
| ce962bfb40 | |||
| dcdd7db33d | |||
| 3c72db2de8 | |||
| 53faaeb396 | |||
| 2928a690b8 | |||
| 220ce225f8 | |||
| ac89c499ab | |||
| 8d1c97da04 | |||
| aa140fd4ec | |||
| 08161c9aad | |||
| 40cda7d988 | |||
| c05b8b4095 | |||
| 3d7ba1dee6 | |||
| a299ada873 | |||
| 91144ad338 | |||
| ef8b785ac6 | |||
| 27f37c9cc1 | |||
| f2464ed76b | |||
| a3615c5666 | |||
| 1b3f72d429 | |||
| 04152f05a9 | |||
| d95944dcfc | |||
| a17644a953 | |||
| 3426a3064e | |||
| a41379a40e | |||
| 39266f8c3c | |||
| 3bcb778628 | |||
| 781b430c33 | |||
| 0ac7ea3096 | |||
| d6a0ae6106 | |||
| 3625afa783 | |||
| 154cbd0160 | |||
| b6c5d65a8d | |||
| d4254121b8 | |||
| da30ae558f | |||
| 7a6936ccd9 | |||
| 58acf7a823 | |||
| d9719a7a50 | |||
| bf63365370 | |||
| 64e142b8c6 | |||
| be2d3c197c | |||
| 30c4f215a2 | |||
| 6ffbbbe636 | |||
| d62599fb8e | |||
| 7e371132ae | |||
| 3504c25f84 | |||
| 68615035aa | |||
| 654c0847b2 | |||
| 91955d44fa | |||
| a2c24c9f07 | |||
| cf25cacc17 | |||
| 19daab173c | |||
| 19f8f522b3 | |||
| 905d39c07b | |||
| 9504792586 | |||
| 1b51d09c58 | |||
| ed3d4c4217 | |||
| 7b52f6d70d | |||
| 19fe33383a | |||
| e57719c118 | |||
| edb077b400 | |||
| bad51db0c8 | |||
| 87ab4c4c28 | |||
| 8f3456a650 | |||
| ce1aee1553 | |||
| dfdc4b8bdc | |||
| 8a61cfe7ef | |||
| 78f79d92aa | |||
| f09fe03e0d | |||
| 8e18ec15c9 | |||
| 4340f5444d | |||
| 8e96750046 | |||
| d01baffb0b | |||
| 82cda42e75 | |||
| 5755d172cd | |||
| 0dd47f50a0 | |||
| 60d2a83df5 | |||
| 7757ac10ec | |||
| f334a6603f | |||
| 15548de79c | |||
| 49713ddc57 | |||
| 0973379f53 | |||
| a3004491d6 | |||
| 199a272eb8 | |||
| 855d1ca1b5 | |||
| 64025e0399 | |||
| 6e5556969d | |||
| b50eeaef46 | |||
| ffc471359a | |||
| 79007e7b4e | |||
| 9e08b6ffc5 | |||
| 9b8a1583c2 | |||
| cae264a77b | |||
| 4f934fef35 | |||
| 3a159b0553 | |||
| 0c973f0216 | |||
| 65d3168eb5 | |||
| 12114b3e77 | |||
| 6f6b717354 | |||
| 58b3fa64bc | |||
| 1acaf24df9 | |||
| c2c9822169 | |||
| dff4f2b3a0 | |||
| bf9ad65ff0 | |||
| 53ec75bf0c | |||
| f5270d75eb | |||
| 4aa596b179 | |||
| 002f21fc9e | |||
| 9ab46e4afc | |||
| caa7880cc4 | |||
| 24112498ce | |||
| c9a5ddd89f | |||
| 3483edbd76 | |||
| fe82c12d5b | |||
| d90c3f0991 | |||
| f6b9131f4a | |||
| c67f7a2b64 | |||
| 047d38ea59 | |||
| 3d07795515 | |||
| ee137b2820 | |||
| cdde340efe | |||
| ee34e8a72e | |||
| 0840d807a0 | |||
| 4655d72554 | |||
| 5b066cbc27 | |||
| 2247473959 | |||
| 57f8c1313e | |||
| 73ed51e3d4 | |||
| 1106cde3e4 | |||
| b43158d3a8 | |||
| 9a928f6feb | |||
| df75efe881 | |||
| 9f1d23ad8e | |||
| 7cd2f7a310 | |||
| 315966504e | |||
| 7209eec012 | |||
| 992f59904a | |||
| 9dbd7210cb | |||
| ac8efc9f88 | |||
| 92a0a9356c | |||
| 28f3471036 | |||
| d3d7235338 | |||
| 92a87a0c64 | |||
| cd7e053fc5 | |||
| 9eecbbab6e | |||
| 3641e636d2 | |||
| 4acab04895 | |||
| aa963a4bda | |||
| bdd18b614f | |||
| e186e0adff | |||
| f105f616f6 | |||
|
|
b99c51181d | ||
|
|
bc6b584480 | ||
| eb97de1413 | |||
| 97f1c25ff8 | |||
| 97aad47a21 | |||
| 43bd1d8550 | |||
|
|
3255199b3f | ||
| 224412e20a | |||
| 000ce2a54d | |||
|
|
f992bf4cbb | ||
|
|
96625bd93d | ||
| 9d4357c066 | |||
| 7a945f47b1 | |||
| 9a352bfc83 | |||
| aa2f117075 | |||
| bf5bb45771 | |||
| cdc966bd8c | |||
| 17b902ebcc | |||
| 996b7ea403 | |||
| da4b09be9e | |||
| b24b5a1346 | |||
| 0d94699206 | |||
| 16a818c95e | |||
| a0454b809d | |||
| af8c096c7a | |||
| 14cf81aed0 | |||
| c43b914fb3 | |||
| f002f6cedd | |||
| ce846eca51 | |||
| e6c8714857 | |||
| 33fc334077 | |||
| 6efc962923 | |||
| 7d02f710ea | |||
| c8876807ed | |||
| f43f834460 | |||
| ccf6b810b5 | |||
| a438c4249d | |||
| 1c7fc39b6c | |||
| f07c9cbce8 | |||
| abdba6d68b | |||
| 3db26a762c | |||
| b35561f74e | |||
| f041d1435e | |||
| b781bd5148 | |||
| a16e0b7659 | |||
| 0bafd3fa98 | |||
| 903b320629 | |||
| 9cd05996e7 | |||
| 15dc76c4a7 | |||
| d46b5e008a | |||
| af4b718053 | |||
| 7eae1e127c | |||
| f97fed7daa | |||
| d764171c82 | |||
| d4a024ea75 | |||
| 491d89f117 | |||
| a86e8f7b58 | |||
| d97798d063 | |||
| 247759b364 | |||
| a745f803b3 | |||
| 1b67000887 | |||
| 04d3e31dbc | |||
| 9f29155d07 | |||
| 021411defa | |||
| ee4d78d2e1 | |||
| 9283c88b4e | |||
| 8d585439bb | |||
| ebf2b08bb1 | |||
| eb21c85170 | |||
| fb68a9f9fe | |||
| c7c3852747 | |||
| c18d0c918e | |||
| 9c4fd41eef | |||
| 9173dc936d | |||
| 77c8681b43 | |||
| 2db9440a38 | |||
| 0d585cfebf | |||
| 02a9bfb76f | |||
| ad7385c21f | |||
| dd6d91ac1d | |||
| 576d9c79be | |||
| 3fa5d9d9df | |||
| b14d30108a |
@@ -134,8 +134,8 @@ JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: "PRIVATE_DEFINITION_START|STATIC_INITIALIZER_BEGIN"
|
||||
MacroBlockEnd: "PRIVATE_DEFINITION_END|STATIC_INITIALIZER_END"
|
||||
MacroBlockBegin: "PRIVATE_DEFINITION_START|STATIC_INITIALIZER_BEGIN|DECLARE_UNIT_CLASS_BEGIN"
|
||||
MacroBlockEnd: "PRIVATE_DEFINITION_END|PRIVATE_DEFINITION_END_NO_INITIALIZE|STATIC_INITIALIZER_END|DECLARE_UNIT_CLASS_END"
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,4 +3,8 @@
|
||||
/doc/rtf
|
||||
_unsused
|
||||
CMakeLists.txt.user*
|
||||
/include
|
||||
/include
|
||||
/release
|
||||
/build*
|
||||
/AGENTS.md
|
||||
/plans
|
||||
|
||||
121
3rd/BLAKE2/COPYING
Normal file
121
3rd/BLAKE2/COPYING
Normal file
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
160
3rd/BLAKE2/blake2-impl.h
Normal file
160
3rd/BLAKE2/blake2-impl.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
|
||||
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
|
||||
your option. The terms of these licenses can be found at:
|
||||
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
*/
|
||||
#ifndef BLAKE2_IMPL_H
|
||||
#define BLAKE2_IMPL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
|
||||
#if defined(_MSC_VER)
|
||||
#define BLAKE2_INLINE __inline
|
||||
#elif defined(__GNUC__)
|
||||
#define BLAKE2_INLINE __inline__
|
||||
#else
|
||||
#define BLAKE2_INLINE
|
||||
#endif
|
||||
#else
|
||||
#define BLAKE2_INLINE inline
|
||||
#endif
|
||||
|
||||
static BLAKE2_INLINE uint32_t load32( const void *src )
|
||||
{
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
uint32_t w;
|
||||
memcpy(&w, src, sizeof w);
|
||||
return w;
|
||||
#else
|
||||
const uint8_t *p = ( const uint8_t * )src;
|
||||
return (( uint32_t )( p[0] ) << 0) |
|
||||
(( uint32_t )( p[1] ) << 8) |
|
||||
(( uint32_t )( p[2] ) << 16) |
|
||||
(( uint32_t )( p[3] ) << 24) ;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint64_t load64( const void *src )
|
||||
{
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
uint64_t w;
|
||||
memcpy(&w, src, sizeof w);
|
||||
return w;
|
||||
#else
|
||||
const uint8_t *p = ( const uint8_t * )src;
|
||||
return (( uint64_t )( p[0] ) << 0) |
|
||||
(( uint64_t )( p[1] ) << 8) |
|
||||
(( uint64_t )( p[2] ) << 16) |
|
||||
(( uint64_t )( p[3] ) << 24) |
|
||||
(( uint64_t )( p[4] ) << 32) |
|
||||
(( uint64_t )( p[5] ) << 40) |
|
||||
(( uint64_t )( p[6] ) << 48) |
|
||||
(( uint64_t )( p[7] ) << 56) ;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint16_t load16( const void *src )
|
||||
{
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
uint16_t w;
|
||||
memcpy(&w, src, sizeof w);
|
||||
return w;
|
||||
#else
|
||||
const uint8_t *p = ( const uint8_t * )src;
|
||||
return ( uint16_t )((( uint32_t )( p[0] ) << 0) |
|
||||
(( uint32_t )( p[1] ) << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void store16( void *dst, uint16_t w )
|
||||
{
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
memcpy(dst, &w, sizeof w);
|
||||
#else
|
||||
uint8_t *p = ( uint8_t * )dst;
|
||||
*p++ = ( uint8_t )w; w >>= 8;
|
||||
*p++ = ( uint8_t )w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void store32( void *dst, uint32_t w )
|
||||
{
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
memcpy(dst, &w, sizeof w);
|
||||
#else
|
||||
uint8_t *p = ( uint8_t * )dst;
|
||||
p[0] = (uint8_t)(w >> 0);
|
||||
p[1] = (uint8_t)(w >> 8);
|
||||
p[2] = (uint8_t)(w >> 16);
|
||||
p[3] = (uint8_t)(w >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void store64( void *dst, uint64_t w )
|
||||
{
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
memcpy(dst, &w, sizeof w);
|
||||
#else
|
||||
uint8_t *p = ( uint8_t * )dst;
|
||||
p[0] = (uint8_t)(w >> 0);
|
||||
p[1] = (uint8_t)(w >> 8);
|
||||
p[2] = (uint8_t)(w >> 16);
|
||||
p[3] = (uint8_t)(w >> 24);
|
||||
p[4] = (uint8_t)(w >> 32);
|
||||
p[5] = (uint8_t)(w >> 40);
|
||||
p[6] = (uint8_t)(w >> 48);
|
||||
p[7] = (uint8_t)(w >> 56);
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint64_t load48( const void *src )
|
||||
{
|
||||
const uint8_t *p = ( const uint8_t * )src;
|
||||
return (( uint64_t )( p[0] ) << 0) |
|
||||
(( uint64_t )( p[1] ) << 8) |
|
||||
(( uint64_t )( p[2] ) << 16) |
|
||||
(( uint64_t )( p[3] ) << 24) |
|
||||
(( uint64_t )( p[4] ) << 32) |
|
||||
(( uint64_t )( p[5] ) << 40) ;
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void store48( void *dst, uint64_t w )
|
||||
{
|
||||
uint8_t *p = ( uint8_t * )dst;
|
||||
p[0] = (uint8_t)(w >> 0);
|
||||
p[1] = (uint8_t)(w >> 8);
|
||||
p[2] = (uint8_t)(w >> 16);
|
||||
p[3] = (uint8_t)(w >> 24);
|
||||
p[4] = (uint8_t)(w >> 32);
|
||||
p[5] = (uint8_t)(w >> 40);
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c )
|
||||
{
|
||||
return ( w >> c ) | ( w << ( 32 - c ) );
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c )
|
||||
{
|
||||
return ( w >> c ) | ( w << ( 64 - c ) );
|
||||
}
|
||||
|
||||
/* prevents compiler optimizing out memset() */
|
||||
static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n)
|
||||
{
|
||||
static void *(*const volatile memset_v)(void *, int, size_t) = &memset;
|
||||
memset_v(v, 0, n);
|
||||
}
|
||||
|
||||
#endif
|
||||
195
3rd/BLAKE2/blake2.h
Normal file
195
3rd/BLAKE2/blake2.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
|
||||
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
|
||||
your option. The terms of these licenses can be found at:
|
||||
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
*/
|
||||
#ifndef BLAKE2_H
|
||||
#define BLAKE2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
|
||||
#else
|
||||
#define BLAKE2_PACKED(x) x __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum blake2s_constant
|
||||
{
|
||||
BLAKE2S_BLOCKBYTES = 64,
|
||||
BLAKE2S_OUTBYTES = 32,
|
||||
BLAKE2S_KEYBYTES = 32,
|
||||
BLAKE2S_SALTBYTES = 8,
|
||||
BLAKE2S_PERSONALBYTES = 8
|
||||
};
|
||||
|
||||
enum blake2b_constant
|
||||
{
|
||||
BLAKE2B_BLOCKBYTES = 128,
|
||||
BLAKE2B_OUTBYTES = 64,
|
||||
BLAKE2B_KEYBYTES = 64,
|
||||
BLAKE2B_SALTBYTES = 16,
|
||||
BLAKE2B_PERSONALBYTES = 16
|
||||
};
|
||||
|
||||
typedef struct blake2s_state__
|
||||
{
|
||||
uint32_t h[8];
|
||||
uint32_t t[2];
|
||||
uint32_t f[2];
|
||||
uint8_t buf[BLAKE2S_BLOCKBYTES];
|
||||
size_t buflen;
|
||||
size_t outlen;
|
||||
uint8_t last_node;
|
||||
} blake2s_state;
|
||||
|
||||
typedef struct blake2b_state__
|
||||
{
|
||||
uint64_t h[8];
|
||||
uint64_t t[2];
|
||||
uint64_t f[2];
|
||||
uint8_t buf[BLAKE2B_BLOCKBYTES];
|
||||
size_t buflen;
|
||||
size_t outlen;
|
||||
uint8_t last_node;
|
||||
} blake2b_state;
|
||||
|
||||
typedef struct blake2sp_state__
|
||||
{
|
||||
blake2s_state S[8][1];
|
||||
blake2s_state R[1];
|
||||
uint8_t buf[8 * BLAKE2S_BLOCKBYTES];
|
||||
size_t buflen;
|
||||
size_t outlen;
|
||||
} blake2sp_state;
|
||||
|
||||
typedef struct blake2bp_state__
|
||||
{
|
||||
blake2b_state S[4][1];
|
||||
blake2b_state R[1];
|
||||
uint8_t buf[4 * BLAKE2B_BLOCKBYTES];
|
||||
size_t buflen;
|
||||
size_t outlen;
|
||||
} blake2bp_state;
|
||||
|
||||
|
||||
BLAKE2_PACKED(struct blake2s_param__
|
||||
{
|
||||
uint8_t digest_length; /* 1 */
|
||||
uint8_t key_length; /* 2 */
|
||||
uint8_t fanout; /* 3 */
|
||||
uint8_t depth; /* 4 */
|
||||
uint32_t leaf_length; /* 8 */
|
||||
uint32_t node_offset; /* 12 */
|
||||
uint16_t xof_length; /* 14 */
|
||||
uint8_t node_depth; /* 15 */
|
||||
uint8_t inner_length; /* 16 */
|
||||
/* uint8_t reserved[0]; */
|
||||
uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */
|
||||
uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */
|
||||
});
|
||||
|
||||
typedef struct blake2s_param__ blake2s_param;
|
||||
|
||||
BLAKE2_PACKED(struct blake2b_param__
|
||||
{
|
||||
uint8_t digest_length; /* 1 */
|
||||
uint8_t key_length; /* 2 */
|
||||
uint8_t fanout; /* 3 */
|
||||
uint8_t depth; /* 4 */
|
||||
uint32_t leaf_length; /* 8 */
|
||||
uint32_t node_offset; /* 12 */
|
||||
uint32_t xof_length; /* 16 */
|
||||
uint8_t node_depth; /* 17 */
|
||||
uint8_t inner_length; /* 18 */
|
||||
uint8_t reserved[14]; /* 32 */
|
||||
uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
|
||||
uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
|
||||
});
|
||||
|
||||
typedef struct blake2b_param__ blake2b_param;
|
||||
|
||||
typedef struct blake2xs_state__
|
||||
{
|
||||
blake2s_state S[1];
|
||||
blake2s_param P[1];
|
||||
} blake2xs_state;
|
||||
|
||||
typedef struct blake2xb_state__
|
||||
{
|
||||
blake2b_state S[1];
|
||||
blake2b_param P[1];
|
||||
} blake2xb_state;
|
||||
|
||||
/* Padded structs result in a compile-time error */
|
||||
enum {
|
||||
BLAKE2_DUMMY_1 = 1/(int)(sizeof(blake2s_param) == BLAKE2S_OUTBYTES),
|
||||
BLAKE2_DUMMY_2 = 1/(int)(sizeof(blake2b_param) == BLAKE2B_OUTBYTES)
|
||||
};
|
||||
|
||||
/* Streaming API */
|
||||
int blake2s_init( blake2s_state *S, size_t outlen );
|
||||
int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
|
||||
int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
|
||||
int blake2s_update( blake2s_state *S, const void *in, size_t inlen );
|
||||
int blake2s_final( blake2s_state *S, void *out, size_t outlen );
|
||||
|
||||
int blake2b_init( blake2b_state *S, size_t outlen );
|
||||
int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
|
||||
int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
|
||||
int blake2b_update( blake2b_state *S, const void *in, size_t inlen );
|
||||
int blake2b_final( blake2b_state *S, void *out, size_t outlen );
|
||||
|
||||
int blake2sp_init( blake2sp_state *S, size_t outlen );
|
||||
int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
|
||||
int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
|
||||
int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
|
||||
|
||||
int blake2bp_init( blake2bp_state *S, size_t outlen );
|
||||
int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
|
||||
int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
|
||||
int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
|
||||
|
||||
/* Variable output length API */
|
||||
int blake2xs_init( blake2xs_state *S, const size_t outlen );
|
||||
int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
|
||||
int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
|
||||
int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
|
||||
|
||||
int blake2xb_init( blake2xb_state *S, const size_t outlen );
|
||||
int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
|
||||
int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
|
||||
int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
|
||||
|
||||
/* Simple API */
|
||||
int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
|
||||
int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
|
||||
|
||||
int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
|
||||
int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
|
||||
|
||||
int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
|
||||
int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
|
||||
|
||||
/* This is simply an alias for blake2b */
|
||||
int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
379
3rd/BLAKE2/blake2b-ref.c
Normal file
379
3rd/BLAKE2/blake2b-ref.c
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
|
||||
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
|
||||
your option. The terms of these licenses can be found at:
|
||||
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "blake2.h"
|
||||
#include "blake2-impl.h"
|
||||
|
||||
static const uint64_t blake2b_IV[8] =
|
||||
{
|
||||
0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
|
||||
0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
|
||||
0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
|
||||
0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
|
||||
};
|
||||
|
||||
static const uint8_t blake2b_sigma[12][16] =
|
||||
{
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
|
||||
};
|
||||
|
||||
|
||||
static void blake2b_set_lastnode( blake2b_state *S )
|
||||
{
|
||||
S->f[1] = (uint64_t)-1;
|
||||
}
|
||||
|
||||
/* Some helper functions, not necessarily useful */
|
||||
static int blake2b_is_lastblock( const blake2b_state *S )
|
||||
{
|
||||
return S->f[0] != 0;
|
||||
}
|
||||
|
||||
static void blake2b_set_lastblock( blake2b_state *S )
|
||||
{
|
||||
if( S->last_node ) blake2b_set_lastnode( S );
|
||||
|
||||
S->f[0] = (uint64_t)-1;
|
||||
}
|
||||
|
||||
static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc )
|
||||
{
|
||||
S->t[0] += inc;
|
||||
S->t[1] += ( S->t[0] < inc );
|
||||
}
|
||||
|
||||
static void blake2b_init0( blake2b_state *S )
|
||||
{
|
||||
size_t i;
|
||||
memset( S, 0, sizeof( blake2b_state ) );
|
||||
|
||||
for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];
|
||||
}
|
||||
|
||||
/* init xors IV with input parameter block */
|
||||
int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
|
||||
{
|
||||
const uint8_t *p = ( const uint8_t * )( P );
|
||||
size_t i;
|
||||
|
||||
blake2b_init0( S );
|
||||
|
||||
/* IV XOR ParamBlock */
|
||||
for( i = 0; i < 8; ++i )
|
||||
S->h[i] ^= load64( p + sizeof( S->h[i] ) * i );
|
||||
|
||||
S->outlen = P->digest_length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int blake2b_init( blake2b_state *S, size_t outlen )
|
||||
{
|
||||
blake2b_param P[1];
|
||||
|
||||
if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
|
||||
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = 0;
|
||||
P->fanout = 1;
|
||||
P->depth = 1;
|
||||
store32( &P->leaf_length, 0 );
|
||||
store32( &P->node_offset, 0 );
|
||||
store32( &P->xof_length, 0 );
|
||||
P->node_depth = 0;
|
||||
P->inner_length = 0;
|
||||
memset( P->reserved, 0, sizeof( P->reserved ) );
|
||||
memset( P->salt, 0, sizeof( P->salt ) );
|
||||
memset( P->personal, 0, sizeof( P->personal ) );
|
||||
return blake2b_init_param( S, P );
|
||||
}
|
||||
|
||||
|
||||
int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
|
||||
{
|
||||
blake2b_param P[1];
|
||||
|
||||
if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
|
||||
|
||||
if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;
|
||||
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = (uint8_t)keylen;
|
||||
P->fanout = 1;
|
||||
P->depth = 1;
|
||||
store32( &P->leaf_length, 0 );
|
||||
store32( &P->node_offset, 0 );
|
||||
store32( &P->xof_length, 0 );
|
||||
P->node_depth = 0;
|
||||
P->inner_length = 0;
|
||||
memset( P->reserved, 0, sizeof( P->reserved ) );
|
||||
memset( P->salt, 0, sizeof( P->salt ) );
|
||||
memset( P->personal, 0, sizeof( P->personal ) );
|
||||
|
||||
if( blake2b_init_param( S, P ) < 0 ) return -1;
|
||||
|
||||
{
|
||||
uint8_t block[BLAKE2B_BLOCKBYTES];
|
||||
memset( block, 0, BLAKE2B_BLOCKBYTES );
|
||||
memcpy( block, key, keylen );
|
||||
blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
|
||||
secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define G(r,i,a,b,c,d) \
|
||||
do { \
|
||||
a = a + b + m[blake2b_sigma[r][2*i+0]]; \
|
||||
d = rotr64(d ^ a, 32); \
|
||||
c = c + d; \
|
||||
b = rotr64(b ^ c, 24); \
|
||||
a = a + b + m[blake2b_sigma[r][2*i+1]]; \
|
||||
d = rotr64(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr64(b ^ c, 63); \
|
||||
} while(0)
|
||||
|
||||
#define ROUND(r) \
|
||||
do { \
|
||||
G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
|
||||
G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
|
||||
G(r,2,v[ 2],v[ 6],v[10],v[14]); \
|
||||
G(r,3,v[ 3],v[ 7],v[11],v[15]); \
|
||||
G(r,4,v[ 0],v[ 5],v[10],v[15]); \
|
||||
G(r,5,v[ 1],v[ 6],v[11],v[12]); \
|
||||
G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
|
||||
G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
|
||||
} while(0)
|
||||
|
||||
static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
|
||||
{
|
||||
uint64_t m[16];
|
||||
uint64_t v[16];
|
||||
size_t i;
|
||||
|
||||
for( i = 0; i < 16; ++i ) {
|
||||
m[i] = load64( block + i * sizeof( m[i] ) );
|
||||
}
|
||||
|
||||
for( i = 0; i < 8; ++i ) {
|
||||
v[i] = S->h[i];
|
||||
}
|
||||
|
||||
v[ 8] = blake2b_IV[0];
|
||||
v[ 9] = blake2b_IV[1];
|
||||
v[10] = blake2b_IV[2];
|
||||
v[11] = blake2b_IV[3];
|
||||
v[12] = blake2b_IV[4] ^ S->t[0];
|
||||
v[13] = blake2b_IV[5] ^ S->t[1];
|
||||
v[14] = blake2b_IV[6] ^ S->f[0];
|
||||
v[15] = blake2b_IV[7] ^ S->f[1];
|
||||
|
||||
ROUND( 0 );
|
||||
ROUND( 1 );
|
||||
ROUND( 2 );
|
||||
ROUND( 3 );
|
||||
ROUND( 4 );
|
||||
ROUND( 5 );
|
||||
ROUND( 6 );
|
||||
ROUND( 7 );
|
||||
ROUND( 8 );
|
||||
ROUND( 9 );
|
||||
ROUND( 10 );
|
||||
ROUND( 11 );
|
||||
|
||||
for( i = 0; i < 8; ++i ) {
|
||||
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||
}
|
||||
}
|
||||
|
||||
#undef G
|
||||
#undef ROUND
|
||||
|
||||
int blake2b_update( blake2b_state *S, const void *pin, size_t inlen )
|
||||
{
|
||||
const unsigned char * in = (const unsigned char *)pin;
|
||||
if( inlen > 0 )
|
||||
{
|
||||
size_t left = S->buflen;
|
||||
size_t fill = BLAKE2B_BLOCKBYTES - left;
|
||||
if( inlen > fill )
|
||||
{
|
||||
S->buflen = 0;
|
||||
memcpy( S->buf + left, in, fill ); /* Fill buffer */
|
||||
blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
|
||||
blake2b_compress( S, S->buf ); /* Compress */
|
||||
in += fill; inlen -= fill;
|
||||
while(inlen > BLAKE2B_BLOCKBYTES) {
|
||||
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
|
||||
blake2b_compress( S, in );
|
||||
in += BLAKE2B_BLOCKBYTES;
|
||||
inlen -= BLAKE2B_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
memcpy( S->buf + S->buflen, in, inlen );
|
||||
S->buflen += inlen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2b_final( blake2b_state *S, void *out, size_t outlen )
|
||||
{
|
||||
uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
|
||||
size_t i;
|
||||
|
||||
if( out == NULL || outlen < S->outlen )
|
||||
return -1;
|
||||
|
||||
if( blake2b_is_lastblock( S ) )
|
||||
return -1;
|
||||
|
||||
blake2b_increment_counter( S, S->buflen );
|
||||
blake2b_set_lastblock( S );
|
||||
memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
|
||||
blake2b_compress( S, S->buf );
|
||||
|
||||
for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
|
||||
store64( buffer + sizeof( S->h[i] ) * i, S->h[i] );
|
||||
|
||||
memcpy( out, buffer, S->outlen );
|
||||
secure_zero_memory(buffer, sizeof(buffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* inlen, at least, should be uint64_t. Others can be size_t. */
|
||||
int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
|
||||
{
|
||||
blake2b_state S[1];
|
||||
|
||||
/* Verify parameters */
|
||||
if ( NULL == in && inlen > 0 ) return -1;
|
||||
|
||||
if ( NULL == out ) return -1;
|
||||
|
||||
if( NULL == key && keylen > 0 ) return -1;
|
||||
|
||||
if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
|
||||
|
||||
if( keylen > BLAKE2B_KEYBYTES ) return -1;
|
||||
|
||||
if( keylen > 0 )
|
||||
{
|
||||
if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( blake2b_init( S, outlen ) < 0 ) return -1;
|
||||
}
|
||||
|
||||
blake2b_update( S, ( const uint8_t * )in, inlen );
|
||||
blake2b_final( S, out, outlen );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) {
|
||||
return blake2b(out, outlen, in, inlen, key, keylen);
|
||||
}
|
||||
|
||||
#if defined(SUPERCOP)
|
||||
int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
|
||||
{
|
||||
return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BLAKE2B_SELFTEST)
|
||||
#include <string.h>
|
||||
#include "blake2-kat.h"
|
||||
int main( void )
|
||||
{
|
||||
uint8_t key[BLAKE2B_KEYBYTES];
|
||||
uint8_t buf[BLAKE2_KAT_LENGTH];
|
||||
size_t i, step;
|
||||
|
||||
for( i = 0; i < BLAKE2B_KEYBYTES; ++i )
|
||||
key[i] = ( uint8_t )i;
|
||||
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
|
||||
buf[i] = ( uint8_t )i;
|
||||
|
||||
/* Test simple API */
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
|
||||
{
|
||||
uint8_t hash[BLAKE2B_OUTBYTES];
|
||||
blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES );
|
||||
|
||||
if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) )
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test streaming API */
|
||||
for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) {
|
||||
for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
|
||||
uint8_t hash[BLAKE2B_OUTBYTES];
|
||||
blake2b_state S;
|
||||
uint8_t * p = buf;
|
||||
size_t mlen = i;
|
||||
int err = 0;
|
||||
|
||||
if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (mlen >= step) {
|
||||
if ( (err = blake2b_update(&S, p, step)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
mlen -= step;
|
||||
p += step;
|
||||
}
|
||||
if ( (err = blake2b_update(&S, p, mlen)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
puts( "ok" );
|
||||
return 0;
|
||||
fail:
|
||||
puts("error");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
359
3rd/BLAKE2/blake2bp-ref.c
Normal file
359
3rd/BLAKE2/blake2bp-ref.c
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
|
||||
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
|
||||
your option. The terms of these licenses can be found at:
|
||||
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#include "blake2.h"
|
||||
#include "blake2-impl.h"
|
||||
|
||||
#define PARALLELISM_DEGREE 4
|
||||
|
||||
/*
|
||||
blake2b_init_param defaults to setting the expecting output length
|
||||
from the digest_length parameter block field.
|
||||
|
||||
In some cases, however, we do not want this, as the output length
|
||||
of these instances is given by inner_length instead.
|
||||
*/
|
||||
static int blake2bp_init_leaf_param( blake2b_state *S, const blake2b_param *P )
|
||||
{
|
||||
int err = blake2b_init_param(S, P);
|
||||
S->outlen = P->inner_length;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int blake2bp_init_leaf( blake2b_state *S, size_t outlen, size_t keylen, uint64_t offset )
|
||||
{
|
||||
blake2b_param P[1];
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = (uint8_t)keylen;
|
||||
P->fanout = PARALLELISM_DEGREE;
|
||||
P->depth = 2;
|
||||
store32( &P->leaf_length, 0 );
|
||||
store32( &P->node_offset, offset );
|
||||
store32( &P->xof_length, 0 );
|
||||
P->node_depth = 0;
|
||||
P->inner_length = BLAKE2B_OUTBYTES;
|
||||
memset( P->reserved, 0, sizeof( P->reserved ) );
|
||||
memset( P->salt, 0, sizeof( P->salt ) );
|
||||
memset( P->personal, 0, sizeof( P->personal ) );
|
||||
return blake2bp_init_leaf_param( S, P );
|
||||
}
|
||||
|
||||
static int blake2bp_init_root( blake2b_state *S, size_t outlen, size_t keylen )
|
||||
{
|
||||
blake2b_param P[1];
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = (uint8_t)keylen;
|
||||
P->fanout = PARALLELISM_DEGREE;
|
||||
P->depth = 2;
|
||||
store32( &P->leaf_length, 0 );
|
||||
store32( &P->node_offset, 0 );
|
||||
store32( &P->xof_length, 0 );
|
||||
P->node_depth = 1;
|
||||
P->inner_length = BLAKE2B_OUTBYTES;
|
||||
memset( P->reserved, 0, sizeof( P->reserved ) );
|
||||
memset( P->salt, 0, sizeof( P->salt ) );
|
||||
memset( P->personal, 0, sizeof( P->personal ) );
|
||||
return blake2b_init_param( S, P );
|
||||
}
|
||||
|
||||
|
||||
int blake2bp_init( blake2bp_state *S, size_t outlen )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
|
||||
|
||||
memset( S->buf, 0, sizeof( S->buf ) );
|
||||
S->buflen = 0;
|
||||
S->outlen = outlen;
|
||||
|
||||
if( blake2bp_init_root( S->R, outlen, 0 ) < 0 )
|
||||
return -1;
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
if( blake2bp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1;
|
||||
|
||||
S->R->last_node = 1;
|
||||
S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
|
||||
|
||||
if( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;
|
||||
|
||||
memset( S->buf, 0, sizeof( S->buf ) );
|
||||
S->buflen = 0;
|
||||
S->outlen = outlen;
|
||||
|
||||
if( blake2bp_init_root( S->R, outlen, keylen ) < 0 )
|
||||
return -1;
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
if( blake2bp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1;
|
||||
|
||||
S->R->last_node = 1;
|
||||
S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
|
||||
{
|
||||
uint8_t block[BLAKE2B_BLOCKBYTES];
|
||||
memset( block, 0, BLAKE2B_BLOCKBYTES );
|
||||
memcpy( block, key, keylen );
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES );
|
||||
|
||||
secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen )
|
||||
{
|
||||
const unsigned char * in = (const unsigned char *)pin;
|
||||
size_t left = S->buflen;
|
||||
size_t fill = sizeof( S->buf ) - left;
|
||||
size_t i;
|
||||
|
||||
if( left && inlen >= fill )
|
||||
{
|
||||
memcpy( S->buf + left, in, fill );
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES );
|
||||
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE)
|
||||
#else
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
#endif
|
||||
{
|
||||
#if defined(_OPENMP)
|
||||
size_t i = omp_get_thread_num();
|
||||
#endif
|
||||
size_t inlen__ = inlen;
|
||||
const unsigned char *in__ = ( const unsigned char * )in;
|
||||
in__ += i * BLAKE2B_BLOCKBYTES;
|
||||
|
||||
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
|
||||
{
|
||||
blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES );
|
||||
in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
|
||||
inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
|
||||
in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES );
|
||||
inlen %= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
|
||||
|
||||
if( inlen > 0 )
|
||||
memcpy( S->buf + left, in, inlen );
|
||||
|
||||
S->buflen = left + inlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2bp_final( blake2bp_state *S, void *out, size_t outlen )
|
||||
{
|
||||
uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
|
||||
size_t i;
|
||||
|
||||
if(out == NULL || outlen < S->outlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
{
|
||||
if( S->buflen > i * BLAKE2B_BLOCKBYTES )
|
||||
{
|
||||
size_t left = S->buflen - i * BLAKE2B_BLOCKBYTES;
|
||||
|
||||
if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES;
|
||||
|
||||
blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left );
|
||||
}
|
||||
|
||||
blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES );
|
||||
}
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES );
|
||||
|
||||
return blake2b_final( S->R, out, S->outlen );
|
||||
}
|
||||
|
||||
int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
|
||||
{
|
||||
uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
|
||||
blake2b_state S[PARALLELISM_DEGREE][1];
|
||||
blake2b_state FS[1];
|
||||
size_t i;
|
||||
|
||||
/* Verify parameters */
|
||||
if ( NULL == in && inlen > 0 ) return -1;
|
||||
|
||||
if ( NULL == out ) return -1;
|
||||
|
||||
if( NULL == key && keylen > 0 ) return -1;
|
||||
|
||||
if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
|
||||
|
||||
if( keylen > BLAKE2B_KEYBYTES ) return -1;
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
if( blake2bp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1;
|
||||
|
||||
S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */
|
||||
|
||||
if( keylen > 0 )
|
||||
{
|
||||
uint8_t block[BLAKE2B_BLOCKBYTES];
|
||||
memset( block, 0, BLAKE2B_BLOCKBYTES );
|
||||
memcpy( block, key, keylen );
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES );
|
||||
|
||||
secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
|
||||
}
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE)
|
||||
#else
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
#endif
|
||||
{
|
||||
#if defined(_OPENMP)
|
||||
size_t i = omp_get_thread_num();
|
||||
#endif
|
||||
size_t inlen__ = inlen;
|
||||
const unsigned char *in__ = ( const unsigned char * )in;
|
||||
in__ += i * BLAKE2B_BLOCKBYTES;
|
||||
|
||||
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
|
||||
{
|
||||
blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES );
|
||||
in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
|
||||
inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
|
||||
}
|
||||
|
||||
if( inlen__ > i * BLAKE2B_BLOCKBYTES )
|
||||
{
|
||||
const size_t left = inlen__ - i * BLAKE2B_BLOCKBYTES;
|
||||
const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES;
|
||||
blake2b_update( S[i], in__, len );
|
||||
}
|
||||
|
||||
blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES );
|
||||
}
|
||||
|
||||
if( blake2bp_init_root( FS, outlen, keylen ) < 0 )
|
||||
return -1;
|
||||
|
||||
FS->last_node = 1; /* Mark as last node */
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES );
|
||||
|
||||
return blake2b_final( FS, out, outlen );;
|
||||
}
|
||||
|
||||
#if defined(BLAKE2BP_SELFTEST)
|
||||
#include <string.h>
|
||||
#include "blake2-kat.h"
|
||||
int main( void )
|
||||
{
|
||||
uint8_t key[BLAKE2B_KEYBYTES];
|
||||
uint8_t buf[BLAKE2_KAT_LENGTH];
|
||||
size_t i, step;
|
||||
|
||||
for( i = 0; i < BLAKE2B_KEYBYTES; ++i )
|
||||
key[i] = ( uint8_t )i;
|
||||
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
|
||||
buf[i] = ( uint8_t )i;
|
||||
|
||||
/* Test simple API */
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
|
||||
{
|
||||
uint8_t hash[BLAKE2B_OUTBYTES];
|
||||
blake2bp( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES );
|
||||
|
||||
if( 0 != memcmp( hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES ) )
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test streaming API */
|
||||
for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) {
|
||||
for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
|
||||
uint8_t hash[BLAKE2B_OUTBYTES];
|
||||
blake2bp_state S;
|
||||
uint8_t * p = buf;
|
||||
size_t mlen = i;
|
||||
int err = 0;
|
||||
|
||||
if( (err = blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (mlen >= step) {
|
||||
if ( (err = blake2bp_update(&S, p, step)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
mlen -= step;
|
||||
p += step;
|
||||
}
|
||||
if ( (err = blake2bp_update(&S, p, mlen)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if ( (err = blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (0 != memcmp(hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
puts( "ok" );
|
||||
return 0;
|
||||
fail:
|
||||
puts("error");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
367
3rd/BLAKE2/blake2s-ref.c
Normal file
367
3rd/BLAKE2/blake2s-ref.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
|
||||
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
|
||||
your option. The terms of these licenses can be found at:
|
||||
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "blake2.h"
|
||||
#include "blake2-impl.h"
|
||||
|
||||
static const uint32_t blake2s_IV[8] =
|
||||
{
|
||||
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
|
||||
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
|
||||
};
|
||||
|
||||
static const uint8_t blake2s_sigma[10][16] =
|
||||
{
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
|
||||
};
|
||||
|
||||
static void blake2s_set_lastnode( blake2s_state *S )
|
||||
{
|
||||
S->f[1] = (uint32_t)-1;
|
||||
}
|
||||
|
||||
/* Some helper functions, not necessarily useful */
|
||||
static int blake2s_is_lastblock( const blake2s_state *S )
|
||||
{
|
||||
return S->f[0] != 0;
|
||||
}
|
||||
|
||||
static void blake2s_set_lastblock( blake2s_state *S )
|
||||
{
|
||||
if( S->last_node ) blake2s_set_lastnode( S );
|
||||
|
||||
S->f[0] = (uint32_t)-1;
|
||||
}
|
||||
|
||||
static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
|
||||
{
|
||||
S->t[0] += inc;
|
||||
S->t[1] += ( S->t[0] < inc );
|
||||
}
|
||||
|
||||
static void blake2s_init0( blake2s_state *S )
|
||||
{
|
||||
size_t i;
|
||||
memset( S, 0, sizeof( blake2s_state ) );
|
||||
|
||||
for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
|
||||
}
|
||||
|
||||
/* init2 xors IV with input parameter block */
|
||||
int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
|
||||
{
|
||||
const unsigned char *p = ( const unsigned char * )( P );
|
||||
size_t i;
|
||||
|
||||
blake2s_init0( S );
|
||||
|
||||
/* IV XOR ParamBlock */
|
||||
for( i = 0; i < 8; ++i )
|
||||
S->h[i] ^= load32( &p[i * 4] );
|
||||
|
||||
S->outlen = P->digest_length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Sequential blake2s initialization */
|
||||
int blake2s_init( blake2s_state *S, size_t outlen )
|
||||
{
|
||||
blake2s_param P[1];
|
||||
|
||||
/* Move interval verification here? */
|
||||
if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
|
||||
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = 0;
|
||||
P->fanout = 1;
|
||||
P->depth = 1;
|
||||
store32( &P->leaf_length, 0 );
|
||||
store32( &P->node_offset, 0 );
|
||||
store16( &P->xof_length, 0 );
|
||||
P->node_depth = 0;
|
||||
P->inner_length = 0;
|
||||
/* memset(P->reserved, 0, sizeof(P->reserved) ); */
|
||||
memset( P->salt, 0, sizeof( P->salt ) );
|
||||
memset( P->personal, 0, sizeof( P->personal ) );
|
||||
return blake2s_init_param( S, P );
|
||||
}
|
||||
|
||||
int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
|
||||
{
|
||||
blake2s_param P[1];
|
||||
|
||||
if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
|
||||
|
||||
if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
|
||||
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = (uint8_t)keylen;
|
||||
P->fanout = 1;
|
||||
P->depth = 1;
|
||||
store32( &P->leaf_length, 0 );
|
||||
store32( &P->node_offset, 0 );
|
||||
store16( &P->xof_length, 0 );
|
||||
P->node_depth = 0;
|
||||
P->inner_length = 0;
|
||||
/* memset(P->reserved, 0, sizeof(P->reserved) ); */
|
||||
memset( P->salt, 0, sizeof( P->salt ) );
|
||||
memset( P->personal, 0, sizeof( P->personal ) );
|
||||
|
||||
if( blake2s_init_param( S, P ) < 0 ) return -1;
|
||||
|
||||
{
|
||||
uint8_t block[BLAKE2S_BLOCKBYTES];
|
||||
memset( block, 0, BLAKE2S_BLOCKBYTES );
|
||||
memcpy( block, key, keylen );
|
||||
blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
|
||||
secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define G(r,i,a,b,c,d) \
|
||||
do { \
|
||||
a = a + b + m[blake2s_sigma[r][2*i+0]]; \
|
||||
d = rotr32(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 12); \
|
||||
a = a + b + m[blake2s_sigma[r][2*i+1]]; \
|
||||
d = rotr32(d ^ a, 8); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 7); \
|
||||
} while(0)
|
||||
|
||||
#define ROUND(r) \
|
||||
do { \
|
||||
G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
|
||||
G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
|
||||
G(r,2,v[ 2],v[ 6],v[10],v[14]); \
|
||||
G(r,3,v[ 3],v[ 7],v[11],v[15]); \
|
||||
G(r,4,v[ 0],v[ 5],v[10],v[15]); \
|
||||
G(r,5,v[ 1],v[ 6],v[11],v[12]); \
|
||||
G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
|
||||
G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
|
||||
} while(0)
|
||||
|
||||
static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] )
|
||||
{
|
||||
uint32_t m[16];
|
||||
uint32_t v[16];
|
||||
size_t i;
|
||||
|
||||
for( i = 0; i < 16; ++i ) {
|
||||
m[i] = load32( in + i * sizeof( m[i] ) );
|
||||
}
|
||||
|
||||
for( i = 0; i < 8; ++i ) {
|
||||
v[i] = S->h[i];
|
||||
}
|
||||
|
||||
v[ 8] = blake2s_IV[0];
|
||||
v[ 9] = blake2s_IV[1];
|
||||
v[10] = blake2s_IV[2];
|
||||
v[11] = blake2s_IV[3];
|
||||
v[12] = S->t[0] ^ blake2s_IV[4];
|
||||
v[13] = S->t[1] ^ blake2s_IV[5];
|
||||
v[14] = S->f[0] ^ blake2s_IV[6];
|
||||
v[15] = S->f[1] ^ blake2s_IV[7];
|
||||
|
||||
ROUND( 0 );
|
||||
ROUND( 1 );
|
||||
ROUND( 2 );
|
||||
ROUND( 3 );
|
||||
ROUND( 4 );
|
||||
ROUND( 5 );
|
||||
ROUND( 6 );
|
||||
ROUND( 7 );
|
||||
ROUND( 8 );
|
||||
ROUND( 9 );
|
||||
|
||||
for( i = 0; i < 8; ++i ) {
|
||||
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||
}
|
||||
}
|
||||
|
||||
#undef G
|
||||
#undef ROUND
|
||||
|
||||
int blake2s_update( blake2s_state *S, const void *pin, size_t inlen )
|
||||
{
|
||||
const unsigned char * in = (const unsigned char *)pin;
|
||||
if( inlen > 0 )
|
||||
{
|
||||
size_t left = S->buflen;
|
||||
size_t fill = BLAKE2S_BLOCKBYTES - left;
|
||||
if( inlen > fill )
|
||||
{
|
||||
S->buflen = 0;
|
||||
memcpy( S->buf + left, in, fill ); /* Fill buffer */
|
||||
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
|
||||
blake2s_compress( S, S->buf ); /* Compress */
|
||||
in += fill; inlen -= fill;
|
||||
while(inlen > BLAKE2S_BLOCKBYTES) {
|
||||
blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES);
|
||||
blake2s_compress( S, in );
|
||||
in += BLAKE2S_BLOCKBYTES;
|
||||
inlen -= BLAKE2S_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
memcpy( S->buf + S->buflen, in, inlen );
|
||||
S->buflen += inlen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2s_final( blake2s_state *S, void *out, size_t outlen )
|
||||
{
|
||||
uint8_t buffer[BLAKE2S_OUTBYTES] = {0};
|
||||
size_t i;
|
||||
|
||||
if( out == NULL || outlen < S->outlen )
|
||||
return -1;
|
||||
|
||||
if( blake2s_is_lastblock( S ) )
|
||||
return -1;
|
||||
|
||||
blake2s_increment_counter( S, ( uint32_t )S->buflen );
|
||||
blake2s_set_lastblock( S );
|
||||
memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
|
||||
blake2s_compress( S, S->buf );
|
||||
|
||||
for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
|
||||
store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );
|
||||
|
||||
memcpy( out, buffer, outlen );
|
||||
secure_zero_memory(buffer, sizeof(buffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
|
||||
{
|
||||
blake2s_state S[1];
|
||||
|
||||
/* Verify parameters */
|
||||
if ( NULL == in && inlen > 0 ) return -1;
|
||||
|
||||
if ( NULL == out ) return -1;
|
||||
|
||||
if ( NULL == key && keylen > 0) return -1;
|
||||
|
||||
if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
|
||||
|
||||
if( keylen > BLAKE2S_KEYBYTES ) return -1;
|
||||
|
||||
if( keylen > 0 )
|
||||
{
|
||||
if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( blake2s_init( S, outlen ) < 0 ) return -1;
|
||||
}
|
||||
|
||||
blake2s_update( S, ( const uint8_t * )in, inlen );
|
||||
blake2s_final( S, out, outlen );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(SUPERCOP)
|
||||
int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
|
||||
{
|
||||
return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BLAKE2S_SELFTEST)
|
||||
#include <string.h>
|
||||
#include "blake2-kat.h"
|
||||
int main( void )
|
||||
{
|
||||
uint8_t key[BLAKE2S_KEYBYTES];
|
||||
uint8_t buf[BLAKE2_KAT_LENGTH];
|
||||
size_t i, step;
|
||||
|
||||
for( i = 0; i < BLAKE2S_KEYBYTES; ++i )
|
||||
key[i] = ( uint8_t )i;
|
||||
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
|
||||
buf[i] = ( uint8_t )i;
|
||||
|
||||
/* Test simple API */
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
|
||||
{
|
||||
uint8_t hash[BLAKE2S_OUTBYTES];
|
||||
blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
|
||||
|
||||
if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) )
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test streaming API */
|
||||
for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
|
||||
for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
|
||||
uint8_t hash[BLAKE2S_OUTBYTES];
|
||||
blake2s_state S;
|
||||
uint8_t * p = buf;
|
||||
size_t mlen = i;
|
||||
int err = 0;
|
||||
|
||||
if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (mlen >= step) {
|
||||
if ( (err = blake2s_update(&S, p, step)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
mlen -= step;
|
||||
p += step;
|
||||
}
|
||||
if ( (err = blake2s_update(&S, p, mlen)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
puts( "ok" );
|
||||
return 0;
|
||||
fail:
|
||||
puts("error");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
359
3rd/BLAKE2/blake2sp-ref.c
Normal file
359
3rd/BLAKE2/blake2sp-ref.c
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
|
||||
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
|
||||
your option. The terms of these licenses can be found at:
|
||||
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#include "blake2.h"
|
||||
#include "blake2-impl.h"
|
||||
|
||||
#define PARALLELISM_DEGREE 8
|
||||
|
||||
/*
|
||||
blake2sp_init_param defaults to setting the expecting output length
|
||||
from the digest_length parameter block field.
|
||||
|
||||
In some cases, however, we do not want this, as the output length
|
||||
of these instances is given by inner_length instead.
|
||||
*/
|
||||
static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P )
|
||||
{
|
||||
int err = blake2s_init_param(S, P);
|
||||
S->outlen = P->inner_length;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint64_t offset )
|
||||
{
|
||||
blake2s_param P[1];
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = (uint8_t)keylen;
|
||||
P->fanout = PARALLELISM_DEGREE;
|
||||
P->depth = 2;
|
||||
store32( &P->leaf_length, 0 );
|
||||
store32( &P->node_offset, offset );
|
||||
store16( &P->xof_length, 0 );
|
||||
P->node_depth = 0;
|
||||
P->inner_length = BLAKE2S_OUTBYTES;
|
||||
memset( P->salt, 0, sizeof( P->salt ) );
|
||||
memset( P->personal, 0, sizeof( P->personal ) );
|
||||
return blake2sp_init_leaf_param( S, P );
|
||||
}
|
||||
|
||||
static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen )
|
||||
{
|
||||
blake2s_param P[1];
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = (uint8_t)keylen;
|
||||
P->fanout = PARALLELISM_DEGREE;
|
||||
P->depth = 2;
|
||||
store32( &P->leaf_length, 0 );
|
||||
store32( &P->node_offset, 0 );
|
||||
store16( &P->xof_length, 0 );
|
||||
P->node_depth = 1;
|
||||
P->inner_length = BLAKE2S_OUTBYTES;
|
||||
memset( P->salt, 0, sizeof( P->salt ) );
|
||||
memset( P->personal, 0, sizeof( P->personal ) );
|
||||
return blake2s_init_param( S, P );
|
||||
}
|
||||
|
||||
|
||||
int blake2sp_init( blake2sp_state *S, size_t outlen )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
|
||||
|
||||
memset( S->buf, 0, sizeof( S->buf ) );
|
||||
S->buflen = 0;
|
||||
S->outlen = outlen;
|
||||
|
||||
if( blake2sp_init_root( S->R, outlen, 0 ) < 0 )
|
||||
return -1;
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
if( blake2sp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1;
|
||||
|
||||
S->R->last_node = 1;
|
||||
S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
|
||||
|
||||
if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
|
||||
|
||||
memset( S->buf, 0, sizeof( S->buf ) );
|
||||
S->buflen = 0;
|
||||
S->outlen = outlen;
|
||||
|
||||
if( blake2sp_init_root( S->R, outlen, keylen ) < 0 )
|
||||
return -1;
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
if( blake2sp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1;
|
||||
|
||||
S->R->last_node = 1;
|
||||
S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
|
||||
{
|
||||
uint8_t block[BLAKE2S_BLOCKBYTES];
|
||||
memset( block, 0, BLAKE2S_BLOCKBYTES );
|
||||
memcpy( block, key, keylen );
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen )
|
||||
{
|
||||
const unsigned char * in = (const unsigned char *)pin;
|
||||
size_t left = S->buflen;
|
||||
size_t fill = sizeof( S->buf ) - left;
|
||||
size_t i;
|
||||
|
||||
if( left && inlen >= fill )
|
||||
{
|
||||
memcpy( S->buf + left, in, fill );
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE)
|
||||
#else
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
#endif
|
||||
{
|
||||
#if defined(_OPENMP)
|
||||
size_t i = omp_get_thread_num();
|
||||
#endif
|
||||
size_t inlen__ = inlen;
|
||||
const unsigned char *in__ = ( const unsigned char * )in;
|
||||
in__ += i * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES );
|
||||
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
|
||||
in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
|
||||
inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
if( inlen > 0 )
|
||||
memcpy( S->buf + left, in, inlen );
|
||||
|
||||
S->buflen = left + inlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int blake2sp_final( blake2sp_state *S, void *out, size_t outlen )
|
||||
{
|
||||
uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
|
||||
size_t i;
|
||||
|
||||
if(out == NULL || outlen < S->outlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
{
|
||||
if( S->buflen > i * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
|
||||
|
||||
blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
|
||||
}
|
||||
|
||||
blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
|
||||
}
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
|
||||
|
||||
return blake2s_final( S->R, out, S->outlen );
|
||||
}
|
||||
|
||||
|
||||
int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
|
||||
{
|
||||
uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
|
||||
blake2s_state S[PARALLELISM_DEGREE][1];
|
||||
blake2s_state FS[1];
|
||||
size_t i;
|
||||
|
||||
/* Verify parameters */
|
||||
if ( NULL == in && inlen > 0 ) return -1;
|
||||
|
||||
if ( NULL == out ) return -1;
|
||||
|
||||
if ( NULL == key && keylen > 0) return -1;
|
||||
|
||||
if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
|
||||
|
||||
if( keylen > BLAKE2S_KEYBYTES ) return -1;
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
if( blake2sp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1;
|
||||
|
||||
S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */
|
||||
|
||||
if( keylen > 0 )
|
||||
{
|
||||
uint8_t block[BLAKE2S_BLOCKBYTES];
|
||||
memset( block, 0, BLAKE2S_BLOCKBYTES );
|
||||
memcpy( block, key, keylen );
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
|
||||
}
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE)
|
||||
#else
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
#endif
|
||||
{
|
||||
#if defined(_OPENMP)
|
||||
size_t i = omp_get_thread_num();
|
||||
#endif
|
||||
size_t inlen__ = inlen;
|
||||
const unsigned char *in__ = ( const unsigned char * )in;
|
||||
in__ += i * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );
|
||||
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
}
|
||||
|
||||
if( inlen__ > i * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES;
|
||||
const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
|
||||
blake2s_update( S[i], in__, len );
|
||||
}
|
||||
|
||||
blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );
|
||||
}
|
||||
|
||||
if( blake2sp_init_root( FS, outlen, keylen ) < 0 )
|
||||
return -1;
|
||||
|
||||
FS->last_node = 1;
|
||||
|
||||
for( i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
|
||||
|
||||
return blake2s_final( FS, out, outlen );
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined(BLAKE2SP_SELFTEST)
|
||||
#include <string.h>
|
||||
#include "blake2-kat.h"
|
||||
int main( void )
|
||||
{
|
||||
uint8_t key[BLAKE2S_KEYBYTES];
|
||||
uint8_t buf[BLAKE2_KAT_LENGTH];
|
||||
size_t i, step;
|
||||
|
||||
for( i = 0; i < BLAKE2S_KEYBYTES; ++i )
|
||||
key[i] = ( uint8_t )i;
|
||||
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
|
||||
buf[i] = ( uint8_t )i;
|
||||
|
||||
/* Test simple API */
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
|
||||
{
|
||||
uint8_t hash[BLAKE2S_OUTBYTES];
|
||||
blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
|
||||
|
||||
if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) )
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test streaming API */
|
||||
for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
|
||||
for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
|
||||
uint8_t hash[BLAKE2S_OUTBYTES];
|
||||
blake2sp_state S;
|
||||
uint8_t * p = buf;
|
||||
size_t mlen = i;
|
||||
int err = 0;
|
||||
|
||||
if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (mlen >= step) {
|
||||
if ( (err = blake2sp_update(&S, p, step)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
mlen -= step;
|
||||
p += step;
|
||||
}
|
||||
if ( (err = blake2sp_update(&S, p, mlen)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
puts( "ok" );
|
||||
return 0;
|
||||
fail:
|
||||
puts("error");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
241
3rd/BLAKE2/blake2xb-ref.c
Normal file
241
3rd/BLAKE2/blake2xb-ref.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Copyright 2016, JP Aumasson <jeanphilippe.aumasson@gmail.com>.
|
||||
Copyright 2016, Samuel Neves <sneves@dei.uc.pt>.
|
||||
|
||||
You may use this under the terms of the CC0, the OpenSSL Licence, or
|
||||
the Apache Public License 2.0, at your option. The terms of these
|
||||
licenses can be found at:
|
||||
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "blake2.h"
|
||||
#include "blake2-impl.h"
|
||||
|
||||
int blake2xb_init( blake2xb_state *S, const size_t outlen ) {
|
||||
return blake2xb_init_key(S, outlen, NULL, 0);
|
||||
}
|
||||
|
||||
int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen)
|
||||
{
|
||||
if ( outlen == 0 || outlen > 0xFFFFFFFFUL ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL != key && keylen > BLAKE2B_KEYBYTES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL == key && keylen > 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize parameter block */
|
||||
S->P->digest_length = BLAKE2B_OUTBYTES;
|
||||
S->P->key_length = keylen;
|
||||
S->P->fanout = 1;
|
||||
S->P->depth = 1;
|
||||
store32( &S->P->leaf_length, 0 );
|
||||
store32( &S->P->node_offset, 0 );
|
||||
store32( &S->P->xof_length, outlen );
|
||||
S->P->node_depth = 0;
|
||||
S->P->inner_length = 0;
|
||||
memset( S->P->reserved, 0, sizeof( S->P->reserved ) );
|
||||
memset( S->P->salt, 0, sizeof( S->P->salt ) );
|
||||
memset( S->P->personal, 0, sizeof( S->P->personal ) );
|
||||
|
||||
if( blake2b_init_param( S->S, S->P ) < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (keylen > 0) {
|
||||
uint8_t block[BLAKE2B_BLOCKBYTES];
|
||||
memset(block, 0, BLAKE2B_BLOCKBYTES);
|
||||
memcpy(block, key, keylen);
|
||||
blake2b_update(S->S, block, BLAKE2B_BLOCKBYTES);
|
||||
secure_zero_memory(block, BLAKE2B_BLOCKBYTES);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ) {
|
||||
return blake2b_update( S->S, in, inlen );
|
||||
}
|
||||
|
||||
int blake2xb_final( blake2xb_state *S, void *out, size_t outlen) {
|
||||
|
||||
blake2b_state C[1];
|
||||
blake2b_param P[1];
|
||||
uint32_t xof_length = load32(&S->P->xof_length);
|
||||
uint8_t root[BLAKE2B_BLOCKBYTES];
|
||||
size_t i;
|
||||
|
||||
if (NULL == out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* outlen must match the output size defined in xof_length, */
|
||||
/* unless it was -1, in which case anything goes except 0. */
|
||||
if(xof_length == 0xFFFFFFFFUL) {
|
||||
if(outlen == 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if(outlen != xof_length) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the root hash */
|
||||
if (blake2b_final(S->S, root, BLAKE2B_OUTBYTES) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set common block structure values */
|
||||
/* Copy values from parent instance, and only change the ones below */
|
||||
memcpy(P, S->P, sizeof(blake2b_param));
|
||||
P->key_length = 0;
|
||||
P->fanout = 0;
|
||||
P->depth = 0;
|
||||
store32(&P->leaf_length, BLAKE2B_OUTBYTES);
|
||||
P->inner_length = BLAKE2B_OUTBYTES;
|
||||
P->node_depth = 0;
|
||||
|
||||
for (i = 0; outlen > 0; ++i) {
|
||||
const size_t block_size = (outlen < BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES;
|
||||
/* Initialize state */
|
||||
P->digest_length = block_size;
|
||||
store32(&P->node_offset, i);
|
||||
blake2b_init_param(C, P);
|
||||
/* Process key if needed */
|
||||
blake2b_update(C, root, BLAKE2B_OUTBYTES);
|
||||
if (blake2b_final(C, (uint8_t *)out + i * BLAKE2B_OUTBYTES, block_size) < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
outlen -= block_size;
|
||||
}
|
||||
secure_zero_memory(root, sizeof(root));
|
||||
secure_zero_memory(P, sizeof(P));
|
||||
secure_zero_memory(C, sizeof(C));
|
||||
/* Put blake2xb in an invalid state? cf. blake2s_is_lastblock */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int blake2xb(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen)
|
||||
{
|
||||
blake2xb_state S[1];
|
||||
|
||||
/* Verify parameters */
|
||||
if (NULL == in && inlen > 0)
|
||||
return -1;
|
||||
|
||||
if (NULL == out)
|
||||
return -1;
|
||||
|
||||
if (NULL == key && keylen > 0)
|
||||
return -1;
|
||||
|
||||
if (keylen > BLAKE2B_KEYBYTES)
|
||||
return -1;
|
||||
|
||||
if (outlen == 0)
|
||||
return -1;
|
||||
|
||||
/* Initialize the root block structure */
|
||||
if (blake2xb_init_key(S, outlen, key, keylen) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Absorb the input message */
|
||||
blake2xb_update(S, in, inlen);
|
||||
|
||||
/* Compute the root node of the tree and the final hash using the counter construction */
|
||||
return blake2xb_final(S, out, outlen);
|
||||
}
|
||||
|
||||
#if defined(BLAKE2XB_SELFTEST)
|
||||
#include <string.h>
|
||||
#include "blake2-kat.h"
|
||||
int main( void )
|
||||
{
|
||||
uint8_t key[BLAKE2B_KEYBYTES];
|
||||
uint8_t buf[BLAKE2_KAT_LENGTH];
|
||||
size_t i, step, outlen;
|
||||
|
||||
for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) {
|
||||
key[i] = ( uint8_t )i;
|
||||
}
|
||||
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) {
|
||||
buf[i] = ( uint8_t )i;
|
||||
}
|
||||
|
||||
/* Testing length of outputs rather than inputs */
|
||||
/* (Test of input lengths mostly covered by blake2b tests) */
|
||||
|
||||
/* Test simple API */
|
||||
for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen )
|
||||
{
|
||||
uint8_t hash[BLAKE2_KAT_LENGTH] = {0};
|
||||
if( blake2xb( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2B_KEYBYTES ) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if( 0 != memcmp( hash, blake2xb_keyed_kat[outlen-1], outlen ) )
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test streaming API */
|
||||
for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) {
|
||||
for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) {
|
||||
uint8_t hash[BLAKE2_KAT_LENGTH];
|
||||
blake2xb_state S;
|
||||
uint8_t * p = buf;
|
||||
size_t mlen = BLAKE2_KAT_LENGTH;
|
||||
int err = 0;
|
||||
|
||||
if( (err = blake2xb_init_key(&S, outlen, key, BLAKE2B_KEYBYTES)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (mlen >= step) {
|
||||
if ( (err = blake2xb_update(&S, p, step)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
mlen -= step;
|
||||
p += step;
|
||||
}
|
||||
if ( (err = blake2xb_update(&S, p, mlen)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if ( (err = blake2xb_final(&S, hash, outlen)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (0 != memcmp(hash, blake2xb_keyed_kat[outlen-1], outlen)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
puts( "ok" );
|
||||
return 0;
|
||||
fail:
|
||||
puts("error");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
239
3rd/BLAKE2/blake2xs-ref.c
Normal file
239
3rd/BLAKE2/blake2xs-ref.c
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Copyright 2016, JP Aumasson <jeanphilippe.aumasson@gmail.com>.
|
||||
Copyright 2016, Samuel Neves <sneves@dei.uc.pt>.
|
||||
|
||||
You may use this under the terms of the CC0, the OpenSSL Licence, or
|
||||
the Apache Public License 2.0, at your option. The terms of these
|
||||
licenses can be found at:
|
||||
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "blake2.h"
|
||||
#include "blake2-impl.h"
|
||||
|
||||
int blake2xs_init( blake2xs_state *S, const size_t outlen ) {
|
||||
return blake2xs_init_key(S, outlen, NULL, 0);
|
||||
}
|
||||
|
||||
int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen )
|
||||
{
|
||||
if ( outlen == 0 || outlen > 0xFFFFUL ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL != key && keylen > BLAKE2S_KEYBYTES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL == key && keylen > 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize parameter block */
|
||||
S->P->digest_length = BLAKE2S_OUTBYTES;
|
||||
S->P->key_length = keylen;
|
||||
S->P->fanout = 1;
|
||||
S->P->depth = 1;
|
||||
store32( &S->P->leaf_length, 0 );
|
||||
store32( &S->P->node_offset, 0 );
|
||||
store16( &S->P->xof_length, outlen );
|
||||
S->P->node_depth = 0;
|
||||
S->P->inner_length = 0;
|
||||
memset( S->P->salt, 0, sizeof( S->P->salt ) );
|
||||
memset( S->P->personal, 0, sizeof( S->P->personal ) );
|
||||
|
||||
if( blake2s_init_param( S->S, S->P ) < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (keylen > 0) {
|
||||
uint8_t block[BLAKE2S_BLOCKBYTES];
|
||||
memset(block, 0, BLAKE2S_BLOCKBYTES);
|
||||
memcpy(block, key, keylen);
|
||||
blake2s_update(S->S, block, BLAKE2S_BLOCKBYTES);
|
||||
secure_zero_memory(block, BLAKE2S_BLOCKBYTES);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ) {
|
||||
return blake2s_update( S->S, in, inlen );
|
||||
}
|
||||
|
||||
int blake2xs_final(blake2xs_state *S, void *out, size_t outlen) {
|
||||
|
||||
blake2s_state C[1];
|
||||
blake2s_param P[1];
|
||||
uint16_t xof_length = load16(&S->P->xof_length);
|
||||
uint8_t root[BLAKE2S_BLOCKBYTES];
|
||||
size_t i;
|
||||
|
||||
if (NULL == out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* outlen must match the output size defined in xof_length, */
|
||||
/* unless it was -1, in which case anything goes except 0. */
|
||||
if(xof_length == 0xFFFFUL) {
|
||||
if(outlen == 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if(outlen != xof_length) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the root hash */
|
||||
if (blake2s_final(S->S, root, BLAKE2S_OUTBYTES) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set common block structure values */
|
||||
/* Copy values from parent instance, and only change the ones below */
|
||||
memcpy(P, S->P, sizeof(blake2s_param));
|
||||
P->key_length = 0;
|
||||
P->fanout = 0;
|
||||
P->depth = 0;
|
||||
store32(&P->leaf_length, BLAKE2S_OUTBYTES);
|
||||
P->inner_length = BLAKE2S_OUTBYTES;
|
||||
P->node_depth = 0;
|
||||
|
||||
for (i = 0; outlen > 0; ++i) {
|
||||
const size_t block_size = (outlen < BLAKE2S_OUTBYTES) ? outlen : BLAKE2S_OUTBYTES;
|
||||
/* Initialize state */
|
||||
P->digest_length = block_size;
|
||||
store32(&P->node_offset, i);
|
||||
blake2s_init_param(C, P);
|
||||
/* Process key if needed */
|
||||
blake2s_update(C, root, BLAKE2S_OUTBYTES);
|
||||
if (blake2s_final(C, (uint8_t *)out + i * BLAKE2S_OUTBYTES, block_size) < 0) {
|
||||
return -1;
|
||||
}
|
||||
outlen -= block_size;
|
||||
}
|
||||
secure_zero_memory(root, sizeof(root));
|
||||
secure_zero_memory(P, sizeof(P));
|
||||
secure_zero_memory(C, sizeof(C));
|
||||
/* Put blake2xs in an invalid state? cf. blake2s_is_lastblock */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2xs(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen)
|
||||
{
|
||||
blake2xs_state S[1];
|
||||
|
||||
/* Verify parameters */
|
||||
if (NULL == in && inlen > 0)
|
||||
return -1;
|
||||
|
||||
if (NULL == out)
|
||||
return -1;
|
||||
|
||||
if (NULL == key && keylen > 0)
|
||||
return -1;
|
||||
|
||||
if (keylen > BLAKE2S_KEYBYTES)
|
||||
return -1;
|
||||
|
||||
if (outlen == 0)
|
||||
return -1;
|
||||
|
||||
/* Initialize the root block structure */
|
||||
if (blake2xs_init_key(S, outlen, key, keylen) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Absorb the input message */
|
||||
blake2xs_update(S, in, inlen);
|
||||
|
||||
/* Compute the root node of the tree and the final hash using the counter construction */
|
||||
return blake2xs_final(S, out, outlen);
|
||||
}
|
||||
|
||||
#if defined(BLAKE2XS_SELFTEST)
|
||||
#include <string.h>
|
||||
#include "blake2-kat.h"
|
||||
int main( void )
|
||||
{
|
||||
uint8_t key[BLAKE2S_KEYBYTES];
|
||||
uint8_t buf[BLAKE2_KAT_LENGTH];
|
||||
size_t i, step, outlen;
|
||||
|
||||
for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) {
|
||||
key[i] = ( uint8_t )i;
|
||||
}
|
||||
|
||||
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) {
|
||||
buf[i] = ( uint8_t )i;
|
||||
}
|
||||
|
||||
/* Testing length of outputs rather than inputs */
|
||||
/* (Test of input lengths mostly covered by blake2s tests) */
|
||||
|
||||
/* Test simple API */
|
||||
for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen )
|
||||
{
|
||||
uint8_t hash[BLAKE2_KAT_LENGTH] = {0};
|
||||
if( blake2xs( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2S_KEYBYTES ) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if( 0 != memcmp( hash, blake2xs_keyed_kat[outlen-1], outlen ) )
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test streaming API */
|
||||
for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
|
||||
for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) {
|
||||
uint8_t hash[BLAKE2_KAT_LENGTH];
|
||||
blake2xs_state S;
|
||||
uint8_t * p = buf;
|
||||
size_t mlen = BLAKE2_KAT_LENGTH;
|
||||
int err = 0;
|
||||
|
||||
if( (err = blake2xs_init_key(&S, outlen, key, BLAKE2S_KEYBYTES)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (mlen >= step) {
|
||||
if ( (err = blake2xs_update(&S, p, step)) < 0 ) {
|
||||
goto fail;
|
||||
}
|
||||
mlen -= step;
|
||||
p += step;
|
||||
}
|
||||
if ( (err = blake2xs_update(&S, p, mlen)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if ( (err = blake2xs_final(&S, hash, outlen)) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (0 != memcmp(hash, blake2xs_keyed_kat[outlen-1], outlen)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
puts( "ok" );
|
||||
return 0;
|
||||
fail:
|
||||
puts("error");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
219
3rd/SipHash/LICENSE_A2LLVM
Normal file
219
3rd/SipHash/LICENSE_A2LLVM
Normal file
@@ -0,0 +1,219 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
---- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
|
||||
116
3rd/SipHash/LICENSE_CC0
Normal file
116
3rd/SipHash/LICENSE_CC0
Normal file
@@ -0,0 +1,116 @@
|
||||
CC0 1.0 Universal
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator and
|
||||
subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for the
|
||||
purpose of contributing to a commons of creative, cultural and scientific
|
||||
works ("Commons") that the public can reliably and without fear of later
|
||||
claims of infringement build upon, modify, incorporate in other works, reuse
|
||||
and redistribute as freely as possible in any form whatsoever and for any
|
||||
purposes, including without limitation commercial purposes. These owners may
|
||||
contribute to the Commons to promote the ideal of a free culture and the
|
||||
further production of creative, cultural and scientific works, or to gain
|
||||
reputation or greater distribution for their Work in part through the use and
|
||||
efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any expectation
|
||||
of additional consideration or compensation, the person associating CC0 with a
|
||||
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
|
||||
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
|
||||
and publicly distribute the Work under its terms, with knowledge of his or her
|
||||
Copyright and Related Rights in the Work and the meaning and intended legal
|
||||
effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not limited
|
||||
to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display, communicate,
|
||||
and translate a Work;
|
||||
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
|
||||
iii. publicity and privacy rights pertaining to a person's image or likeness
|
||||
depicted in a Work;
|
||||
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data in
|
||||
a Work;
|
||||
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation thereof,
|
||||
including any amended or successor version of such directive); and
|
||||
|
||||
vii. other similar, equivalent or corresponding rights throughout the world
|
||||
based on applicable law or treaty, and any national implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention of,
|
||||
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
|
||||
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
|
||||
and Related Rights and associated claims and causes of action, whether now
|
||||
known or unknown (including existing as well as future claims and causes of
|
||||
action), in the Work (i) in all territories worldwide, (ii) for the maximum
|
||||
duration provided by applicable law or treaty (including future time
|
||||
extensions), (iii) in any current or future medium and for any number of
|
||||
copies, and (iv) for any purpose whatsoever, including without limitation
|
||||
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
|
||||
the Waiver for the benefit of each member of the public at large and to the
|
||||
detriment of Affirmer's heirs and successors, fully intending that such Waiver
|
||||
shall not be subject to revocation, rescission, cancellation, termination, or
|
||||
any other legal or equitable action to disrupt the quiet enjoyment of the Work
|
||||
by the public as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason be
|
||||
judged legally invalid or ineffective under applicable law, then the Waiver
|
||||
shall be preserved to the maximum extent permitted taking into account
|
||||
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
|
||||
is so judged Affirmer hereby grants to each affected person a royalty-free,
|
||||
non transferable, non sublicensable, non exclusive, irrevocable and
|
||||
unconditional license to exercise Affirmer's Copyright and Related Rights in
|
||||
the Work (i) in all territories worldwide, (ii) for the maximum duration
|
||||
provided by applicable law or treaty (including future time extensions), (iii)
|
||||
in any current or future medium and for any number of copies, and (iv) for any
|
||||
purpose whatsoever, including without limitation commercial, advertising or
|
||||
promotional purposes (the "License"). The License shall be deemed effective as
|
||||
of the date CC0 was applied by Affirmer to the Work. Should any part of the
|
||||
License for any reason be judged legally invalid or ineffective under
|
||||
applicable law, such partial invalidity or ineffectiveness shall not
|
||||
invalidate the remainder of the License, and in such case Affirmer hereby
|
||||
affirms that he or she will not (i) exercise any of his or her remaining
|
||||
Copyright and Related Rights in the Work or (ii) assert any associated claims
|
||||
and causes of action with respect to the Work, in either case contrary to
|
||||
Affirmer's express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
|
||||
b. Affirmer offers the Work as-is and makes no representations or warranties
|
||||
of any kind concerning the Work, express, implied, statutory or otherwise,
|
||||
including without limitation warranties of title, merchantability, fitness
|
||||
for a particular purpose, non infringement, or the absence of latent or
|
||||
other defects, accuracy, or the present or absence of errors, whether or not
|
||||
discoverable, all to the greatest extent permissible under applicable law.
|
||||
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without limitation
|
||||
any person's Copyright and Related Rights in the Work. Further, Affirmer
|
||||
disclaims responsibility for obtaining any necessary consents, permissions
|
||||
or other rights required for any use of the Work.
|
||||
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to this
|
||||
CC0 or use of the Work.
|
||||
|
||||
For more information, please see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>
|
||||
7
3rd/SipHash/LICENSE_MIT
Normal file
7
3rd/SipHash/LICENSE_MIT
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright 2012-2024 JP Aumasson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
166
3rd/SipHash/halfsiphash.c
Normal file
166
3rd/SipHash/halfsiphash.c
Normal file
@@ -0,0 +1,166 @@
|
||||
|
||||
/*
|
||||
SipHash reference C implementation
|
||||
|
||||
Copyright (c) 2016 Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along
|
||||
with
|
||||
this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
#include "halfsiphash.h"
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* default: SipHash-2-4 */
|
||||
#ifndef cROUNDS
|
||||
#define cROUNDS 2
|
||||
#endif
|
||||
#ifndef dROUNDS
|
||||
#define dROUNDS 4
|
||||
#endif
|
||||
|
||||
#define ROTL(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
|
||||
|
||||
#define U32TO8_LE(p, v) \
|
||||
(p)[0] = (uint8_t)((v)); \
|
||||
(p)[1] = (uint8_t)((v) >> 8); \
|
||||
(p)[2] = (uint8_t)((v) >> 16); \
|
||||
(p)[3] = (uint8_t)((v) >> 24);
|
||||
|
||||
#define U8TO32_LE(p) \
|
||||
(((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \
|
||||
((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24))
|
||||
|
||||
#define SIPROUND \
|
||||
do { \
|
||||
v0 += v1; \
|
||||
v1 = ROTL(v1, 5); \
|
||||
v1 ^= v0; \
|
||||
v0 = ROTL(v0, 16); \
|
||||
v2 += v3; \
|
||||
v3 = ROTL(v3, 8); \
|
||||
v3 ^= v2; \
|
||||
v0 += v3; \
|
||||
v3 = ROTL(v3, 7); \
|
||||
v3 ^= v0; \
|
||||
v2 += v1; \
|
||||
v1 = ROTL(v1, 13); \
|
||||
v1 ^= v2; \
|
||||
v2 = ROTL(v2, 16); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#ifdef DEBUG_SIPHASH
|
||||
#include <stdio.h>
|
||||
|
||||
#define TRACE \
|
||||
do { \
|
||||
printf("(%3zu) v0 %08" PRIx32 "\n", inlen, v0); \
|
||||
printf("(%3zu) v1 %08" PRIx32 "\n", inlen, v1); \
|
||||
printf("(%3zu) v2 %08" PRIx32 "\n", inlen, v2); \
|
||||
printf("(%3zu) v3 %08" PRIx32 "\n", inlen, v3); \
|
||||
} while (0)
|
||||
#else
|
||||
#define TRACE
|
||||
#endif
|
||||
|
||||
/*
|
||||
Computes a SipHash value
|
||||
*in: pointer to input data (read-only)
|
||||
inlen: input data length in bytes (any size_t value)
|
||||
*k: pointer to the key data (read-only), must be 8 bytes
|
||||
*out: pointer to output data (write-only), outlen bytes must be allocated
|
||||
outlen: length of the output in bytes, must be 4 or 8
|
||||
*/
|
||||
int halfsiphash(const void *in, const size_t inlen, const void *k, uint8_t *out,
|
||||
const size_t outlen) {
|
||||
|
||||
const unsigned char *ni = (const unsigned char *)in;
|
||||
const unsigned char *kk = (const unsigned char *)k;
|
||||
|
||||
assert((outlen == 4) || (outlen == 8));
|
||||
uint32_t v0 = 0;
|
||||
uint32_t v1 = 0;
|
||||
uint32_t v2 = UINT32_C(0x6c796765);
|
||||
uint32_t v3 = UINT32_C(0x74656462);
|
||||
uint32_t k0 = U8TO32_LE(kk);
|
||||
uint32_t k1 = U8TO32_LE(kk + 4);
|
||||
uint32_t m;
|
||||
int i;
|
||||
const unsigned char *end = ni + inlen - (inlen % sizeof(uint32_t));
|
||||
const int left = inlen & 3;
|
||||
uint32_t b = ((uint32_t)inlen) << 24;
|
||||
v3 ^= k1;
|
||||
v2 ^= k0;
|
||||
v1 ^= k1;
|
||||
v0 ^= k0;
|
||||
|
||||
if (outlen == 8)
|
||||
v1 ^= 0xee;
|
||||
|
||||
for (; ni != end; ni += 4) {
|
||||
m = U8TO32_LE(ni);
|
||||
v3 ^= m;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < cROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= m;
|
||||
}
|
||||
|
||||
switch (left) {
|
||||
case 3:
|
||||
b |= ((uint32_t)ni[2]) << 16;
|
||||
/* FALLTHRU */
|
||||
case 2:
|
||||
b |= ((uint32_t)ni[1]) << 8;
|
||||
/* FALLTHRU */
|
||||
case 1:
|
||||
b |= ((uint32_t)ni[0]);
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
v3 ^= b;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < cROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= b;
|
||||
|
||||
if (outlen == 8)
|
||||
v2 ^= 0xee;
|
||||
else
|
||||
v2 ^= 0xff;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < dROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
b = v1 ^ v3;
|
||||
U32TO8_LE(out, b);
|
||||
|
||||
if (outlen == 4)
|
||||
return 0;
|
||||
|
||||
v1 ^= 0xdd;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < dROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
b = v1 ^ v3;
|
||||
U32TO8_LE(out + 4, b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
34
3rd/SipHash/halfsiphash.h
Normal file
34
3rd/SipHash/halfsiphash.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
SipHash reference C implementation
|
||||
|
||||
Copyright (c) 2012-2021 Jean-Philippe Aumasson
|
||||
<jeanphilippe.aumasson@gmail.com>
|
||||
Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along
|
||||
with
|
||||
this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef HALFSIPHASH_H
|
||||
#define HALFSIPHASH_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int halfsiphash(const void * in, const size_t inlen, const void * k, uint8_t * out, const size_t outlen);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
185
3rd/SipHash/siphash.c
Normal file
185
3rd/SipHash/siphash.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
SipHash reference C implementation
|
||||
|
||||
Copyright (c) 2012-2022 Jean-Philippe Aumasson
|
||||
<jeanphilippe.aumasson@gmail.com>
|
||||
Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along
|
||||
with
|
||||
this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#include "siphash.h"
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* default: SipHash-2-4 */
|
||||
#ifndef cROUNDS
|
||||
#define cROUNDS 2
|
||||
#endif
|
||||
#ifndef dROUNDS
|
||||
#define dROUNDS 4
|
||||
#endif
|
||||
|
||||
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
|
||||
|
||||
#define U32TO8_LE(p, v) \
|
||||
(p)[0] = (uint8_t)((v)); \
|
||||
(p)[1] = (uint8_t)((v) >> 8); \
|
||||
(p)[2] = (uint8_t)((v) >> 16); \
|
||||
(p)[3] = (uint8_t)((v) >> 24);
|
||||
|
||||
#define U64TO8_LE(p, v) \
|
||||
U32TO8_LE((p), (uint32_t)((v))); \
|
||||
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
|
||||
|
||||
#define U8TO64_LE(p) \
|
||||
(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
|
||||
((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
|
||||
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
|
||||
((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
|
||||
|
||||
#define SIPROUND \
|
||||
do { \
|
||||
v0 += v1; \
|
||||
v1 = ROTL(v1, 13); \
|
||||
v1 ^= v0; \
|
||||
v0 = ROTL(v0, 32); \
|
||||
v2 += v3; \
|
||||
v3 = ROTL(v3, 16); \
|
||||
v3 ^= v2; \
|
||||
v0 += v3; \
|
||||
v3 = ROTL(v3, 21); \
|
||||
v3 ^= v0; \
|
||||
v2 += v1; \
|
||||
v1 = ROTL(v1, 17); \
|
||||
v1 ^= v2; \
|
||||
v2 = ROTL(v2, 32); \
|
||||
} while (0)
|
||||
|
||||
#ifdef DEBUG_SIPHASH
|
||||
#include <stdio.h>
|
||||
|
||||
#define TRACE \
|
||||
do { \
|
||||
printf("(%3zu) v0 %016" PRIx64 "\n", inlen, v0); \
|
||||
printf("(%3zu) v1 %016" PRIx64 "\n", inlen, v1); \
|
||||
printf("(%3zu) v2 %016" PRIx64 "\n", inlen, v2); \
|
||||
printf("(%3zu) v3 %016" PRIx64 "\n", inlen, v3); \
|
||||
} while (0)
|
||||
#else
|
||||
#define TRACE
|
||||
#endif
|
||||
|
||||
/*
|
||||
Computes a SipHash value
|
||||
*in: pointer to input data (read-only)
|
||||
inlen: input data length in bytes (any size_t value)
|
||||
*k: pointer to the key data (read-only), must be 16 bytes
|
||||
*out: pointer to output data (write-only), outlen bytes must be allocated
|
||||
outlen: length of the output in bytes, must be 8 or 16
|
||||
*/
|
||||
int siphash(const void *in, const size_t inlen, const void *k, uint8_t *out,
|
||||
const size_t outlen) {
|
||||
|
||||
const unsigned char *ni = (const unsigned char *)in;
|
||||
const unsigned char *kk = (const unsigned char *)k;
|
||||
|
||||
assert((outlen == 8) || (outlen == 16));
|
||||
uint64_t v0 = UINT64_C(0x736f6d6570736575);
|
||||
uint64_t v1 = UINT64_C(0x646f72616e646f6d);
|
||||
uint64_t v2 = UINT64_C(0x6c7967656e657261);
|
||||
uint64_t v3 = UINT64_C(0x7465646279746573);
|
||||
uint64_t k0 = U8TO64_LE(kk);
|
||||
uint64_t k1 = U8TO64_LE(kk + 8);
|
||||
uint64_t m;
|
||||
int i;
|
||||
const unsigned char *end = ni + inlen - (inlen % sizeof(uint64_t));
|
||||
const int left = inlen & 7;
|
||||
uint64_t b = ((uint64_t)inlen) << 56;
|
||||
v3 ^= k1;
|
||||
v2 ^= k0;
|
||||
v1 ^= k1;
|
||||
v0 ^= k0;
|
||||
|
||||
if (outlen == 16)
|
||||
v1 ^= 0xee;
|
||||
|
||||
for (; ni != end; ni += 8) {
|
||||
m = U8TO64_LE(ni);
|
||||
v3 ^= m;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < cROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= m;
|
||||
}
|
||||
|
||||
switch (left) {
|
||||
case 7:
|
||||
b |= ((uint64_t)ni[6]) << 48;
|
||||
/* FALLTHRU */
|
||||
case 6:
|
||||
b |= ((uint64_t)ni[5]) << 40;
|
||||
/* FALLTHRU */
|
||||
case 5:
|
||||
b |= ((uint64_t)ni[4]) << 32;
|
||||
/* FALLTHRU */
|
||||
case 4:
|
||||
b |= ((uint64_t)ni[3]) << 24;
|
||||
/* FALLTHRU */
|
||||
case 3:
|
||||
b |= ((uint64_t)ni[2]) << 16;
|
||||
/* FALLTHRU */
|
||||
case 2:
|
||||
b |= ((uint64_t)ni[1]) << 8;
|
||||
/* FALLTHRU */
|
||||
case 1:
|
||||
b |= ((uint64_t)ni[0]);
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
v3 ^= b;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < cROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= b;
|
||||
|
||||
if (outlen == 16)
|
||||
v2 ^= 0xee;
|
||||
else
|
||||
v2 ^= 0xff;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < dROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
U64TO8_LE(out, b);
|
||||
|
||||
if (outlen == 8)
|
||||
return 0;
|
||||
|
||||
v1 ^= 0xdd;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < dROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
U64TO8_LE(out + 8, b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
34
3rd/SipHash/siphash.h
Normal file
34
3rd/SipHash/siphash.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
SipHash reference C implementation
|
||||
|
||||
Copyright (c) 2012-2021 Jean-Philippe Aumasson
|
||||
<jeanphilippe.aumasson@gmail.com>
|
||||
Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along
|
||||
with
|
||||
this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef SIPHASH_H
|
||||
#define SIPHASH_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int siphash(const void * in, const size_t inlen, const void * k, uint8_t * out, const size_t outlen);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
2826
3rd/SipHash/vectors.h
Normal file
2826
3rd/SipHash/vectors.h
Normal file
@@ -0,0 +1,2826 @@
|
||||
#include <stdint.h>
|
||||
|
||||
const uint8_t vectors_sip64[64][8] = {
|
||||
{
|
||||
0x31,
|
||||
0x0e,
|
||||
0x0e,
|
||||
0xdd,
|
||||
0x47,
|
||||
0xdb,
|
||||
0x6f,
|
||||
0x72,
|
||||
},
|
||||
{
|
||||
0xfd,
|
||||
0x67,
|
||||
0xdc,
|
||||
0x93,
|
||||
0xc5,
|
||||
0x39,
|
||||
0xf8,
|
||||
0x74,
|
||||
},
|
||||
{
|
||||
0x5a,
|
||||
0x4f,
|
||||
0xa9,
|
||||
0xd9,
|
||||
0x09,
|
||||
0x80,
|
||||
0x6c,
|
||||
0x0d,
|
||||
},
|
||||
{
|
||||
0x2d,
|
||||
0x7e,
|
||||
0xfb,
|
||||
0xd7,
|
||||
0x96,
|
||||
0x66,
|
||||
0x67,
|
||||
0x85,
|
||||
},
|
||||
{
|
||||
0xb7,
|
||||
0x87,
|
||||
0x71,
|
||||
0x27,
|
||||
0xe0,
|
||||
0x94,
|
||||
0x27,
|
||||
0xcf,
|
||||
},
|
||||
{
|
||||
0x8d,
|
||||
0xa6,
|
||||
0x99,
|
||||
0xcd,
|
||||
0x64,
|
||||
0x55,
|
||||
0x76,
|
||||
0x18,
|
||||
},
|
||||
{
|
||||
0xce,
|
||||
0xe3,
|
||||
0xfe,
|
||||
0x58,
|
||||
0x6e,
|
||||
0x46,
|
||||
0xc9,
|
||||
0xcb,
|
||||
},
|
||||
{
|
||||
0x37,
|
||||
0xd1,
|
||||
0x01,
|
||||
0x8b,
|
||||
0xf5,
|
||||
0x00,
|
||||
0x02,
|
||||
0xab,
|
||||
},
|
||||
{
|
||||
0x62,
|
||||
0x24,
|
||||
0x93,
|
||||
0x9a,
|
||||
0x79,
|
||||
0xf5,
|
||||
0xf5,
|
||||
0x93,
|
||||
},
|
||||
{
|
||||
0xb0,
|
||||
0xe4,
|
||||
0xa9,
|
||||
0x0b,
|
||||
0xdf,
|
||||
0x82,
|
||||
0x00,
|
||||
0x9e,
|
||||
},
|
||||
{
|
||||
0xf3,
|
||||
0xb9,
|
||||
0xdd,
|
||||
0x94,
|
||||
0xc5,
|
||||
0xbb,
|
||||
0x5d,
|
||||
0x7a,
|
||||
},
|
||||
{
|
||||
0xa7,
|
||||
0xad,
|
||||
0x6b,
|
||||
0x22,
|
||||
0x46,
|
||||
0x2f,
|
||||
0xb3,
|
||||
0xf4,
|
||||
},
|
||||
{
|
||||
0xfb,
|
||||
0xe5,
|
||||
0x0e,
|
||||
0x86,
|
||||
0xbc,
|
||||
0x8f,
|
||||
0x1e,
|
||||
0x75,
|
||||
},
|
||||
{
|
||||
0x90,
|
||||
0x3d,
|
||||
0x84,
|
||||
0xc0,
|
||||
0x27,
|
||||
0x56,
|
||||
0xea,
|
||||
0x14,
|
||||
},
|
||||
{
|
||||
0xee,
|
||||
0xf2,
|
||||
0x7a,
|
||||
0x8e,
|
||||
0x90,
|
||||
0xca,
|
||||
0x23,
|
||||
0xf7,
|
||||
},
|
||||
{
|
||||
0xe5,
|
||||
0x45,
|
||||
0xbe,
|
||||
0x49,
|
||||
0x61,
|
||||
0xca,
|
||||
0x29,
|
||||
0xa1,
|
||||
},
|
||||
{
|
||||
0xdb,
|
||||
0x9b,
|
||||
0xc2,
|
||||
0x57,
|
||||
0x7f,
|
||||
0xcc,
|
||||
0x2a,
|
||||
0x3f,
|
||||
},
|
||||
{
|
||||
0x94,
|
||||
0x47,
|
||||
0xbe,
|
||||
0x2c,
|
||||
0xf5,
|
||||
0xe9,
|
||||
0x9a,
|
||||
0x69,
|
||||
},
|
||||
{
|
||||
0x9c,
|
||||
0xd3,
|
||||
0x8d,
|
||||
0x96,
|
||||
0xf0,
|
||||
0xb3,
|
||||
0xc1,
|
||||
0x4b,
|
||||
},
|
||||
{
|
||||
0xbd,
|
||||
0x61,
|
||||
0x79,
|
||||
0xa7,
|
||||
0x1d,
|
||||
0xc9,
|
||||
0x6d,
|
||||
0xbb,
|
||||
},
|
||||
{
|
||||
0x98,
|
||||
0xee,
|
||||
0xa2,
|
||||
0x1a,
|
||||
0xf2,
|
||||
0x5c,
|
||||
0xd6,
|
||||
0xbe,
|
||||
},
|
||||
{
|
||||
0xc7,
|
||||
0x67,
|
||||
0x3b,
|
||||
0x2e,
|
||||
0xb0,
|
||||
0xcb,
|
||||
0xf2,
|
||||
0xd0,
|
||||
},
|
||||
{
|
||||
0x88,
|
||||
0x3e,
|
||||
0xa3,
|
||||
0xe3,
|
||||
0x95,
|
||||
0x67,
|
||||
0x53,
|
||||
0x93,
|
||||
},
|
||||
{
|
||||
0xc8,
|
||||
0xce,
|
||||
0x5c,
|
||||
0xcd,
|
||||
0x8c,
|
||||
0x03,
|
||||
0x0c,
|
||||
0xa8,
|
||||
},
|
||||
{
|
||||
0x94,
|
||||
0xaf,
|
||||
0x49,
|
||||
0xf6,
|
||||
0xc6,
|
||||
0x50,
|
||||
0xad,
|
||||
0xb8,
|
||||
},
|
||||
{
|
||||
0xea,
|
||||
0xb8,
|
||||
0x85,
|
||||
0x8a,
|
||||
0xde,
|
||||
0x92,
|
||||
0xe1,
|
||||
0xbc,
|
||||
},
|
||||
{
|
||||
0xf3,
|
||||
0x15,
|
||||
0xbb,
|
||||
0x5b,
|
||||
0xb8,
|
||||
0x35,
|
||||
0xd8,
|
||||
0x17,
|
||||
},
|
||||
{
|
||||
0xad,
|
||||
0xcf,
|
||||
0x6b,
|
||||
0x07,
|
||||
0x63,
|
||||
0x61,
|
||||
0x2e,
|
||||
0x2f,
|
||||
},
|
||||
{
|
||||
0xa5,
|
||||
0xc9,
|
||||
0x1d,
|
||||
0xa7,
|
||||
0xac,
|
||||
0xaa,
|
||||
0x4d,
|
||||
0xde,
|
||||
},
|
||||
{
|
||||
0x71,
|
||||
0x65,
|
||||
0x95,
|
||||
0x87,
|
||||
0x66,
|
||||
0x50,
|
||||
0xa2,
|
||||
0xa6,
|
||||
},
|
||||
{
|
||||
0x28,
|
||||
0xef,
|
||||
0x49,
|
||||
0x5c,
|
||||
0x53,
|
||||
0xa3,
|
||||
0x87,
|
||||
0xad,
|
||||
},
|
||||
{
|
||||
0x42,
|
||||
0xc3,
|
||||
0x41,
|
||||
0xd8,
|
||||
0xfa,
|
||||
0x92,
|
||||
0xd8,
|
||||
0x32,
|
||||
},
|
||||
{
|
||||
0xce,
|
||||
0x7c,
|
||||
0xf2,
|
||||
0x72,
|
||||
0x2f,
|
||||
0x51,
|
||||
0x27,
|
||||
0x71,
|
||||
},
|
||||
{
|
||||
0xe3,
|
||||
0x78,
|
||||
0x59,
|
||||
0xf9,
|
||||
0x46,
|
||||
0x23,
|
||||
0xf3,
|
||||
0xa7,
|
||||
},
|
||||
{
|
||||
0x38,
|
||||
0x12,
|
||||
0x05,
|
||||
0xbb,
|
||||
0x1a,
|
||||
0xb0,
|
||||
0xe0,
|
||||
0x12,
|
||||
},
|
||||
{
|
||||
0xae,
|
||||
0x97,
|
||||
0xa1,
|
||||
0x0f,
|
||||
0xd4,
|
||||
0x34,
|
||||
0xe0,
|
||||
0x15,
|
||||
},
|
||||
{
|
||||
0xb4,
|
||||
0xa3,
|
||||
0x15,
|
||||
0x08,
|
||||
0xbe,
|
||||
0xff,
|
||||
0x4d,
|
||||
0x31,
|
||||
},
|
||||
{
|
||||
0x81,
|
||||
0x39,
|
||||
0x62,
|
||||
0x29,
|
||||
0xf0,
|
||||
0x90,
|
||||
0x79,
|
||||
0x02,
|
||||
},
|
||||
{
|
||||
0x4d,
|
||||
0x0c,
|
||||
0xf4,
|
||||
0x9e,
|
||||
0xe5,
|
||||
0xd4,
|
||||
0xdc,
|
||||
0xca,
|
||||
},
|
||||
{
|
||||
0x5c,
|
||||
0x73,
|
||||
0x33,
|
||||
0x6a,
|
||||
0x76,
|
||||
0xd8,
|
||||
0xbf,
|
||||
0x9a,
|
||||
},
|
||||
{
|
||||
0xd0,
|
||||
0xa7,
|
||||
0x04,
|
||||
0x53,
|
||||
0x6b,
|
||||
0xa9,
|
||||
0x3e,
|
||||
0x0e,
|
||||
},
|
||||
{
|
||||
0x92,
|
||||
0x59,
|
||||
0x58,
|
||||
0xfc,
|
||||
0xd6,
|
||||
0x42,
|
||||
0x0c,
|
||||
0xad,
|
||||
},
|
||||
{
|
||||
0xa9,
|
||||
0x15,
|
||||
0xc2,
|
||||
0x9b,
|
||||
0xc8,
|
||||
0x06,
|
||||
0x73,
|
||||
0x18,
|
||||
},
|
||||
{
|
||||
0x95,
|
||||
0x2b,
|
||||
0x79,
|
||||
0xf3,
|
||||
0xbc,
|
||||
0x0a,
|
||||
0xa6,
|
||||
0xd4,
|
||||
},
|
||||
{
|
||||
0xf2,
|
||||
0x1d,
|
||||
0xf2,
|
||||
0xe4,
|
||||
0x1d,
|
||||
0x45,
|
||||
0x35,
|
||||
0xf9,
|
||||
},
|
||||
{
|
||||
0x87,
|
||||
0x57,
|
||||
0x75,
|
||||
0x19,
|
||||
0x04,
|
||||
0x8f,
|
||||
0x53,
|
||||
0xa9,
|
||||
},
|
||||
{
|
||||
0x10,
|
||||
0xa5,
|
||||
0x6c,
|
||||
0xf5,
|
||||
0xdf,
|
||||
0xcd,
|
||||
0x9a,
|
||||
0xdb,
|
||||
},
|
||||
{
|
||||
0xeb,
|
||||
0x75,
|
||||
0x09,
|
||||
0x5c,
|
||||
0xcd,
|
||||
0x98,
|
||||
0x6c,
|
||||
0xd0,
|
||||
},
|
||||
{
|
||||
0x51,
|
||||
0xa9,
|
||||
0xcb,
|
||||
0x9e,
|
||||
0xcb,
|
||||
0xa3,
|
||||
0x12,
|
||||
0xe6,
|
||||
},
|
||||
{
|
||||
0x96,
|
||||
0xaf,
|
||||
0xad,
|
||||
0xfc,
|
||||
0x2c,
|
||||
0xe6,
|
||||
0x66,
|
||||
0xc7,
|
||||
},
|
||||
{
|
||||
0x72,
|
||||
0xfe,
|
||||
0x52,
|
||||
0x97,
|
||||
0x5a,
|
||||
0x43,
|
||||
0x64,
|
||||
0xee,
|
||||
},
|
||||
{
|
||||
0x5a,
|
||||
0x16,
|
||||
0x45,
|
||||
0xb2,
|
||||
0x76,
|
||||
0xd5,
|
||||
0x92,
|
||||
0xa1,
|
||||
},
|
||||
{
|
||||
0xb2,
|
||||
0x74,
|
||||
0xcb,
|
||||
0x8e,
|
||||
0xbf,
|
||||
0x87,
|
||||
0x87,
|
||||
0x0a,
|
||||
},
|
||||
{
|
||||
0x6f,
|
||||
0x9b,
|
||||
0xb4,
|
||||
0x20,
|
||||
0x3d,
|
||||
0xe7,
|
||||
0xb3,
|
||||
0x81,
|
||||
},
|
||||
{
|
||||
0xea,
|
||||
0xec,
|
||||
0xb2,
|
||||
0xa3,
|
||||
0x0b,
|
||||
0x22,
|
||||
0xa8,
|
||||
0x7f,
|
||||
},
|
||||
{
|
||||
0x99,
|
||||
0x24,
|
||||
0xa4,
|
||||
0x3c,
|
||||
0xc1,
|
||||
0x31,
|
||||
0x57,
|
||||
0x24,
|
||||
},
|
||||
{
|
||||
0xbd,
|
||||
0x83,
|
||||
0x8d,
|
||||
0x3a,
|
||||
0xaf,
|
||||
0xbf,
|
||||
0x8d,
|
||||
0xb7,
|
||||
},
|
||||
{
|
||||
0x0b,
|
||||
0x1a,
|
||||
0x2a,
|
||||
0x32,
|
||||
0x65,
|
||||
0xd5,
|
||||
0x1a,
|
||||
0xea,
|
||||
},
|
||||
{
|
||||
0x13,
|
||||
0x50,
|
||||
0x79,
|
||||
0xa3,
|
||||
0x23,
|
||||
0x1c,
|
||||
0xe6,
|
||||
0x60,
|
||||
},
|
||||
{
|
||||
0x93,
|
||||
0x2b,
|
||||
0x28,
|
||||
0x46,
|
||||
0xe4,
|
||||
0xd7,
|
||||
0x06,
|
||||
0x66,
|
||||
},
|
||||
{
|
||||
0xe1,
|
||||
0x91,
|
||||
0x5f,
|
||||
0x5c,
|
||||
0xb1,
|
||||
0xec,
|
||||
0xa4,
|
||||
0x6c,
|
||||
},
|
||||
{
|
||||
0xf3,
|
||||
0x25,
|
||||
0x96,
|
||||
0x5c,
|
||||
0xa1,
|
||||
0x6d,
|
||||
0x62,
|
||||
0x9f,
|
||||
},
|
||||
{
|
||||
0x57,
|
||||
0x5f,
|
||||
0xf2,
|
||||
0x8e,
|
||||
0x60,
|
||||
0x38,
|
||||
0x1b,
|
||||
0xe5,
|
||||
},
|
||||
{
|
||||
0x72,
|
||||
0x45,
|
||||
0x06,
|
||||
0xeb,
|
||||
0x4c,
|
||||
0x32,
|
||||
0x8a,
|
||||
0x95,
|
||||
},
|
||||
};
|
||||
const uint8_t vectors_sip128[64][16] = {
|
||||
{
|
||||
0xa3,
|
||||
0x81,
|
||||
0x7f,
|
||||
0x04,
|
||||
0xba,
|
||||
0x25,
|
||||
0xa8,
|
||||
0xe6,
|
||||
0x6d,
|
||||
0xf6,
|
||||
0x72,
|
||||
0x14,
|
||||
0xc7,
|
||||
0x55,
|
||||
0x02,
|
||||
0x93,
|
||||
},
|
||||
{
|
||||
0xda,
|
||||
0x87,
|
||||
0xc1,
|
||||
0xd8,
|
||||
0x6b,
|
||||
0x99,
|
||||
0xaf,
|
||||
0x44,
|
||||
0x34,
|
||||
0x76,
|
||||
0x59,
|
||||
0x11,
|
||||
0x9b,
|
||||
0x22,
|
||||
0xfc,
|
||||
0x45,
|
||||
},
|
||||
{
|
||||
0x81,
|
||||
0x77,
|
||||
0x22,
|
||||
0x8d,
|
||||
0xa4,
|
||||
0xa4,
|
||||
0x5d,
|
||||
0xc7,
|
||||
0xfc,
|
||||
0xa3,
|
||||
0x8b,
|
||||
0xde,
|
||||
0xf6,
|
||||
0x0a,
|
||||
0xff,
|
||||
0xe4,
|
||||
},
|
||||
{
|
||||
0x9c,
|
||||
0x70,
|
||||
0xb6,
|
||||
0x0c,
|
||||
0x52,
|
||||
0x67,
|
||||
0xa9,
|
||||
0x4e,
|
||||
0x5f,
|
||||
0x33,
|
||||
0xb6,
|
||||
0xb0,
|
||||
0x29,
|
||||
0x85,
|
||||
0xed,
|
||||
0x51,
|
||||
},
|
||||
{
|
||||
0xf8,
|
||||
0x81,
|
||||
0x64,
|
||||
0xc1,
|
||||
0x2d,
|
||||
0x9c,
|
||||
0x8f,
|
||||
0xaf,
|
||||
0x7d,
|
||||
0x0f,
|
||||
0x6e,
|
||||
0x7c,
|
||||
0x7b,
|
||||
0xcd,
|
||||
0x55,
|
||||
0x79,
|
||||
},
|
||||
{
|
||||
0x13,
|
||||
0x68,
|
||||
0x87,
|
||||
0x59,
|
||||
0x80,
|
||||
0x77,
|
||||
0x6f,
|
||||
0x88,
|
||||
0x54,
|
||||
0x52,
|
||||
0x7a,
|
||||
0x07,
|
||||
0x69,
|
||||
0x0e,
|
||||
0x96,
|
||||
0x27,
|
||||
},
|
||||
{
|
||||
0x14,
|
||||
0xee,
|
||||
0xca,
|
||||
0x33,
|
||||
0x8b,
|
||||
0x20,
|
||||
0x86,
|
||||
0x13,
|
||||
0x48,
|
||||
0x5e,
|
||||
0xa0,
|
||||
0x30,
|
||||
0x8f,
|
||||
0xd7,
|
||||
0xa1,
|
||||
0x5e,
|
||||
},
|
||||
{
|
||||
0xa1,
|
||||
0xf1,
|
||||
0xeb,
|
||||
0xbe,
|
||||
0xd8,
|
||||
0xdb,
|
||||
0xc1,
|
||||
0x53,
|
||||
0xc0,
|
||||
0xb8,
|
||||
0x4a,
|
||||
0xa6,
|
||||
0x1f,
|
||||
0xf0,
|
||||
0x82,
|
||||
0x39,
|
||||
},
|
||||
{
|
||||
0x3b,
|
||||
0x62,
|
||||
0xa9,
|
||||
0xba,
|
||||
0x62,
|
||||
0x58,
|
||||
0xf5,
|
||||
0x61,
|
||||
0x0f,
|
||||
0x83,
|
||||
0xe2,
|
||||
0x64,
|
||||
0xf3,
|
||||
0x14,
|
||||
0x97,
|
||||
0xb4,
|
||||
},
|
||||
{
|
||||
0x26,
|
||||
0x44,
|
||||
0x99,
|
||||
0x06,
|
||||
0x0a,
|
||||
0xd9,
|
||||
0xba,
|
||||
0xab,
|
||||
0xc4,
|
||||
0x7f,
|
||||
0x8b,
|
||||
0x02,
|
||||
0xbb,
|
||||
0x6d,
|
||||
0x71,
|
||||
0xed,
|
||||
},
|
||||
{
|
||||
0x00,
|
||||
0x11,
|
||||
0x0d,
|
||||
0xc3,
|
||||
0x78,
|
||||
0x14,
|
||||
0x69,
|
||||
0x56,
|
||||
0xc9,
|
||||
0x54,
|
||||
0x47,
|
||||
0xd3,
|
||||
0xf3,
|
||||
0xd0,
|
||||
0xfb,
|
||||
0xba,
|
||||
},
|
||||
{
|
||||
0x01,
|
||||
0x51,
|
||||
0xc5,
|
||||
0x68,
|
||||
0x38,
|
||||
0x6b,
|
||||
0x66,
|
||||
0x77,
|
||||
0xa2,
|
||||
0xb4,
|
||||
0xdc,
|
||||
0x6f,
|
||||
0x81,
|
||||
0xe5,
|
||||
0xdc,
|
||||
0x18,
|
||||
},
|
||||
{
|
||||
0xd6,
|
||||
0x26,
|
||||
0xb2,
|
||||
0x66,
|
||||
0x90,
|
||||
0x5e,
|
||||
0xf3,
|
||||
0x58,
|
||||
0x82,
|
||||
0x63,
|
||||
0x4d,
|
||||
0xf6,
|
||||
0x85,
|
||||
0x32,
|
||||
0xc1,
|
||||
0x25,
|
||||
},
|
||||
{
|
||||
0x98,
|
||||
0x69,
|
||||
0xe2,
|
||||
0x47,
|
||||
0xe9,
|
||||
0xc0,
|
||||
0x8b,
|
||||
0x10,
|
||||
0xd0,
|
||||
0x29,
|
||||
0x93,
|
||||
0x4f,
|
||||
0xc4,
|
||||
0xb9,
|
||||
0x52,
|
||||
0xf7,
|
||||
},
|
||||
{
|
||||
0x31,
|
||||
0xfc,
|
||||
0xef,
|
||||
0xac,
|
||||
0x66,
|
||||
0xd7,
|
||||
0xde,
|
||||
0x9c,
|
||||
0x7e,
|
||||
0xc7,
|
||||
0x48,
|
||||
0x5f,
|
||||
0xe4,
|
||||
0x49,
|
||||
0x49,
|
||||
0x02,
|
||||
},
|
||||
{
|
||||
0x54,
|
||||
0x93,
|
||||
0xe9,
|
||||
0x99,
|
||||
0x33,
|
||||
0xb0,
|
||||
0xa8,
|
||||
0x11,
|
||||
0x7e,
|
||||
0x08,
|
||||
0xec,
|
||||
0x0f,
|
||||
0x97,
|
||||
0xcf,
|
||||
0xc3,
|
||||
0xd9,
|
||||
},
|
||||
{
|
||||
0x6e,
|
||||
0xe2,
|
||||
0xa4,
|
||||
0xca,
|
||||
0x67,
|
||||
0xb0,
|
||||
0x54,
|
||||
0xbb,
|
||||
0xfd,
|
||||
0x33,
|
||||
0x15,
|
||||
0xbf,
|
||||
0x85,
|
||||
0x23,
|
||||
0x05,
|
||||
0x77,
|
||||
},
|
||||
{
|
||||
0x47,
|
||||
0x3d,
|
||||
0x06,
|
||||
0xe8,
|
||||
0x73,
|
||||
0x8d,
|
||||
0xb8,
|
||||
0x98,
|
||||
0x54,
|
||||
0xc0,
|
||||
0x66,
|
||||
0xc4,
|
||||
0x7a,
|
||||
0xe4,
|
||||
0x77,
|
||||
0x40,
|
||||
},
|
||||
{
|
||||
0xa4,
|
||||
0x26,
|
||||
0xe5,
|
||||
0xe4,
|
||||
0x23,
|
||||
0xbf,
|
||||
0x48,
|
||||
0x85,
|
||||
0x29,
|
||||
0x4d,
|
||||
0xa4,
|
||||
0x81,
|
||||
0xfe,
|
||||
0xae,
|
||||
0xf7,
|
||||
0x23,
|
||||
},
|
||||
{
|
||||
0x78,
|
||||
0x01,
|
||||
0x77,
|
||||
0x31,
|
||||
0xcf,
|
||||
0x65,
|
||||
0xfa,
|
||||
0xb0,
|
||||
0x74,
|
||||
0xd5,
|
||||
0x20,
|
||||
0x89,
|
||||
0x52,
|
||||
0x51,
|
||||
0x2e,
|
||||
0xb1,
|
||||
},
|
||||
{
|
||||
0x9e,
|
||||
0x25,
|
||||
0xfc,
|
||||
0x83,
|
||||
0x3f,
|
||||
0x22,
|
||||
0x90,
|
||||
0x73,
|
||||
0x3e,
|
||||
0x93,
|
||||
0x44,
|
||||
0xa5,
|
||||
0xe8,
|
||||
0x38,
|
||||
0x39,
|
||||
0xeb,
|
||||
},
|
||||
{
|
||||
0x56,
|
||||
0x8e,
|
||||
0x49,
|
||||
0x5a,
|
||||
0xbe,
|
||||
0x52,
|
||||
0x5a,
|
||||
0x21,
|
||||
0x8a,
|
||||
0x22,
|
||||
0x14,
|
||||
0xcd,
|
||||
0x3e,
|
||||
0x07,
|
||||
0x1d,
|
||||
0x12,
|
||||
},
|
||||
{
|
||||
0x4a,
|
||||
0x29,
|
||||
0xb5,
|
||||
0x45,
|
||||
0x52,
|
||||
0xd1,
|
||||
0x6b,
|
||||
0x9a,
|
||||
0x46,
|
||||
0x9c,
|
||||
0x10,
|
||||
0x52,
|
||||
0x8e,
|
||||
0xff,
|
||||
0x0a,
|
||||
0xae,
|
||||
},
|
||||
{
|
||||
0xc9,
|
||||
0xd1,
|
||||
0x84,
|
||||
0xdd,
|
||||
0xd5,
|
||||
0xa9,
|
||||
0xf5,
|
||||
0xe0,
|
||||
0xcf,
|
||||
0x8c,
|
||||
0xe2,
|
||||
0x9a,
|
||||
0x9a,
|
||||
0xbf,
|
||||
0x69,
|
||||
0x1c,
|
||||
},
|
||||
{
|
||||
0x2d,
|
||||
0xb4,
|
||||
0x79,
|
||||
0xae,
|
||||
0x78,
|
||||
0xbd,
|
||||
0x50,
|
||||
0xd8,
|
||||
0x88,
|
||||
0x2a,
|
||||
0x8a,
|
||||
0x17,
|
||||
0x8a,
|
||||
0x61,
|
||||
0x32,
|
||||
0xad,
|
||||
},
|
||||
{
|
||||
0x8e,
|
||||
0xce,
|
||||
0x5f,
|
||||
0x04,
|
||||
0x2d,
|
||||
0x5e,
|
||||
0x44,
|
||||
0x7b,
|
||||
0x50,
|
||||
0x51,
|
||||
0xb9,
|
||||
0xea,
|
||||
0xcb,
|
||||
0x8d,
|
||||
0x8f,
|
||||
0x6f,
|
||||
},
|
||||
{
|
||||
0x9c,
|
||||
0x0b,
|
||||
0x53,
|
||||
0xb4,
|
||||
0xb3,
|
||||
0xc3,
|
||||
0x07,
|
||||
0xe8,
|
||||
0x7e,
|
||||
0xae,
|
||||
0xe0,
|
||||
0x86,
|
||||
0x78,
|
||||
0x14,
|
||||
0x1f,
|
||||
0x66,
|
||||
},
|
||||
{
|
||||
0xab,
|
||||
0xf2,
|
||||
0x48,
|
||||
0xaf,
|
||||
0x69,
|
||||
0xa6,
|
||||
0xea,
|
||||
0xe4,
|
||||
0xbf,
|
||||
0xd3,
|
||||
0xeb,
|
||||
0x2f,
|
||||
0x12,
|
||||
0x9e,
|
||||
0xeb,
|
||||
0x94,
|
||||
},
|
||||
{
|
||||
0x06,
|
||||
0x64,
|
||||
0xda,
|
||||
0x16,
|
||||
0x68,
|
||||
0x57,
|
||||
0x4b,
|
||||
0x88,
|
||||
0xb9,
|
||||
0x35,
|
||||
0xf3,
|
||||
0x02,
|
||||
0x73,
|
||||
0x58,
|
||||
0xae,
|
||||
0xf4,
|
||||
},
|
||||
{
|
||||
0xaa,
|
||||
0x4b,
|
||||
0x9d,
|
||||
0xc4,
|
||||
0xbf,
|
||||
0x33,
|
||||
0x7d,
|
||||
0xe9,
|
||||
0x0c,
|
||||
0xd4,
|
||||
0xfd,
|
||||
0x3c,
|
||||
0x46,
|
||||
0x7c,
|
||||
0x6a,
|
||||
0xb7,
|
||||
},
|
||||
{
|
||||
0xea,
|
||||
0x5c,
|
||||
0x7f,
|
||||
0x47,
|
||||
0x1f,
|
||||
0xaf,
|
||||
0x6b,
|
||||
0xde,
|
||||
0x2b,
|
||||
0x1a,
|
||||
0xd7,
|
||||
0xd4,
|
||||
0x68,
|
||||
0x6d,
|
||||
0x22,
|
||||
0x87,
|
||||
},
|
||||
{
|
||||
0x29,
|
||||
0x39,
|
||||
0xb0,
|
||||
0x18,
|
||||
0x32,
|
||||
0x23,
|
||||
0xfa,
|
||||
0xfc,
|
||||
0x17,
|
||||
0x23,
|
||||
0xde,
|
||||
0x4f,
|
||||
0x52,
|
||||
0xc4,
|
||||
0x3d,
|
||||
0x35,
|
||||
},
|
||||
{
|
||||
0x7c,
|
||||
0x39,
|
||||
0x56,
|
||||
0xca,
|
||||
0x5e,
|
||||
0xea,
|
||||
0xfc,
|
||||
0x3e,
|
||||
0x36,
|
||||
0x3e,
|
||||
0x9d,
|
||||
0x55,
|
||||
0x65,
|
||||
0x46,
|
||||
0xeb,
|
||||
0x68,
|
||||
},
|
||||
{
|
||||
0x77,
|
||||
0xc6,
|
||||
0x07,
|
||||
0x71,
|
||||
0x46,
|
||||
0xf0,
|
||||
0x1c,
|
||||
0x32,
|
||||
0xb6,
|
||||
0xb6,
|
||||
0x9d,
|
||||
0x5f,
|
||||
0x4e,
|
||||
0xa9,
|
||||
0xff,
|
||||
0xcf,
|
||||
},
|
||||
{
|
||||
0x37,
|
||||
0xa6,
|
||||
0x98,
|
||||
0x6c,
|
||||
0xb8,
|
||||
0x84,
|
||||
0x7e,
|
||||
0xdf,
|
||||
0x09,
|
||||
0x25,
|
||||
0xf0,
|
||||
0xf1,
|
||||
0x30,
|
||||
0x9b,
|
||||
0x54,
|
||||
0xde,
|
||||
},
|
||||
{
|
||||
0xa7,
|
||||
0x05,
|
||||
0xf0,
|
||||
0xe6,
|
||||
0x9d,
|
||||
0xa9,
|
||||
0xa8,
|
||||
0xf9,
|
||||
0x07,
|
||||
0x24,
|
||||
0x1a,
|
||||
0x2e,
|
||||
0x92,
|
||||
0x3c,
|
||||
0x8c,
|
||||
0xc8,
|
||||
},
|
||||
{
|
||||
0x3d,
|
||||
0xc4,
|
||||
0x7d,
|
||||
0x1f,
|
||||
0x29,
|
||||
0xc4,
|
||||
0x48,
|
||||
0x46,
|
||||
0x1e,
|
||||
0x9e,
|
||||
0x76,
|
||||
0xed,
|
||||
0x90,
|
||||
0x4f,
|
||||
0x67,
|
||||
0x11,
|
||||
},
|
||||
{
|
||||
0x0d,
|
||||
0x62,
|
||||
0xbf,
|
||||
0x01,
|
||||
0xe6,
|
||||
0xfc,
|
||||
0x0e,
|
||||
0x1a,
|
||||
0x0d,
|
||||
0x3c,
|
||||
0x47,
|
||||
0x51,
|
||||
0xc5,
|
||||
0xd3,
|
||||
0x69,
|
||||
0x2b,
|
||||
},
|
||||
{
|
||||
0x8c,
|
||||
0x03,
|
||||
0x46,
|
||||
0x8b,
|
||||
0xca,
|
||||
0x7c,
|
||||
0x66,
|
||||
0x9e,
|
||||
0xe4,
|
||||
0xfd,
|
||||
0x5e,
|
||||
0x08,
|
||||
0x4b,
|
||||
0xbe,
|
||||
0xe7,
|
||||
0xb5,
|
||||
},
|
||||
{
|
||||
0x52,
|
||||
0x8a,
|
||||
0x5b,
|
||||
0xb9,
|
||||
0x3b,
|
||||
0xaf,
|
||||
0x2c,
|
||||
0x9c,
|
||||
0x44,
|
||||
0x73,
|
||||
0xcc,
|
||||
0xe5,
|
||||
0xd0,
|
||||
0xd2,
|
||||
0x2b,
|
||||
0xd9,
|
||||
},
|
||||
{
|
||||
0xdf,
|
||||
0x6a,
|
||||
0x30,
|
||||
0x1e,
|
||||
0x95,
|
||||
0xc9,
|
||||
0x5d,
|
||||
0xad,
|
||||
0x97,
|
||||
0xae,
|
||||
0x0c,
|
||||
0xc8,
|
||||
0xc6,
|
||||
0x91,
|
||||
0x3b,
|
||||
0xd8,
|
||||
},
|
||||
{
|
||||
0x80,
|
||||
0x11,
|
||||
0x89,
|
||||
0x90,
|
||||
0x2c,
|
||||
0x85,
|
||||
0x7f,
|
||||
0x39,
|
||||
0xe7,
|
||||
0x35,
|
||||
0x91,
|
||||
0x28,
|
||||
0x5e,
|
||||
0x70,
|
||||
0xb6,
|
||||
0xdb,
|
||||
},
|
||||
{
|
||||
0xe6,
|
||||
0x17,
|
||||
0x34,
|
||||
0x6a,
|
||||
0xc9,
|
||||
0xc2,
|
||||
0x31,
|
||||
0xbb,
|
||||
0x36,
|
||||
0x50,
|
||||
0xae,
|
||||
0x34,
|
||||
0xcc,
|
||||
0xca,
|
||||
0x0c,
|
||||
0x5b,
|
||||
},
|
||||
{
|
||||
0x27,
|
||||
0xd9,
|
||||
0x34,
|
||||
0x37,
|
||||
0xef,
|
||||
0xb7,
|
||||
0x21,
|
||||
0xaa,
|
||||
0x40,
|
||||
0x18,
|
||||
0x21,
|
||||
0xdc,
|
||||
0xec,
|
||||
0x5a,
|
||||
0xdf,
|
||||
0x89,
|
||||
},
|
||||
{
|
||||
0x89,
|
||||
0x23,
|
||||
0x7d,
|
||||
0x9d,
|
||||
0xed,
|
||||
0x9c,
|
||||
0x5e,
|
||||
0x78,
|
||||
0xd8,
|
||||
0xb1,
|
||||
0xc9,
|
||||
0xb1,
|
||||
0x66,
|
||||
0xcc,
|
||||
0x73,
|
||||
0x42,
|
||||
},
|
||||
{
|
||||
0x4a,
|
||||
0x6d,
|
||||
0x80,
|
||||
0x91,
|
||||
0xbf,
|
||||
0x5e,
|
||||
0x7d,
|
||||
0x65,
|
||||
0x11,
|
||||
0x89,
|
||||
0xfa,
|
||||
0x94,
|
||||
0xa2,
|
||||
0x50,
|
||||
0xb1,
|
||||
0x4c,
|
||||
},
|
||||
{
|
||||
0x0e,
|
||||
0x33,
|
||||
0xf9,
|
||||
0x60,
|
||||
0x55,
|
||||
0xe7,
|
||||
0xae,
|
||||
0x89,
|
||||
0x3f,
|
||||
0xfc,
|
||||
0x0e,
|
||||
0x3d,
|
||||
0xcf,
|
||||
0x49,
|
||||
0x29,
|
||||
0x02,
|
||||
},
|
||||
{
|
||||
0xe6,
|
||||
0x1c,
|
||||
0x43,
|
||||
0x2b,
|
||||
0x72,
|
||||
0x0b,
|
||||
0x19,
|
||||
0xd1,
|
||||
0x8e,
|
||||
0xc8,
|
||||
0xd8,
|
||||
0x4b,
|
||||
0xdc,
|
||||
0x63,
|
||||
0x15,
|
||||
0x1b,
|
||||
},
|
||||
{
|
||||
0xf7,
|
||||
0xe5,
|
||||
0xae,
|
||||
0xf5,
|
||||
0x49,
|
||||
0xf7,
|
||||
0x82,
|
||||
0xcf,
|
||||
0x37,
|
||||
0x90,
|
||||
0x55,
|
||||
0xa6,
|
||||
0x08,
|
||||
0x26,
|
||||
0x9b,
|
||||
0x16,
|
||||
},
|
||||
{
|
||||
0x43,
|
||||
0x8d,
|
||||
0x03,
|
||||
0x0f,
|
||||
0xd0,
|
||||
0xb7,
|
||||
0xa5,
|
||||
0x4f,
|
||||
0xa8,
|
||||
0x37,
|
||||
0xf2,
|
||||
0xad,
|
||||
0x20,
|
||||
0x1a,
|
||||
0x64,
|
||||
0x03,
|
||||
},
|
||||
{
|
||||
0xa5,
|
||||
0x90,
|
||||
0xd3,
|
||||
0xee,
|
||||
0x4f,
|
||||
0xbf,
|
||||
0x04,
|
||||
0xe3,
|
||||
0x24,
|
||||
0x7e,
|
||||
0x0d,
|
||||
0x27,
|
||||
0xf2,
|
||||
0x86,
|
||||
0x42,
|
||||
0x3f,
|
||||
},
|
||||
{
|
||||
0x5f,
|
||||
0xe2,
|
||||
0xc1,
|
||||
0xa1,
|
||||
0x72,
|
||||
0xfe,
|
||||
0x93,
|
||||
0xc4,
|
||||
0xb1,
|
||||
0x5c,
|
||||
0xd3,
|
||||
0x7c,
|
||||
0xae,
|
||||
0xf9,
|
||||
0xf5,
|
||||
0x38,
|
||||
},
|
||||
{
|
||||
0x2c,
|
||||
0x97,
|
||||
0x32,
|
||||
0x5c,
|
||||
0xbd,
|
||||
0x06,
|
||||
0xb3,
|
||||
0x6e,
|
||||
0xb2,
|
||||
0x13,
|
||||
0x3d,
|
||||
0xd0,
|
||||
0x8b,
|
||||
0x3a,
|
||||
0x01,
|
||||
0x7c,
|
||||
},
|
||||
{
|
||||
0x92,
|
||||
0xc8,
|
||||
0x14,
|
||||
0x22,
|
||||
0x7a,
|
||||
0x6b,
|
||||
0xca,
|
||||
0x94,
|
||||
0x9f,
|
||||
0xf0,
|
||||
0x65,
|
||||
0x9f,
|
||||
0x00,
|
||||
0x2a,
|
||||
0xd3,
|
||||
0x9e,
|
||||
},
|
||||
{
|
||||
0xdc,
|
||||
0xe8,
|
||||
0x50,
|
||||
0x11,
|
||||
0x0b,
|
||||
0xd8,
|
||||
0x32,
|
||||
0x8c,
|
||||
0xfb,
|
||||
0xd5,
|
||||
0x08,
|
||||
0x41,
|
||||
0xd6,
|
||||
0x91,
|
||||
0x1d,
|
||||
0x87,
|
||||
},
|
||||
{
|
||||
0x67,
|
||||
0xf1,
|
||||
0x49,
|
||||
0x84,
|
||||
0xc7,
|
||||
0xda,
|
||||
0x79,
|
||||
0x12,
|
||||
0x48,
|
||||
0xe3,
|
||||
0x2b,
|
||||
0xb5,
|
||||
0x92,
|
||||
0x25,
|
||||
0x83,
|
||||
0xda,
|
||||
},
|
||||
{
|
||||
0x19,
|
||||
0x38,
|
||||
0xf2,
|
||||
0xcf,
|
||||
0x72,
|
||||
0xd5,
|
||||
0x4e,
|
||||
0xe9,
|
||||
0x7e,
|
||||
0x94,
|
||||
0x16,
|
||||
0x6f,
|
||||
0xa9,
|
||||
0x1d,
|
||||
0x2a,
|
||||
0x36,
|
||||
},
|
||||
{
|
||||
0x74,
|
||||
0x48,
|
||||
0x1e,
|
||||
0x96,
|
||||
0x46,
|
||||
0xed,
|
||||
0x49,
|
||||
0xfe,
|
||||
0x0f,
|
||||
0x62,
|
||||
0x24,
|
||||
0x30,
|
||||
0x16,
|
||||
0x04,
|
||||
0x69,
|
||||
0x8e,
|
||||
},
|
||||
{
|
||||
0x57,
|
||||
0xfc,
|
||||
0xa5,
|
||||
0xde,
|
||||
0x98,
|
||||
0xa9,
|
||||
0xd6,
|
||||
0xd8,
|
||||
0x00,
|
||||
0x64,
|
||||
0x38,
|
||||
0xd0,
|
||||
0x58,
|
||||
0x3d,
|
||||
0x8a,
|
||||
0x1d,
|
||||
},
|
||||
{
|
||||
0x9f,
|
||||
0xec,
|
||||
0xde,
|
||||
0x1c,
|
||||
0xef,
|
||||
0xdc,
|
||||
0x1c,
|
||||
0xbe,
|
||||
0xd4,
|
||||
0x76,
|
||||
0x36,
|
||||
0x74,
|
||||
0xd9,
|
||||
0x57,
|
||||
0x53,
|
||||
0x59,
|
||||
},
|
||||
{
|
||||
0xe3,
|
||||
0x04,
|
||||
0x0c,
|
||||
0x00,
|
||||
0xeb,
|
||||
0x28,
|
||||
0xf1,
|
||||
0x53,
|
||||
0x66,
|
||||
0xca,
|
||||
0x73,
|
||||
0xcb,
|
||||
0xd8,
|
||||
0x72,
|
||||
0xe7,
|
||||
0x40,
|
||||
},
|
||||
{
|
||||
0x76,
|
||||
0x97,
|
||||
0x00,
|
||||
0x9a,
|
||||
0x6a,
|
||||
0x83,
|
||||
0x1d,
|
||||
0xfe,
|
||||
0xcc,
|
||||
0xa9,
|
||||
0x1c,
|
||||
0x59,
|
||||
0x93,
|
||||
0x67,
|
||||
0x0f,
|
||||
0x7a,
|
||||
},
|
||||
{
|
||||
0x58,
|
||||
0x53,
|
||||
0x54,
|
||||
0x23,
|
||||
0x21,
|
||||
0xf5,
|
||||
0x67,
|
||||
0xa0,
|
||||
0x05,
|
||||
0xd5,
|
||||
0x47,
|
||||
0xa4,
|
||||
0xf0,
|
||||
0x47,
|
||||
0x59,
|
||||
0xbd,
|
||||
},
|
||||
{
|
||||
0x51,
|
||||
0x50,
|
||||
0xd1,
|
||||
0x77,
|
||||
0x2f,
|
||||
0x50,
|
||||
0x83,
|
||||
0x4a,
|
||||
0x50,
|
||||
0x3e,
|
||||
0x06,
|
||||
0x9a,
|
||||
0x97,
|
||||
0x3f,
|
||||
0xbd,
|
||||
0x7c,
|
||||
},
|
||||
};
|
||||
const uint8_t vectors_hsip32[64][4] = {
|
||||
{
|
||||
0xa9,
|
||||
0x35,
|
||||
0x9f,
|
||||
0x5b,
|
||||
},
|
||||
{
|
||||
0x27,
|
||||
0x47,
|
||||
0x5a,
|
||||
0xb8,
|
||||
},
|
||||
{
|
||||
0xfa,
|
||||
0x62,
|
||||
0xa6,
|
||||
0x03,
|
||||
},
|
||||
{
|
||||
0x8a,
|
||||
0xfe,
|
||||
0xe7,
|
||||
0x04,
|
||||
},
|
||||
{
|
||||
0x2a,
|
||||
0x6e,
|
||||
0x46,
|
||||
0x89,
|
||||
},
|
||||
{
|
||||
0xc5,
|
||||
0xfa,
|
||||
0xb6,
|
||||
0x69,
|
||||
},
|
||||
{
|
||||
0x58,
|
||||
0x63,
|
||||
0xfc,
|
||||
0x23,
|
||||
},
|
||||
{
|
||||
0x8b,
|
||||
0xcf,
|
||||
0x63,
|
||||
0xc5,
|
||||
},
|
||||
{
|
||||
0xd0,
|
||||
0xb8,
|
||||
0x84,
|
||||
0x8f,
|
||||
},
|
||||
{
|
||||
0xf8,
|
||||
0x06,
|
||||
0xe7,
|
||||
0x79,
|
||||
},
|
||||
{
|
||||
0x94,
|
||||
0xb0,
|
||||
0x79,
|
||||
0x34,
|
||||
},
|
||||
{
|
||||
0x08,
|
||||
0x08,
|
||||
0x30,
|
||||
0x50,
|
||||
},
|
||||
{
|
||||
0x57,
|
||||
0xf0,
|
||||
0x87,
|
||||
0x2f,
|
||||
},
|
||||
{
|
||||
0x77,
|
||||
0xe6,
|
||||
0x63,
|
||||
0xff,
|
||||
},
|
||||
{
|
||||
0xd6,
|
||||
0xff,
|
||||
0xf8,
|
||||
0x7c,
|
||||
},
|
||||
{
|
||||
0x74,
|
||||
0xfe,
|
||||
0x2b,
|
||||
0x97,
|
||||
},
|
||||
{
|
||||
0xd9,
|
||||
0xb5,
|
||||
0xac,
|
||||
0x84,
|
||||
},
|
||||
{
|
||||
0xc4,
|
||||
0x74,
|
||||
0x64,
|
||||
0x5b,
|
||||
},
|
||||
{
|
||||
0x46,
|
||||
0x5b,
|
||||
0x8d,
|
||||
0x9b,
|
||||
},
|
||||
{
|
||||
0x7b,
|
||||
0xef,
|
||||
0xe3,
|
||||
0x87,
|
||||
},
|
||||
{
|
||||
0xe3,
|
||||
0x4d,
|
||||
0x10,
|
||||
0x45,
|
||||
},
|
||||
{
|
||||
0x61,
|
||||
0x3f,
|
||||
0x62,
|
||||
0xb3,
|
||||
},
|
||||
{
|
||||
0x70,
|
||||
0xf3,
|
||||
0x67,
|
||||
0xfe,
|
||||
},
|
||||
{
|
||||
0xe6,
|
||||
0xad,
|
||||
0xb8,
|
||||
0xbd,
|
||||
},
|
||||
{
|
||||
0x27,
|
||||
0x40,
|
||||
0x0c,
|
||||
0x63,
|
||||
},
|
||||
{
|
||||
0x26,
|
||||
0x78,
|
||||
0x78,
|
||||
0x75,
|
||||
},
|
||||
{
|
||||
0x4f,
|
||||
0x56,
|
||||
0x7b,
|
||||
0x5f,
|
||||
},
|
||||
{
|
||||
0x3a,
|
||||
0xb0,
|
||||
0xe6,
|
||||
0x69,
|
||||
},
|
||||
{
|
||||
0xb0,
|
||||
0x64,
|
||||
0x40,
|
||||
0x00,
|
||||
},
|
||||
{
|
||||
0xff,
|
||||
0x67,
|
||||
0x0f,
|
||||
0xb4,
|
||||
},
|
||||
{
|
||||
0x50,
|
||||
0x9e,
|
||||
0x33,
|
||||
0x8b,
|
||||
},
|
||||
{
|
||||
0x5d,
|
||||
0x58,
|
||||
0x9f,
|
||||
0x1a,
|
||||
},
|
||||
{
|
||||
0xfe,
|
||||
0xe7,
|
||||
0x21,
|
||||
0x12,
|
||||
},
|
||||
{
|
||||
0x33,
|
||||
0x75,
|
||||
0x32,
|
||||
0x59,
|
||||
},
|
||||
{
|
||||
0x6a,
|
||||
0x43,
|
||||
0x4f,
|
||||
0x8c,
|
||||
},
|
||||
{
|
||||
0xfe,
|
||||
0x28,
|
||||
0xb7,
|
||||
0x29,
|
||||
},
|
||||
{
|
||||
0xe7,
|
||||
0x5c,
|
||||
0xc6,
|
||||
0xec,
|
||||
},
|
||||
{
|
||||
0x69,
|
||||
0x7e,
|
||||
0x8d,
|
||||
0x54,
|
||||
},
|
||||
{
|
||||
0x63,
|
||||
0x68,
|
||||
0x8b,
|
||||
0x0f,
|
||||
},
|
||||
{
|
||||
0x65,
|
||||
0x0b,
|
||||
0x62,
|
||||
0xb4,
|
||||
},
|
||||
{
|
||||
0xb6,
|
||||
0xbc,
|
||||
0x18,
|
||||
0x40,
|
||||
},
|
||||
{
|
||||
0x5d,
|
||||
0x07,
|
||||
0x45,
|
||||
0x05,
|
||||
},
|
||||
{
|
||||
0x24,
|
||||
0x42,
|
||||
0xfd,
|
||||
0x2e,
|
||||
},
|
||||
{
|
||||
0x7b,
|
||||
0xb7,
|
||||
0x86,
|
||||
0x3a,
|
||||
},
|
||||
{
|
||||
0x77,
|
||||
0x05,
|
||||
0xd5,
|
||||
0x48,
|
||||
},
|
||||
{
|
||||
0xd7,
|
||||
0x52,
|
||||
0x08,
|
||||
0xb1,
|
||||
},
|
||||
{
|
||||
0xb6,
|
||||
0xd4,
|
||||
0x99,
|
||||
0xc8,
|
||||
},
|
||||
{
|
||||
0x08,
|
||||
0x92,
|
||||
0x20,
|
||||
0x2e,
|
||||
},
|
||||
{
|
||||
0x69,
|
||||
0xe1,
|
||||
0x2c,
|
||||
0xe3,
|
||||
},
|
||||
{
|
||||
0x8d,
|
||||
0xb5,
|
||||
0x80,
|
||||
0xe5,
|
||||
},
|
||||
{
|
||||
0x36,
|
||||
0x97,
|
||||
0x64,
|
||||
0xc6,
|
||||
},
|
||||
{
|
||||
0x01,
|
||||
0x6e,
|
||||
0x02,
|
||||
0x04,
|
||||
},
|
||||
{
|
||||
0x3b,
|
||||
0x85,
|
||||
0xf3,
|
||||
0xd4,
|
||||
},
|
||||
{
|
||||
0xfe,
|
||||
0xdb,
|
||||
0x66,
|
||||
0xbe,
|
||||
},
|
||||
{
|
||||
0x1e,
|
||||
0x69,
|
||||
0x2a,
|
||||
0x3a,
|
||||
},
|
||||
{
|
||||
0xc6,
|
||||
0x89,
|
||||
0x84,
|
||||
0xc0,
|
||||
},
|
||||
{
|
||||
0xa5,
|
||||
0xc5,
|
||||
0xb9,
|
||||
0x40,
|
||||
},
|
||||
{
|
||||
0x9b,
|
||||
0xe9,
|
||||
0xe8,
|
||||
0x8c,
|
||||
},
|
||||
{
|
||||
0x7d,
|
||||
0xbc,
|
||||
0x81,
|
||||
0x40,
|
||||
},
|
||||
{
|
||||
0x7c,
|
||||
0x07,
|
||||
0x8e,
|
||||
0xc5,
|
||||
},
|
||||
{
|
||||
0xd4,
|
||||
0xe7,
|
||||
0x6c,
|
||||
0x73,
|
||||
},
|
||||
{
|
||||
0x42,
|
||||
0x8f,
|
||||
0xcb,
|
||||
0xb9,
|
||||
},
|
||||
{
|
||||
0xbd,
|
||||
0x83,
|
||||
0x99,
|
||||
0x7a,
|
||||
},
|
||||
{
|
||||
0x59,
|
||||
0xea,
|
||||
0x4a,
|
||||
0x74,
|
||||
},
|
||||
};
|
||||
const uint8_t vectors_hsip64[64][8] = {
|
||||
{
|
||||
0x21,
|
||||
0x8d,
|
||||
0x1f,
|
||||
0x59,
|
||||
0xb9,
|
||||
0xb8,
|
||||
0x3c,
|
||||
0xc8,
|
||||
},
|
||||
{
|
||||
0xbe,
|
||||
0x55,
|
||||
0x24,
|
||||
0x12,
|
||||
0xf8,
|
||||
0x38,
|
||||
0x73,
|
||||
0x15,
|
||||
},
|
||||
{
|
||||
0x06,
|
||||
0x4f,
|
||||
0x39,
|
||||
0xef,
|
||||
0x7c,
|
||||
0x50,
|
||||
0xeb,
|
||||
0x57,
|
||||
},
|
||||
{
|
||||
0xce,
|
||||
0x0f,
|
||||
0x1a,
|
||||
0x45,
|
||||
0xf7,
|
||||
0x06,
|
||||
0x06,
|
||||
0x79,
|
||||
},
|
||||
{
|
||||
0xd5,
|
||||
0xe7,
|
||||
0x8a,
|
||||
0x17,
|
||||
0x5b,
|
||||
0xe5,
|
||||
0x2e,
|
||||
0xa1,
|
||||
},
|
||||
{
|
||||
0xcb,
|
||||
0x9d,
|
||||
0x7c,
|
||||
0x3f,
|
||||
0x2f,
|
||||
0x3d,
|
||||
0xb5,
|
||||
0x80,
|
||||
},
|
||||
{
|
||||
0xce,
|
||||
0x3e,
|
||||
0x91,
|
||||
0x35,
|
||||
0x8a,
|
||||
0xa2,
|
||||
0xbc,
|
||||
0x25,
|
||||
},
|
||||
{
|
||||
0xff,
|
||||
0x20,
|
||||
0x27,
|
||||
0x28,
|
||||
0xb0,
|
||||
0x7b,
|
||||
0xc6,
|
||||
0x84,
|
||||
},
|
||||
{
|
||||
0xed,
|
||||
0xfe,
|
||||
0xe8,
|
||||
0x20,
|
||||
0xbc,
|
||||
0xe4,
|
||||
0x85,
|
||||
0x8c,
|
||||
},
|
||||
{
|
||||
0x5b,
|
||||
0x51,
|
||||
0xcc,
|
||||
0xcc,
|
||||
0x13,
|
||||
0x88,
|
||||
0x83,
|
||||
0x07,
|
||||
},
|
||||
{
|
||||
0x95,
|
||||
0xb0,
|
||||
0x46,
|
||||
0x9f,
|
||||
0x06,
|
||||
0xa6,
|
||||
0xf2,
|
||||
0xee,
|
||||
},
|
||||
{
|
||||
0xae,
|
||||
0x26,
|
||||
0x33,
|
||||
0x39,
|
||||
0x94,
|
||||
0xdd,
|
||||
0xcd,
|
||||
0x48,
|
||||
},
|
||||
{
|
||||
0x7b,
|
||||
0xc7,
|
||||
0x1f,
|
||||
0x9f,
|
||||
0xae,
|
||||
0xf5,
|
||||
0xc7,
|
||||
0x99,
|
||||
},
|
||||
{
|
||||
0x5a,
|
||||
0x23,
|
||||
0x52,
|
||||
0xd7,
|
||||
0x5a,
|
||||
0x0c,
|
||||
0x37,
|
||||
0x44,
|
||||
},
|
||||
{
|
||||
0x3b,
|
||||
0xb1,
|
||||
0xa8,
|
||||
0x70,
|
||||
0xea,
|
||||
0xe8,
|
||||
0xe6,
|
||||
0x58,
|
||||
},
|
||||
{
|
||||
0x21,
|
||||
0x7d,
|
||||
0x0b,
|
||||
0xcb,
|
||||
0x4e,
|
||||
0x81,
|
||||
0xc9,
|
||||
0x02,
|
||||
},
|
||||
{
|
||||
0x73,
|
||||
0x36,
|
||||
0xaa,
|
||||
0xd2,
|
||||
0x5f,
|
||||
0x7b,
|
||||
0xf3,
|
||||
0xb5,
|
||||
},
|
||||
{
|
||||
0x37,
|
||||
0xad,
|
||||
0xc0,
|
||||
0x64,
|
||||
0x1c,
|
||||
0x4c,
|
||||
0x4f,
|
||||
0x6a,
|
||||
},
|
||||
{
|
||||
0xc9,
|
||||
0xb2,
|
||||
0xdb,
|
||||
0x2b,
|
||||
0x9a,
|
||||
0x3e,
|
||||
0x42,
|
||||
0xf9,
|
||||
},
|
||||
{
|
||||
0xf9,
|
||||
0x10,
|
||||
0xe4,
|
||||
0x80,
|
||||
0x20,
|
||||
0xab,
|
||||
0x36,
|
||||
0x3c,
|
||||
},
|
||||
{
|
||||
0x1b,
|
||||
0xf5,
|
||||
0x2b,
|
||||
0x0a,
|
||||
0x6f,
|
||||
0xee,
|
||||
0xa7,
|
||||
0xdb,
|
||||
},
|
||||
{
|
||||
0x00,
|
||||
0x74,
|
||||
0x1d,
|
||||
0xc2,
|
||||
0x69,
|
||||
0xe8,
|
||||
0xb3,
|
||||
0xef,
|
||||
},
|
||||
{
|
||||
0xe2,
|
||||
0x01,
|
||||
0x03,
|
||||
0xfa,
|
||||
0x1b,
|
||||
0xa7,
|
||||
0x76,
|
||||
0xef,
|
||||
},
|
||||
{
|
||||
0x4c,
|
||||
0x22,
|
||||
0x10,
|
||||
0xe5,
|
||||
0x4b,
|
||||
0x68,
|
||||
0x1d,
|
||||
0x73,
|
||||
},
|
||||
{
|
||||
0x70,
|
||||
0x74,
|
||||
0x10,
|
||||
0x45,
|
||||
0xae,
|
||||
0x3f,
|
||||
0xa6,
|
||||
0xf1,
|
||||
},
|
||||
{
|
||||
0x0c,
|
||||
0x86,
|
||||
0x40,
|
||||
0x37,
|
||||
0x39,
|
||||
0x71,
|
||||
0x40,
|
||||
0x38,
|
||||
},
|
||||
{
|
||||
0x0d,
|
||||
0x89,
|
||||
0x9e,
|
||||
0xd8,
|
||||
0x11,
|
||||
0x29,
|
||||
0x23,
|
||||
0xf0,
|
||||
},
|
||||
{
|
||||
0x22,
|
||||
0x6b,
|
||||
0xf5,
|
||||
0xfa,
|
||||
0xb8,
|
||||
0x1e,
|
||||
0xe1,
|
||||
0xb8,
|
||||
},
|
||||
{
|
||||
0x2d,
|
||||
0x92,
|
||||
0x5f,
|
||||
0xfb,
|
||||
0x1e,
|
||||
0x00,
|
||||
0x16,
|
||||
0xb5,
|
||||
},
|
||||
{
|
||||
0x36,
|
||||
0x19,
|
||||
0x58,
|
||||
0xd5,
|
||||
0x2c,
|
||||
0xee,
|
||||
0x10,
|
||||
0xf1,
|
||||
},
|
||||
{
|
||||
0x29,
|
||||
0x1a,
|
||||
0xaf,
|
||||
0x86,
|
||||
0x48,
|
||||
0x98,
|
||||
0x17,
|
||||
0x9d,
|
||||
},
|
||||
{
|
||||
0x86,
|
||||
0x3c,
|
||||
0x7f,
|
||||
0x15,
|
||||
0x5c,
|
||||
0x34,
|
||||
0x11,
|
||||
0x7c,
|
||||
},
|
||||
{
|
||||
0x28,
|
||||
0x70,
|
||||
0x9d,
|
||||
0x46,
|
||||
0xd8,
|
||||
0x11,
|
||||
0x62,
|
||||
0x6c,
|
||||
},
|
||||
{
|
||||
0x24,
|
||||
0x84,
|
||||
0x77,
|
||||
0x68,
|
||||
0x1d,
|
||||
0x28,
|
||||
0xf8,
|
||||
0x9c,
|
||||
},
|
||||
{
|
||||
0x83,
|
||||
0x24,
|
||||
0xe4,
|
||||
0xd7,
|
||||
0x52,
|
||||
0x8f,
|
||||
0x98,
|
||||
0x30,
|
||||
},
|
||||
{
|
||||
0xf9,
|
||||
0xef,
|
||||
0xd4,
|
||||
0xe1,
|
||||
0x3a,
|
||||
0xea,
|
||||
0x6b,
|
||||
0xd8,
|
||||
},
|
||||
{
|
||||
0x86,
|
||||
0xd6,
|
||||
0x7a,
|
||||
0x40,
|
||||
0xec,
|
||||
0x42,
|
||||
0x76,
|
||||
0xdc,
|
||||
},
|
||||
{
|
||||
0x3f,
|
||||
0x62,
|
||||
0x92,
|
||||
0xec,
|
||||
0xcc,
|
||||
0xa9,
|
||||
0x7e,
|
||||
0x35,
|
||||
},
|
||||
{
|
||||
0xcb,
|
||||
0xd9,
|
||||
0x2e,
|
||||
0xe7,
|
||||
0x24,
|
||||
0xd4,
|
||||
0x21,
|
||||
0x09,
|
||||
},
|
||||
{
|
||||
0x36,
|
||||
0x8d,
|
||||
0xf6,
|
||||
0x80,
|
||||
0x8d,
|
||||
0x40,
|
||||
0x3d,
|
||||
0x79,
|
||||
},
|
||||
{
|
||||
0x5b,
|
||||
0x38,
|
||||
0xc8,
|
||||
0x1c,
|
||||
0x67,
|
||||
0xc8,
|
||||
0xae,
|
||||
0x4c,
|
||||
},
|
||||
{
|
||||
0x95,
|
||||
0xab,
|
||||
0x71,
|
||||
0x89,
|
||||
0xd4,
|
||||
0x39,
|
||||
0xac,
|
||||
0xb3,
|
||||
},
|
||||
{
|
||||
0xa9,
|
||||
0x1a,
|
||||
0x52,
|
||||
0xc0,
|
||||
0x25,
|
||||
0x32,
|
||||
0x70,
|
||||
0x24,
|
||||
},
|
||||
{
|
||||
0x5b,
|
||||
0x00,
|
||||
0x87,
|
||||
0xc6,
|
||||
0x95,
|
||||
0x28,
|
||||
0xac,
|
||||
0xea,
|
||||
},
|
||||
{
|
||||
0x1e,
|
||||
0x30,
|
||||
0xf3,
|
||||
0xad,
|
||||
0x27,
|
||||
0xdc,
|
||||
0xb1,
|
||||
0x5a,
|
||||
},
|
||||
{
|
||||
0x69,
|
||||
0x7f,
|
||||
0x5c,
|
||||
0x9a,
|
||||
0x90,
|
||||
0x32,
|
||||
0x4e,
|
||||
0xd4,
|
||||
},
|
||||
{
|
||||
0x49,
|
||||
0x5c,
|
||||
0x0f,
|
||||
0x99,
|
||||
0x55,
|
||||
0x57,
|
||||
0xdc,
|
||||
0x38,
|
||||
},
|
||||
{
|
||||
0x94,
|
||||
0x27,
|
||||
0x20,
|
||||
0x2a,
|
||||
0x3c,
|
||||
0x29,
|
||||
0xf9,
|
||||
0x4d,
|
||||
},
|
||||
{
|
||||
0xa9,
|
||||
0xea,
|
||||
0xa8,
|
||||
0xc0,
|
||||
0x4b,
|
||||
0xa9,
|
||||
0x3e,
|
||||
0x3e,
|
||||
},
|
||||
{
|
||||
0xee,
|
||||
0xa4,
|
||||
0xc1,
|
||||
0x73,
|
||||
0x7d,
|
||||
0x01,
|
||||
0x12,
|
||||
0x18,
|
||||
},
|
||||
{
|
||||
0x91,
|
||||
0x2d,
|
||||
0x56,
|
||||
0x8f,
|
||||
0xd8,
|
||||
0xf6,
|
||||
0x5a,
|
||||
0x49,
|
||||
},
|
||||
{
|
||||
0x56,
|
||||
0x91,
|
||||
0x95,
|
||||
0x96,
|
||||
0xb0,
|
||||
0xff,
|
||||
0x5c,
|
||||
0x97,
|
||||
},
|
||||
{
|
||||
0x02,
|
||||
0x44,
|
||||
0x5a,
|
||||
0x79,
|
||||
0x98,
|
||||
0xf5,
|
||||
0x50,
|
||||
0xe1,
|
||||
},
|
||||
{
|
||||
0x86,
|
||||
0xec,
|
||||
0x46,
|
||||
0x6c,
|
||||
0xe7,
|
||||
0x1d,
|
||||
0x1f,
|
||||
0xb2,
|
||||
},
|
||||
{
|
||||
0x35,
|
||||
0x95,
|
||||
0x69,
|
||||
0xe7,
|
||||
0xd2,
|
||||
0x89,
|
||||
0xe3,
|
||||
0xbc,
|
||||
},
|
||||
{
|
||||
0x87,
|
||||
0x1b,
|
||||
0x05,
|
||||
0xca,
|
||||
0x62,
|
||||
0xbb,
|
||||
0x7c,
|
||||
0x96,
|
||||
},
|
||||
{
|
||||
0xa1,
|
||||
0xa4,
|
||||
0x92,
|
||||
0xf9,
|
||||
0x42,
|
||||
0xf1,
|
||||
0x5f,
|
||||
0x1d,
|
||||
},
|
||||
{
|
||||
0x12,
|
||||
0xec,
|
||||
0x26,
|
||||
0x7f,
|
||||
0xf6,
|
||||
0x09,
|
||||
0x5b,
|
||||
0x6e,
|
||||
},
|
||||
{
|
||||
0x5d,
|
||||
0x1b,
|
||||
0x5e,
|
||||
0xa1,
|
||||
0xb2,
|
||||
0x31,
|
||||
0xd8,
|
||||
0x9d,
|
||||
},
|
||||
{
|
||||
0xd8,
|
||||
0xcf,
|
||||
0xb4,
|
||||
0x45,
|
||||
0x3f,
|
||||
0x92,
|
||||
0xee,
|
||||
0x54,
|
||||
},
|
||||
{
|
||||
0xd6,
|
||||
0x76,
|
||||
0x28,
|
||||
0x90,
|
||||
0xbf,
|
||||
0x26,
|
||||
0xe4,
|
||||
0x60,
|
||||
},
|
||||
{
|
||||
0x31,
|
||||
0x35,
|
||||
0x63,
|
||||
0xa4,
|
||||
0xb7,
|
||||
0xed,
|
||||
0x5c,
|
||||
0xf3,
|
||||
},
|
||||
{
|
||||
0xf9,
|
||||
0x0b,
|
||||
0x3a,
|
||||
0xb5,
|
||||
0x72,
|
||||
0xd4,
|
||||
0x66,
|
||||
0x93,
|
||||
},
|
||||
{
|
||||
0x2e,
|
||||
0xa6,
|
||||
0x3c,
|
||||
0x71,
|
||||
0xbf,
|
||||
0x32,
|
||||
0x60,
|
||||
0x87,
|
||||
},
|
||||
};
|
||||
156
3rd/paho.mqtt.c/CMakeLists.txt
Normal file
156
3rd/paho.mqtt.c/CMakeLists.txt
Normal file
@@ -0,0 +1,156 @@
|
||||
#*******************************************************************************
|
||||
# Copyright (c) 2015, 2026 logi.cals GmbH, Frank Pagliughi <fpagliughi@mindspring.com> and others
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v2.0
|
||||
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
#
|
||||
# The Eclipse Public License is available at
|
||||
# https://www.eclipse.org/legal/epl-2.0/
|
||||
# and the Eclipse Distribution License is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# Contributors:
|
||||
# Rainer Poisel - initial version
|
||||
# Genis Riera Perez - Add support for building debian package
|
||||
#*******************************************************************************/
|
||||
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project("Eclipse Paho C"
|
||||
VERSION 1.3.16
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
message(STATUS "CMake version: " ${CMAKE_VERSION})
|
||||
message(STATUS "CMake system name: " ${CMAKE_SYSTEM_NAME})
|
||||
|
||||
set(CMAKE_SCRIPTS "${PROJECT_SOURCE_DIR}/cmake")
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
## Project Version
|
||||
## Previously we read in the version from these files, but now we use the
|
||||
## CMake project setting. We just make sure the files and CMake match.
|
||||
file(READ version.major PAHO_VERSION_MAJOR)
|
||||
file(READ version.minor PAHO_VERSION_MINOR)
|
||||
file(READ version.patch PAHO_VERSION_PATCH)
|
||||
set(CLIENT_VERSION ${PAHO_VERSION_MAJOR}.${PAHO_VERSION_MINOR}.${PAHO_VERSION_PATCH})
|
||||
|
||||
if(NOT (CLIENT_VERSION VERSION_EQUAL PROJECT_VERSION))
|
||||
message(FATAL_ERROR "CMake project version does NOT match. CMake: ${PROJECT_VERSION}, Files: ${CLIENT_VERSION}")
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
string(TIMESTAMP BUILD_TIMESTAMP UTC)
|
||||
message(STATUS "Timestamp is ${BUILD_TIMESTAMP}")
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-D_CRT_SECURE_NO_DEPRECATE -DWIN32_LEAN_AND_MEAN)
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
add_definitions(-DOSX)
|
||||
endif()
|
||||
|
||||
## build options
|
||||
option(PAHO_WITH_SSL "Flag that defines whether to build ssl-enabled binaries too. " FALSE)
|
||||
option(PAHO_WITH_LIBRESSL "Flag that defines whether to build ssl-enabled binaries with LibreSSL instead of OpenSSL. " FALSE)
|
||||
option(PAHO_WITH_LIBUUID "Flag that defines whether libuuid or a custom uuid implementation should be used" FALSE)
|
||||
option(PAHO_BUILD_SHARED "Build shared library" TRUE)
|
||||
option(PAHO_BUILD_STATIC "Build static library" FALSE)
|
||||
option(PAHO_BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" FALSE)
|
||||
option(PAHO_BUILD_SAMPLES "Build sample programs" FALSE)
|
||||
option(PAHO_BUILD_DEB_PACKAGE "Build debian package" FALSE)
|
||||
option(PAHO_ENABLE_TESTING "Build tests and run" TRUE)
|
||||
option(PAHO_ENABLE_CPACK "Enable CPack" TRUE)
|
||||
option(PAHO_HIGH_PERFORMANCE "Disable tracing and heap tracking" FALSE)
|
||||
option(PAHO_USE_SELECT "Revert to select system call instead of poll" FALSE)
|
||||
option(PAHO_NO_TCP_NODELAY "Don't disable Nagle's algorithm on TCP sockets" FALSE)
|
||||
|
||||
if(NOT WIN32)
|
||||
option(PAHO_WITH_UNIX_SOCKETS "Flag that defines whether to enable Unix-domain sockets" FALSE)
|
||||
|
||||
if(PAHO_WITH_UNIX_SOCKETS)
|
||||
add_definitions(-DUNIXSOCK=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(PAHO_HIGH_PERFORMANCE)
|
||||
add_definitions(-DHIGH_PERFORMANCE=1)
|
||||
endif()
|
||||
|
||||
if(PAHO_USE_SELECT)
|
||||
add_definitions(-DUSE_SELECT=1)
|
||||
endif()
|
||||
|
||||
if(PAHO_WITH_LIBUUID)
|
||||
add_definitions(-DUSE_LIBUUID=1)
|
||||
endif()
|
||||
|
||||
if(PAHO_NO_TCP_NODELAY)
|
||||
add_definitions(-DNO_TCP_NODELAY=1)
|
||||
endif()
|
||||
|
||||
if(NOT PAHO_BUILD_SHARED AND NOT PAHO_BUILD_STATIC)
|
||||
message(FATAL_ERROR "You must set either PAHO_BUILD_SHARED, PAHO_BUILD_STATIC, or both")
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_SAMPLES AND NOT (PAHO_WITH_SSL OR PAHO_WITH_LIBRESSL))
|
||||
message(WARNING "You must build with SSL to build the full set of samples")
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_DEB_PACKAGE)
|
||||
set(CMAKE_INSTALL_DOCDIR share/doc/libpaho-mqtt)
|
||||
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON)
|
||||
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY ">=")
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
if(PAHO_BUILD_SAMPLES)
|
||||
add_subdirectory(src/samples)
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_DOCUMENTATION)
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
|
||||
if(PAHO_ENABLE_CPACK)
|
||||
### packaging settings
|
||||
file(GLOB samples "src/samples/*.c")
|
||||
install(FILES ${samples} DESTINATION ${CMAKE_INSTALL_DOCDIR}/samples)
|
||||
|
||||
set(CPACK_PACKAGE_VENDOR "Eclipse Paho")
|
||||
set(CPACK_PACKAGE_NAME "Eclipse-Paho-MQTT-C")
|
||||
install(FILES CONTRIBUTING.md epl-v20 edl-v10 README.md notice.html DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
|
||||
if(WIN32)
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
elseif(PAHO_BUILD_DEB_PACKAGE)
|
||||
install(FILES CONTRIBUTING.md epl-v20 edl-v10 README.md notice.html DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
|
||||
set(CPACK_GENERATOR "DEB")
|
||||
configure_file(${CMAKE_SCRIPTS}/CPackDebConfig.cmake.in
|
||||
${CMAKE_BINARY_DIR}/CPackDebConfig.cmake @ONLY
|
||||
)
|
||||
set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_BINARY_DIR}/CPackDebConfig.cmake)
|
||||
else()
|
||||
set(CPACK_GENERATOR "TGZ")
|
||||
endif()
|
||||
else()
|
||||
file(GLOB samples "src/samples/*.c")
|
||||
install(FILES ${samples} DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
endif()
|
||||
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||
|
||||
include(CPack)
|
||||
|
||||
if(PAHO_ENABLE_TESTING)
|
||||
enable_testing()
|
||||
include_directories(test src)
|
||||
add_subdirectory(test)
|
||||
else()
|
||||
include_directories(src)
|
||||
endif()
|
||||
47
3rd/paho.mqtt.c/CODE_OF_CONDUCT.md
Normal file
47
3rd/paho.mqtt.c/CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Community Code of Conduct
|
||||
|
||||
**Version 1.2
|
||||
August 19, 2020**
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as community members, contributors, committers, and project leaders pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
With the support of the Eclipse Foundation staff (the “Staff”), project committers and leaders are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project committers and leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the Eclipse Foundation project or its community in public spaces. Examples of representing a project or community include posting via an official social media account, or acting as a project representative at an online or offline event. Representation of a project may be further defined and clarified by project committers, leaders, or the EMO.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Staff at codeofconduct@eclipse.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The Staff is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project committers or leaders who do not follow the Code of Conduct in good faith may face temporary or permanent repercussions as determined by the Staff.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org) , version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/)
|
||||
|
||||
63
3rd/paho.mqtt.c/CONTRIBUTING.md
Normal file
63
3rd/paho.mqtt.c/CONTRIBUTING.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Contributing to Paho
|
||||
|
||||
Thanks for your interest in this project!
|
||||
|
||||
You can contribute bugfixes and new features by sending pull requests through GitHub.
|
||||
|
||||
## Legal
|
||||
|
||||
In order for your contribution to be accepted, it must comply with the Eclipse Foundation IP policy.
|
||||
|
||||
Please read the [Eclipse Foundation policy on accepting contributions via Git](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git).
|
||||
|
||||
1. Sign the [Eclipse ECA](http://www.eclipse.org/legal/ECA.php)
|
||||
1. Register for an Eclipse Foundation User ID. You can register [here](https://dev.eclipse.org/site_login/createaccount.php).
|
||||
2. Log into the [Eclipse projects forge](https://www.eclipse.org/contribute/cla), and click on 'Eclipse Contributor Agreement'.
|
||||
2. Go to your [account settings](https://dev.eclipse.org/site_login/myaccount.php#open_tab_accountsettings) and add your GitHub username to your account.
|
||||
3. Make sure that you _sign-off_ your Git commits in the following format:
|
||||
``` Signed-off-by: Alex Smith <alexsmith@nowhere.com> ``` This is usually at the bottom of the commit message. You can automate this by adding the '-s' flag when you make the commits. e.g. ```git commit -s -m "Adding a cool feature"```
|
||||
4. Ensure that the email address that you make your commits with is the same one you used to sign up to the Eclipse Foundation website with.
|
||||
|
||||
## Contributing a change
|
||||
|
||||
1. [Fork the repository on GitHub](https://github.com/eclipse/paho.mqtt.c/fork)
|
||||
2. Clone the forked repository onto your computer: ``` git clone https://github.com/<your username>/paho.mqtt.c.git ```
|
||||
3. Create a new branch from the latest ```develop``` branch with ```git checkout -b YOUR_BRANCH_NAME origin/develop```
|
||||
4. Make your changes
|
||||
5. If developing a new feature, make sure to include JUnit tests.
|
||||
6. Ensure that all new and existing tests pass.
|
||||
7. Commit the changes into the branch: ``` git commit -s ``` Make sure that your commit message is meaningful and describes your changes correctly.
|
||||
8. If you have a lot of commits for the change, squash them into a single / few commits.
|
||||
9. Push the changes in your branch to your forked repository.
|
||||
10. Finally, go to [https://github.com/eclipse/paho.mqtt.c](https://github.com/eclipse/paho.mqtt.c) and create a pull request from your "YOUR_BRANCH_NAME" branch to the ```develop``` one to request review and merge of the commits in your pushed branch.
|
||||
|
||||
What happens next depends on the content of the patch.
|
||||
If it is 100% authored by the contributor with less than 1000 lines of new product code that meets
|
||||
the needs of the project (refactored, test code and sample code is excluded from the count), then it
|
||||
can be pulled into the main repository. When there are more than 1000 lines of new product code,
|
||||
more steps are required. More details are provided in the [handbook](https://www.eclipse.org/projects/handbook/#ip).
|
||||
|
||||
## Developer resources:
|
||||
|
||||
|
||||
Information regarding source code management, builds, coding standards, and more.
|
||||
|
||||
- [https://projects.eclipse.org/projects/iot.paho/developer](https://projects.eclipse.org/projects/iot.paho/developer)
|
||||
|
||||
Contact:
|
||||
--------
|
||||
|
||||
Contact the project developers via the project's development
|
||||
[mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev).
|
||||
|
||||
Search for bugs:
|
||||
----------------
|
||||
|
||||
This project uses GitHub Issues here: [github.com/eclipse/paho.mqtt.c/issues](https://github.com/eclipse/paho.mqtt.c/issues) to track ongoing development and issues.
|
||||
|
||||
Create a new bug:
|
||||
-----------------
|
||||
|
||||
Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
|
||||
|
||||
- [Create new Paho bug](https://github.com/eclipse/paho.mqtt.c/issues/new)
|
||||
13
3rd/paho.mqtt.c/LICENSE
Normal file
13
3rd/paho.mqtt.c/LICENSE
Normal file
@@ -0,0 +1,13 @@
|
||||
Eclipse Public License - v 2.0
|
||||
|
||||
This program and the accompanying materials
|
||||
are made available under the terms of the Eclipse Public License v2.0
|
||||
and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
|
||||
The Eclipse Public License is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/
|
||||
and the Eclipse Distribution License is available at
|
||||
http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
|
||||
For an explanation of what dual-licensing means to you, see:
|
||||
https://www.eclipse.org/legal/eplfaq.php#DUALLIC
|
||||
373
3rd/paho.mqtt.c/Makefile
Normal file
373
3rd/paho.mqtt.c/Makefile
Normal file
@@ -0,0 +1,373 @@
|
||||
#*******************************************************************************
|
||||
# Copyright (c) 2009, 2021 IBM Corp.
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v2.0
|
||||
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
#
|
||||
# The Eclipse Public License is available at
|
||||
# https://www.eclipse.org/legal/epl-2.0/
|
||||
# and the Eclipse Distribution License is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# Contributors:
|
||||
# Ian Craggs - initial API and implementation and/or initial documentation
|
||||
# Allan Stockdill-Mander - SSL updates
|
||||
# Andy Piper - various fixes
|
||||
# Ian Craggs - OSX build
|
||||
# Rainer Poisel - support for multi-core builds and cross-compilation
|
||||
#*******************************************************************************/
|
||||
|
||||
# Note: on OS X you should install XCode and the associated command-line tools
|
||||
|
||||
SHELL = /bin/sh
|
||||
.PHONY: clean mkdir install install-strip uninstall html strip-options
|
||||
|
||||
MAJOR_VERSION := $(shell cat version.major)
|
||||
MINOR_VERSION := $(shell cat version.minor)
|
||||
PATCH_VERSION := $(shell cat version.patch)
|
||||
|
||||
ifndef release.version
|
||||
release.version = $(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)
|
||||
endif
|
||||
|
||||
# determine current platform
|
||||
BUILD_TYPE ?= debug
|
||||
ifeq ($(OS),Windows_NT)
|
||||
OSTYPE ?= $(OS)
|
||||
MACHINETYPE ?= $(PROCESSOR_ARCHITECTURE)
|
||||
else
|
||||
OSTYPE ?= $(shell uname -s)
|
||||
MACHINETYPE ?= $(shell uname -m)
|
||||
build.level = $(shell date)
|
||||
endif # OS
|
||||
ifeq ($(OSTYPE),linux)
|
||||
OSTYPE = Linux
|
||||
endif
|
||||
|
||||
# assume this is normally run in the main Paho directory
|
||||
ifndef srcdir
|
||||
srcdir = src
|
||||
endif
|
||||
|
||||
ifndef blddir
|
||||
blddir = build/output
|
||||
endif
|
||||
|
||||
ifndef blddir_work
|
||||
blddir_work = build
|
||||
endif
|
||||
|
||||
ifndef docdir
|
||||
docdir = $(blddir)/doc
|
||||
endif
|
||||
|
||||
ifndef docdir_work
|
||||
docdir_work = $(blddir)/../doc
|
||||
endif
|
||||
|
||||
ifndef prefix
|
||||
prefix = /usr/local
|
||||
endif
|
||||
|
||||
ifndef exec_prefix
|
||||
exec_prefix = ${prefix}
|
||||
endif
|
||||
|
||||
bindir = $(exec_prefix)/bin
|
||||
includedir = $(prefix)/include
|
||||
libdir = $(exec_prefix)/lib
|
||||
datarootdir = $(prefix)/share
|
||||
mandir = $(datarootdir)/man
|
||||
man1dir = $(mandir)/man1
|
||||
man2dir = $(mandir)/man2
|
||||
man3dir = $(mandir)/man3
|
||||
|
||||
SOURCE_FILES = $(wildcard $(srcdir)/*.c)
|
||||
SOURCE_FILES_C = $(filter-out $(srcdir)/MQTTAsync.c $(srcdir)/MQTTAsyncUtils.c $(srcdir)/MQTTVersion.c $(srcdir)/SSLSocket.c, $(SOURCE_FILES))
|
||||
SOURCE_FILES_CS = $(filter-out $(srcdir)/MQTTAsync.c $(srcdir)/MQTTAsyncUtils.c $(srcdir)/MQTTVersion.c, $(SOURCE_FILES))
|
||||
SOURCE_FILES_A = $(filter-out $(srcdir)/MQTTClient.c $(srcdir)/MQTTVersion.c $(srcdir)/SSLSocket.c, $(SOURCE_FILES))
|
||||
SOURCE_FILES_AS = $(filter-out $(srcdir)/MQTTClient.c $(srcdir)/MQTTVersion.c, $(SOURCE_FILES))
|
||||
|
||||
HEADERS = $(srcdir)/*.h
|
||||
HEADERS_C = $(filter-out $(srcdir)/MQTTAsync.h, $(HEADERS))
|
||||
HEADERS_A = $(HEADERS)
|
||||
|
||||
SAMPLE_FILES_C = MQTTClient_publish MQTTClient_publish_async MQTTClient_subscribe
|
||||
SYNC_SAMPLES = ${addprefix ${blddir}/samples/,${SAMPLE_FILES_C}}
|
||||
|
||||
UTIL_FILES_CS = paho_cs_pub paho_cs_sub
|
||||
SYNC_UTILS = ${addprefix ${blddir}/samples/,${UTIL_FILES_CS}}
|
||||
|
||||
SAMPLE_FILES_A = MQTTAsync_subscribe MQTTAsync_publish
|
||||
ASYNC_SAMPLES = ${addprefix ${blddir}/samples/,${SAMPLE_FILES_A}}
|
||||
|
||||
UTIL_FILES_AS = paho_c_pub paho_c_sub
|
||||
ASYNC_UTILS = ${addprefix ${blddir}/samples/,${UTIL_FILES_AS}}
|
||||
|
||||
TEST_FILES_C = test1 test15 test2 sync_client_test test_mqtt4sync test10
|
||||
SYNC_TESTS = ${addprefix ${blddir}/test/,${TEST_FILES_C}}
|
||||
|
||||
TEST_FILES_CS = test3
|
||||
SYNC_SSL_TESTS = ${addprefix ${blddir}/test/,${TEST_FILES_CS}}
|
||||
|
||||
TEST_FILES_A = test4 test45 test6 test9 test95 test_mqtt4async test11
|
||||
ASYNC_TESTS = ${addprefix ${blddir}/test/,${TEST_FILES_A}}
|
||||
|
||||
TEST_FILES_AS = test5
|
||||
ASYNC_SSL_TESTS = ${addprefix ${blddir}/test/,${TEST_FILES_AS}}
|
||||
|
||||
# The names of the four different libraries to be built
|
||||
MQTTLIB_C = paho-mqtt3c
|
||||
MQTTLIB_CS = paho-mqtt3cs
|
||||
MQTTLIB_A = paho-mqtt3a
|
||||
MQTTLIB_AS = paho-mqtt3as
|
||||
|
||||
CC ?= gcc
|
||||
|
||||
ifndef INSTALL
|
||||
INSTALL = install
|
||||
endif
|
||||
INSTALL_PROGRAM = $(INSTALL)
|
||||
INSTALL_DATA = $(INSTALL) -m 644
|
||||
DOXYGEN_COMMAND = doxygen
|
||||
|
||||
VERSION = ${MAJOR_VERSION}.${MINOR_VERSION}
|
||||
|
||||
MQTTLIB_C_NAME = lib${MQTTLIB_C}.so.${VERSION}
|
||||
MQTTLIB_CS_NAME = lib${MQTTLIB_CS}.so.${VERSION}
|
||||
MQTTLIB_A_NAME = lib${MQTTLIB_A}.so.${VERSION}
|
||||
MQTTLIB_AS_NAME = lib${MQTTLIB_AS}.so.${VERSION}
|
||||
MQTTVERSION_NAME = paho_c_version
|
||||
PAHO_C_PUB_NAME = paho_c_pub
|
||||
PAHO_C_SUB_NAME = paho_c_sub
|
||||
PAHO_CS_PUB_NAME = paho_cs_pub
|
||||
PAHO_CS_SUB_NAME = paho_cs_sub
|
||||
|
||||
MQTTLIB_C_TARGET = ${blddir}/${MQTTLIB_C_NAME}
|
||||
MQTTLIB_CS_TARGET = ${blddir}/${MQTTLIB_CS_NAME}
|
||||
MQTTLIB_A_TARGET = ${blddir}/${MQTTLIB_A_NAME}
|
||||
MQTTLIB_AS_TARGET = ${blddir}/${MQTTLIB_AS_NAME}
|
||||
MQTTVERSION_TARGET = ${blddir}/${MQTTVERSION_NAME}
|
||||
PAHO_C_PUB_TARGET = ${blddir}/samples/${PAHO_C_PUB_NAME}
|
||||
PAHO_C_SUB_TARGET = ${blddir}/samples/${PAHO_C_SUB_NAME}
|
||||
PAHO_CS_PUB_TARGET = ${blddir}/samples/${PAHO_CS_PUB_NAME}
|
||||
PAHO_CS_SUB_TARGET = ${blddir}/samples/${PAHO_CS_SUB_NAME}
|
||||
|
||||
#CCFLAGS_SO = -g -fPIC $(CFLAGS) -Os -Wall -fvisibility=hidden -I$(blddir_work)
|
||||
#FLAGS_EXE = $(LDFLAGS) -I ${srcdir} -pthread -L ${blddir}
|
||||
#FLAGS_EXES = $(LDFLAGS) -I ${srcdir} ${START_GROUP} -pthread -lssl -lcrypto ${END_GROUP} -L ${blddir}
|
||||
|
||||
CCFLAGS_SO = -g -fPIC $(CFLAGS) -D_GNU_SOURCE -Os -Wall -fvisibility=hidden -I$(blddir_work) -DPAHO_MQTT_EXPORTS=1
|
||||
FLAGS_EXE = $(LDFLAGS) -I ${srcdir} ${START_GROUP} -pthread ${GAI_LIB} ${END_GROUP} -L ${blddir}
|
||||
FLAGS_EXES = $(LDFLAGS) -I ${srcdir} ${START_GROUP} -pthread ${GAI_LIB} -lssl -lcrypto ${END_GROUP} -L ${blddir}
|
||||
|
||||
LDCONFIG ?= /sbin/ldconfig
|
||||
LDFLAGS_C = $(LDFLAGS) -shared -Wl,-init,$(MQTTCLIENT_INIT) $(START_GROUP) -pthread $(GAI_LIB) $(END_GROUP)
|
||||
LDFLAGS_CS = $(LDFLAGS) -shared $(START_GROUP) -pthread $(GAI_LIB) $(EXTRA_LIB) -lssl -lcrypto $(END_GROUP) -Wl,-init,$(MQTTCLIENT_INIT)
|
||||
LDFLAGS_A = $(LDFLAGS) -shared -Wl,-init,$(MQTTASYNC_INIT) $(START_GROUP) -pthread $(GAI_LIB) $(END_GROUP)
|
||||
LDFLAGS_AS = $(LDFLAGS) -shared $(START_GROUP) -pthread $(GAI_LIB) $(EXTRA_LIB) -lssl -lcrypto $(END_GROUP) -Wl,-init,$(MQTTASYNC_INIT)
|
||||
|
||||
SED_COMMAND = sed \
|
||||
-e "s/@CLIENT_VERSION@/${release.version}/g" \
|
||||
-e "s/@BUILD_TIMESTAMP@/${build.level}/g"
|
||||
|
||||
ifeq ($(OSTYPE),Linux)
|
||||
|
||||
MQTTCLIENT_INIT = MQTTClient_init
|
||||
MQTTASYNC_INIT = MQTTAsync_init
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
|
||||
GAI_LIB = -lanl
|
||||
EXTRA_LIB = -ldl
|
||||
|
||||
LDFLAGS_C += -Wl,-soname,lib$(MQTTLIB_C).so.${MAJOR_VERSION}
|
||||
LDFLAGS_CS += -Wl,-soname,lib$(MQTTLIB_CS).so.${MAJOR_VERSION} -Wl,-no-whole-archive
|
||||
LDFLAGS_A += -Wl,-soname,lib${MQTTLIB_A}.so.${MAJOR_VERSION}
|
||||
LDFLAGS_AS += -Wl,-soname,lib${MQTTLIB_AS}.so.${MAJOR_VERSION} -Wl,-no-whole-archive
|
||||
|
||||
else ifeq ($(OSTYPE),Darwin)
|
||||
|
||||
MQTTCLIENT_INIT = _MQTTClient_init
|
||||
MQTTASYNC_INIT = _MQTTAsync_init
|
||||
START_GROUP =
|
||||
END_GROUP =
|
||||
|
||||
GAI_LIB =
|
||||
EXTRA_LIB = -ldl
|
||||
|
||||
CCFLAGS_SO += -Wno-deprecated-declarations -DOSX -I /usr/local/opt/openssl/include
|
||||
LDFLAGS_C += -Wl,-install_name,lib$(MQTTLIB_C).so.${MAJOR_VERSION}
|
||||
LDFLAGS_CS += -Wl,-install_name,lib$(MQTTLIB_CS).so.${MAJOR_VERSION} -L /usr/local/opt/openssl/lib
|
||||
LDFLAGS_A += -Wl,-install_name,lib${MQTTLIB_A}.so.${MAJOR_VERSION}
|
||||
LDFLAGS_AS += -Wl,-install_name,lib${MQTTLIB_AS}.so.${MAJOR_VERSION} -L /usr/local/opt/openssl/lib
|
||||
FLAGS_EXE += -DOSX
|
||||
FLAGS_EXES += -L /usr/local/opt/openssl/lib
|
||||
|
||||
LDCONFIG = echo
|
||||
|
||||
endif
|
||||
|
||||
all: build
|
||||
|
||||
build: | mkdir ${MQTTLIB_C_TARGET} ${MQTTLIB_CS_TARGET} ${MQTTLIB_A_TARGET} ${MQTTLIB_AS_TARGET} ${MQTTVERSION_TARGET} ${SYNC_SAMPLES} ${SYNC_UTILS} ${ASYNC_SAMPLES} ${ASYNC_UTILS} ${SYNC_TESTS} ${SYNC_SSL_TESTS} ${ASYNC_TESTS} ${ASYNC_SSL_TESTS}
|
||||
|
||||
clean:
|
||||
rm -rf ${blddir}/*
|
||||
rm -rf ${blddir_work}/*
|
||||
|
||||
mkdir:
|
||||
-mkdir -p ${blddir}/samples
|
||||
-mkdir -p ${blddir}/test
|
||||
echo OSTYPE is $(OSTYPE)
|
||||
|
||||
${SYNC_TESTS}: ${blddir}/test/%: ${srcdir}/../test/%.c $(MQTTLIB_C_TARGET)
|
||||
${CC} -DNOSTACKTRACE -DNOLOG_MESSAGES $(srcdir)/Thread.c -g -o $@ $< -l${MQTTLIB_C} ${FLAGS_EXE}
|
||||
|
||||
${SYNC_SSL_TESTS}: ${blddir}/test/%: ${srcdir}/../test/%.c $(MQTTLIB_CS_TARGET)
|
||||
${CC} -g -o $@ $< -l${MQTTLIB_CS} ${FLAGS_EXES}
|
||||
|
||||
${ASYNC_TESTS}: ${blddir}/test/%: ${srcdir}/../test/%.c $(MQTTLIB_A_TARGET)
|
||||
${CC} -g -o $@ $< -l${MQTTLIB_A} ${FLAGS_EXE}
|
||||
|
||||
${ASYNC_SSL_TESTS}: ${blddir}/test/%: ${srcdir}/../test/%.c $(MQTTLIB_AS_TARGET)
|
||||
${CC} -g -o $@ $< -l${MQTTLIB_AS} ${FLAGS_EXES}
|
||||
|
||||
${SYNC_SAMPLES}: ${blddir}/samples/%: ${srcdir}/samples/%.c $(MQTTLIB_CS_TARGET)
|
||||
${CC} -o $@ $< -l${MQTTLIB_CS} ${FLAGS_EXES}
|
||||
|
||||
${SYNC_UTILS}: ${blddir}/samples/%: ${srcdir}/samples/%.c ${srcdir}/samples/pubsub_opts.c $(MQTTLIB_CS_TARGET)
|
||||
${CC} -o $@ $< -l${MQTTLIB_CS} ${FLAGS_EXES} ${srcdir}/samples/pubsub_opts.c
|
||||
|
||||
${ASYNC_SAMPLES}: ${blddir}/samples/%: ${srcdir}/samples/%.c $(MQTTLIB_AS_TARGET)
|
||||
${CC} -o $@ $< -l${MQTTLIB_AS} ${FLAGS_EXES}
|
||||
|
||||
${ASYNC_UTILS}: ${blddir}/samples/%: ${srcdir}/samples/%.c ${srcdir}/samples/pubsub_opts.c $(MQTTLIB_AS_TARGET)
|
||||
${CC} -o $@ $< -l${MQTTLIB_AS} ${FLAGS_EXES} ${srcdir}/samples/pubsub_opts.c
|
||||
|
||||
$(blddir_work)/VersionInfo.h: $(srcdir)/VersionInfo.h.in
|
||||
-mkdir -p $(blddir_work)
|
||||
$(SED_COMMAND) $< > $@
|
||||
|
||||
${MQTTLIB_C_TARGET}: ${SOURCE_FILES_C} ${HEADERS_C} $(blddir_work)/VersionInfo.h
|
||||
${CC} ${CCFLAGS_SO} -o $@ ${SOURCE_FILES_C} ${LDFLAGS_C}
|
||||
-ln -s lib$(MQTTLIB_C).so.${VERSION} ${blddir}/lib$(MQTTLIB_C).so.${MAJOR_VERSION}
|
||||
-ln -s lib$(MQTTLIB_C).so.${MAJOR_VERSION} ${blddir}/lib$(MQTTLIB_C).so
|
||||
|
||||
${MQTTLIB_CS_TARGET}: ${SOURCE_FILES_CS} ${HEADERS_C} $(blddir_work)/VersionInfo.h
|
||||
${CC} ${CCFLAGS_SO} -o $@ ${SOURCE_FILES_CS} -DOPENSSL ${LDFLAGS_CS}
|
||||
-ln -s lib$(MQTTLIB_CS).so.${VERSION} ${blddir}/lib$(MQTTLIB_CS).so.${MAJOR_VERSION}
|
||||
-ln -s lib$(MQTTLIB_CS).so.${MAJOR_VERSION} ${blddir}/lib$(MQTTLIB_CS).so
|
||||
|
||||
${MQTTLIB_A_TARGET}: ${SOURCE_FILES_A} ${HEADERS_A} $(blddir_work)/VersionInfo.h
|
||||
${CC} ${CCFLAGS_SO} -o $@ ${SOURCE_FILES_A} ${LDFLAGS_A}
|
||||
-ln -s lib$(MQTTLIB_A).so.${VERSION} ${blddir}/lib$(MQTTLIB_A).so.${MAJOR_VERSION}
|
||||
-ln -s lib$(MQTTLIB_A).so.${MAJOR_VERSION} ${blddir}/lib$(MQTTLIB_A).so
|
||||
|
||||
${MQTTLIB_AS_TARGET}: ${SOURCE_FILES_AS} ${HEADERS_A} $(blddir_work)/VersionInfo.h
|
||||
${CC} ${CCFLAGS_SO} -o $@ ${SOURCE_FILES_AS} -DOPENSSL ${LDFLAGS_AS}
|
||||
-ln -s lib$(MQTTLIB_AS).so.${VERSION} ${blddir}/lib$(MQTTLIB_AS).so.${MAJOR_VERSION}
|
||||
-ln -s lib$(MQTTLIB_AS).so.${MAJOR_VERSION} ${blddir}/lib$(MQTTLIB_AS).so
|
||||
|
||||
${MQTTVERSION_TARGET}: $(srcdir)/MQTTVersion.c $(srcdir)/MQTTAsync.h $(MQTTLIB_A_TARGET)
|
||||
${CC} ${FLAGS_EXE} -o $@ -l${MQTTLIB_A} $(srcdir)/MQTTVersion.c -ldl
|
||||
|
||||
strip_options:
|
||||
$(eval INSTALL_OPTS := -s)
|
||||
|
||||
install-strip: build strip_options install
|
||||
|
||||
install: build
|
||||
mkdir -p $(DESTDIR)$(PREFIX)${includedir}
|
||||
$(INSTALL_DATA) ${INSTALL_OPTS} ${MQTTLIB_C_TARGET} $(DESTDIR)${libdir}
|
||||
$(INSTALL_DATA) ${INSTALL_OPTS} ${MQTTLIB_CS_TARGET} $(DESTDIR)${libdir}
|
||||
$(INSTALL_DATA) ${INSTALL_OPTS} ${MQTTLIB_A_TARGET} $(DESTDIR)${libdir}
|
||||
$(INSTALL_DATA) ${INSTALL_OPTS} ${MQTTLIB_AS_TARGET} $(DESTDIR)${libdir}
|
||||
$(INSTALL_PROGRAM) ${INSTALL_OPTS} ${MQTTVERSION_TARGET} $(DESTDIR)${bindir}
|
||||
$(INSTALL_PROGRAM) ${INSTALL_OPTS} ${PAHO_C_PUB_TARGET} $(DESTDIR)${bindir}
|
||||
$(INSTALL_PROGRAM) ${INSTALL_OPTS} ${PAHO_C_SUB_TARGET} $(DESTDIR)${bindir}
|
||||
$(INSTALL_PROGRAM) ${INSTALL_OPTS} ${PAHO_CS_PUB_TARGET} $(DESTDIR)${bindir}
|
||||
$(INSTALL_PROGRAM) ${INSTALL_OPTS} ${PAHO_CS_SUB_TARGET} $(DESTDIR)${bindir}
|
||||
$(LDCONFIG) $(DESTDIR)${libdir}
|
||||
ln -s lib$(MQTTLIB_C).so.${MAJOR_VERSION} $(DESTDIR)${libdir}/lib$(MQTTLIB_C).so
|
||||
ln -s lib$(MQTTLIB_CS).so.${MAJOR_VERSION} $(DESTDIR)${libdir}/lib$(MQTTLIB_CS).so
|
||||
ln -s lib$(MQTTLIB_A).so.${MAJOR_VERSION} $(DESTDIR)${libdir}/lib$(MQTTLIB_A).so
|
||||
ln -s lib$(MQTTLIB_AS).so.${MAJOR_VERSION} $(DESTDIR)${libdir}/lib$(MQTTLIB_AS).so
|
||||
@if test ! -f $(DESTDIR)${libdir}/lib$(MQTTLIB_C).so.${MAJOR_VERSION}; then ln -s lib$(MQTTLIB_C).so.${VERSION} $(DESTDIR)${libdir}/lib$(MQTTLIB_C).so.${MAJOR_VERSION}; fi
|
||||
@if test ! -f $(DESTDIR)${libdir}/lib$(MQTTLIB_CS).so.${MAJOR_VERSION}; then ln -s lib$(MQTTLIB_CS).so.${VERSION} $(DESTDIR)${libdir}/lib$(MQTTLIB_CS).so.${MAJOR_VERSION}; fi
|
||||
@if test ! -f $(DESTDIR)${libdir}/lib$(MQTTLIB_A).so.${MAJOR_VERSION}; then ln -s lib$(MQTTLIB_A).so.${VERSION} $(DESTDIR)${libdir}/lib$(MQTTLIB_A).so.${MAJOR_VERSION}; fi
|
||||
@if test ! -f $(DESTDIR)${libdir}/lib$(MQTTLIB_AS).so.${MAJOR_VERSION}; then ln -s lib$(MQTTLIB_AS).so.${VERSION} $(DESTDIR)${libdir}/lib$(MQTTLIB_AS).so.${MAJOR_VERSION}; fi
|
||||
$(INSTALL_DATA) ${srcdir}/MQTTAsync.h $(DESTDIR)${includedir}
|
||||
$(INSTALL_DATA) ${srcdir}/MQTTClient.h $(DESTDIR)${includedir}
|
||||
$(INSTALL_DATA) ${srcdir}/MQTTClientPersistence.h $(DESTDIR)${includedir}
|
||||
$(INSTALL_DATA) ${srcdir}/MQTTProperties.h $(DESTDIR)${includedir}
|
||||
$(INSTALL_DATA) ${srcdir}/MQTTReasonCodes.h $(DESTDIR)${includedir}
|
||||
$(INSTALL_DATA) ${srcdir}/MQTTSubscribeOpts.h $(DESTDIR)${includedir}
|
||||
$(INSTALL_DATA) ${srcdir}/MQTTExportDeclarations.h $(DESTDIR)${includedir}
|
||||
- $(INSTALL_DATA) doc/man/man1/paho_c_pub.1 $(DESTDIR)${man1dir}
|
||||
- $(INSTALL_DATA) doc/man/man1/paho_c_sub.1 $(DESTDIR)${man1dir}
|
||||
- $(INSTALL_DATA) doc/man/man1/paho_cs_pub.1 $(DESTDIR)${man1dir}
|
||||
- $(INSTALL_DATA) doc/man/man1/paho_cs_sub.1 $(DESTDIR)${man1dir}
|
||||
|
||||
ifneq ("$(wildcard ${blddir}/doc/MQTTClient/man/man3/MQTTClient.h.3)","")
|
||||
- $(INSTALL_DATA) ${blddir}/doc/MQTTClient/man/man3/MQTTClient.h.3 $(DESTDIR)${man3dir}
|
||||
endif
|
||||
ifneq ("$(wildcard ${blddir}/doc/MQTTAsync/man/man3/MQTTAsync.h.3)","")
|
||||
- $(INSTALL_DATA) ${blddir}/doc/MQTTAsync/man/man3/MQTTAsync.h.3 $(DESTDIR)${man3dir}
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
- rm $(DESTDIR)${libdir}/${MQTTLIB_C_NAME}
|
||||
- rm $(DESTDIR)${libdir}/${MQTTLIB_CS_NAME}
|
||||
- rm $(DESTDIR)${libdir}/${MQTTLIB_A_NAME}
|
||||
- rm $(DESTDIR)${libdir}/${MQTTLIB_AS_NAME}
|
||||
- rm $(DESTDIR)${bindir}/${MQTTVERSION_NAME}
|
||||
- rm $(DESTDIR)${bindir}/${PAHO_C_PUB_NAME}
|
||||
- rm $(DESTDIR)${bindir}/${PAHO_C_SUB_NAME}
|
||||
- rm $(DESTDIR)${bindir}/${PAHO_CS_PUB_NAME}
|
||||
- rm $(DESTDIR)${bindir}/${PAHO_CS_SUB_NAME}
|
||||
$(LDCONFIG) $(DESTDIR)${libdir}
|
||||
- rm $(DESTDIR)${libdir}/lib$(MQTTLIB_C).so
|
||||
- rm $(DESTDIR)${libdir}/lib$(MQTTLIB_CS).so
|
||||
- rm $(DESTDIR)${libdir}/lib$(MQTTLIB_A).so
|
||||
- rm $(DESTDIR)${libdir}/lib$(MQTTLIB_AS).so
|
||||
- rm $(DESTDIR)${libdir}/lib$(MQTTLIB_C).so.${MAJOR_VERSION}
|
||||
- rm $(DESTDIR)${libdir}/lib$(MQTTLIB_CS).so.${MAJOR_VERSION}
|
||||
- rm $(DESTDIR)${libdir}/lib$(MQTTLIB_A).so.${MAJOR_VERSION}
|
||||
- rm $(DESTDIR)${libdir}/lib$(MQTTLIB_AS).so.${MAJOR_VERSION}
|
||||
- rm $(DESTDIR)${includedir}/MQTTAsync.h
|
||||
- rm $(DESTDIR)${includedir}/MQTTClient.h
|
||||
- rm $(DESTDIR)${includedir}/MQTTClientPersistence.h
|
||||
- rm $(DESTDIR)${includedir}/MQTTProperties.h
|
||||
- rm $(DESTDIR)${includedir}/MQTTReasonCodes.h
|
||||
- rm $(DESTDIR)${includedir}/MQTTSubscribeOpts.h
|
||||
- rm $(DESTDIR)${includedir}/MQTTExportDeclarations.h
|
||||
|
||||
- rm $(DESTDIR)${man1dir}/paho_c_pub.1
|
||||
- rm $(DESTDIR)${man1dir}/paho_c_sub.1
|
||||
- rm $(DESTDIR)${man1dir}/paho_cs_pub.1
|
||||
- rm $(DESTDIR)${man1dir}/paho_cs_sub.1
|
||||
|
||||
ifneq ("$(wildcard $(DESTDIR)${man3dir}/MQTTClient.h.3)","")
|
||||
- rm $(DESTDIR)${man3dir}/MQTTClient.h.3
|
||||
endif
|
||||
ifneq ("$(wildcard $(DESTDIR)${man3dir}/MQTTAsync.h.3)","")
|
||||
- rm $(DESTDIR)${man3dir}/MQTTAsync.h.3
|
||||
endif
|
||||
|
||||
REGEX_DOXYGEN := \
|
||||
's;@PROJECT_SOURCE_DIR@/src/\?;;' \
|
||||
's;@PROJECT_SOURCE_DIR@;..;' \
|
||||
's;@CMAKE_CURRENT_BINARY_DIR@;../build/output;'
|
||||
SED_DOXYGEN := $(foreach sed_exp,$(REGEX_DOXYGEN),-e $(sed_exp))
|
||||
define process_doxygen
|
||||
cd ${srcdir}; sed $(SED_DOXYGEN) ../doc/${1}.in > ../$(docdir_work)/${1}
|
||||
cd ${srcdir}; $(DOXYGEN_COMMAND) ../$(docdir_work)/${1}
|
||||
endef
|
||||
html:
|
||||
-mkdir -p $(docdir_work)
|
||||
-mkdir -p ${docdir}
|
||||
$(call process_doxygen,DoxyfileV3ClientAPI)
|
||||
$(call process_doxygen,DoxyfileV3AsyncAPI)
|
||||
$(call process_doxygen,DoxyfileV3ClientInternal)
|
||||
55
3rd/paho.mqtt.c/NOTICE
Normal file
55
3rd/paho.mqtt.c/NOTICE
Normal file
@@ -0,0 +1,55 @@
|
||||
# Notices for Eclipse Paho
|
||||
|
||||
This content is produced and maintained by the Eclipse Paho project.
|
||||
|
||||
* Project home: https://projects.eclipse.org/projects/iot.paho
|
||||
|
||||
## Trademarks
|
||||
|
||||
Paho™ is a trademark of the Eclipse Foundation.
|
||||
|
||||
## Copyright
|
||||
|
||||
All content is the property of the respective authors or their employers. For
|
||||
more information regarding authorship of content, please consult the listed
|
||||
source code repository logs.
|
||||
|
||||
## Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License v2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
v1.0 which is available at https://www.eclipse.org/org/documents/edl-v10.php.
|
||||
|
||||
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
|
||||
## Source Code
|
||||
|
||||
The project maintains the following source code repositories:
|
||||
|
||||
* https://github.com/eclipse/paho-website
|
||||
* https://github.com/eclipse/paho.golang
|
||||
* https://github.com/eclipse/paho.mqtt-sn.embedded-c
|
||||
* https://github.com/eclipse/paho.mqtt-spy
|
||||
* https://github.com/eclipse/paho.mqtt.android
|
||||
* https://github.com/eclipse/paho.mqtt.c
|
||||
* https://github.com/eclipse/paho.mqtt.cpp
|
||||
* https://github.com/eclipse/paho.mqtt.d
|
||||
* https://github.com/eclipse/paho.mqtt.embedded-c
|
||||
* https://github.com/eclipse/paho.mqtt.golang
|
||||
* https://github.com/eclipse/paho.mqtt.java
|
||||
* https://github.com/eclipse/paho.mqtt.javascript
|
||||
* https://github.com/eclipse/paho.mqtt.m2mqtt
|
||||
* https://github.com/eclipse/paho.mqtt.python
|
||||
* https://github.com/eclipse/paho.mqtt.ruby
|
||||
* https://github.com/eclipse/paho.mqtt.rust
|
||||
* https://github.com/eclipse/paho.mqtt.testing
|
||||
|
||||
## Cryptography
|
||||
|
||||
Content may contain encryption software. The country in which you are currently
|
||||
may have restrictions on the import, possession, and use, and/or re-export to
|
||||
another country, of encryption software. BEFORE using any encryption software,
|
||||
please check the country's laws, regulations and policies concerning the import,
|
||||
possession, or use, and re-export of encryption software, to see if this is
|
||||
permitted.
|
||||
13
3rd/paho.mqtt.c/PULL_REQUEST_TEMPLATE.md
Normal file
13
3rd/paho.mqtt.c/PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
Thank you for your interest in this project managed by the Eclipse Foundation.
|
||||
|
||||
The guidelines for contributions can be found in the CONTRIBUTING.md file.
|
||||
|
||||
At a minimum, you must sign the Eclipse ECA, and sign off each commit.
|
||||
|
||||
To complete and submit a ECA, log into the Eclipse projects forge
|
||||
You will need to create an account with the Eclipse Foundation if you have not already done so.
|
||||
Be sure to use the same email address when you register for the account that you intend to use when you commit to Git.
|
||||
Go to https://accounts.eclipse.org/user/eca to sign the Eclipse ECA.
|
||||
|
||||
|
||||
358
3rd/paho.mqtt.c/README.md
Normal file
358
3rd/paho.mqtt.c/README.md
Normal file
@@ -0,0 +1,358 @@
|
||||
[](https://travis-ci.org/eclipse/paho.mqtt.c)
|
||||
[](https://lgtm.com/projects/g/eclipse/paho.mqtt.c/alerts/)
|
||||
[](https://scan.coverity.com/projects/paho-c)
|
||||
|
||||
# Eclipse Paho C Client Library for the MQTT Protocol
|
||||
|
||||
This repository contains the source code for the [Eclipse Paho](http://eclipse.org/paho) MQTT C client library.
|
||||
|
||||
This code builds libraries which enable applications to connect to an [MQTT](http://mqtt.org) broker to publish messages, and to subscribe to topics and receive published messages.
|
||||
|
||||
Synchronous and various asynchronous programming models are supported.
|
||||
|
||||
## Information About MQTT
|
||||
|
||||
* [MQTT website](http://mqtt.org)
|
||||
* [The MQTT 3.1.1 standard](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html)
|
||||
* [The MQTT 5.0 standard](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html)
|
||||
* [HiveMQ introduction to MQTT](https://www.hivemq.com/mqtt/)
|
||||
* [OASIS Introduction to MQTT presentation](https://www.oasis-open.org/committees/download.php/49205/MQTT-OASIS-Webinar.pdf)
|
||||
|
||||
## Libraries
|
||||
|
||||
The Paho C client comprises four variant libraries, shared or static:
|
||||
|
||||
* paho-mqtt3a - asynchronous (MQTTAsync)
|
||||
* paho-mqtt3as - asynchronous with SSL/TLS (MQTTAsync)
|
||||
* paho-mqtt3c - "classic" / synchronous (MQTTClient)
|
||||
* paho-mqtt3cs - "classic" / synchronous with SSL/TLS (MQTTClient)
|
||||
|
||||
[Which Paho C API to use, with some history, for context](https://modelbasedtesting.co.uk/2013/10/13/which-paho-mqtt-c-api-to-use-and-some-history/)
|
||||
|
||||
## Usage and API
|
||||
|
||||
Detailed API documentation [is available online](https://eclipse-paho.github.io/paho.mqtt.c/MQTTClient/html/). It is also available by building the Doxygen docs in the ``doc`` directory.
|
||||
|
||||
Samples are available in the Doxygen docs and also in `src/samples` for reference. These are:
|
||||
|
||||
- *paho_c_pub.c* and *paho_c_sub.c:* command line utilities to publish and subscribe, -h will give help
|
||||
- *paho_cs_pub.c* and *paho_cs_sub.c:* command line utilities using MQTTClient to publish and subscribe
|
||||
- *MQTTClient_publish.c, MQTTClient_subscribe.c* and *MQTTClient_publish_async.c:* MQTTClient simple code examples
|
||||
- *MQTTAsync_publish.c* and *MQTTAsync_subscribe.c:* MQTTAsync simple code examples
|
||||
|
||||
Some potentially useful blog posts:
|
||||
|
||||
- [Paho client MQTT 5.0 support and command line utilities](https://modelbasedtesting.co.uk/2018/08/08/paho-c-client-mqtt-5-0-and-command-line-utilities/)
|
||||
- [MQTT, QoS and persistence](https://modelbasedtesting.co.uk/2013/11/24/mqtt-qos-and-persistence/)
|
||||
- [A story of MQTT 5.0](https://modelbasedtesting.co.uk/2018/04/09/a-story-of-mqtt-5-0/)
|
||||
|
||||
[Various MQTT and MQTT-SN talks I've given.](https://modelbasedtesting.co.uk/talks-ive-given/)
|
||||
|
||||
### Supported Network Protocols
|
||||
|
||||
The library supports connecting to an MQTT server using TCP, SSL/TLS, Unix-domain sockets, and websockets (secure and insecure). This is chosen by the client using the URI supplied in the connect options. It can be specified as:
|
||||
|
||||
"mqtt://<host>:<port>" - TCP, unsecure
|
||||
"tcp://<host>:<port>" (same)
|
||||
|
||||
"mqtts://<host>:<port>" - SSL/TLS
|
||||
"ssl://<host>:<port>" (same)
|
||||
|
||||
"unix:///path/to/socket - UNIX-domain socket (*nix systems only)
|
||||
|
||||
"ws://<host>:<port>[/path]" - Websockets, unsecure
|
||||
"wss://<host>:<port>[/path]" - Websockets, secure
|
||||
|
||||
The "mqtt://" and "tcp://" schemas are identical. They indicate an insecure connection over TCP. The "mqtt://" variation is new for the library, but becoming more common across different MQTT libraries.
|
||||
|
||||
Similarly, the "mqtts://" and "ssl://" schemas are identical. They specify a secure connection over SSL/TLS sockets. The use any of the secure connect options requires that you compile the library with the `PAHO_WITH_SSL=TRUE` CMake option to include OpenSSL. In addition, you _must_ specify `ssl_options` when you connect to the broker - i.e. you must add an instance of `ssl_options` to the `connect_options` when calling `connect()`.
|
||||
|
||||
The use of Unix-domain sockets requires the build option of `PAHO_WITH_UNIX_SOCKETS=TRUE` is required. This is only available on *nix-style systems like Linux and macOS. It is not vailable on Windows.
|
||||
|
||||
## Runtime tracing
|
||||
|
||||
A number of environment variables control runtime tracing of the C library.
|
||||
|
||||
Tracing is switched on using `MQTT_C_CLIENT_TRACE` (a value of ON traces to stdout, any other value should specify a file to trace to).
|
||||
|
||||
The verbosity of the output is controlled using the `MQTT_C_CLIENT_TRACE_LEVEL` environment variable - valid values are ERROR, PROTOCOL, MINIMUM, MEDIUM and MAXIMUM (from least to most verbose).
|
||||
|
||||
The variable `MQTT_C_CLIENT_TRACE_MAX_LINES` limits the number of lines of trace that are output.
|
||||
|
||||
```
|
||||
export MQTT_C_CLIENT_TRACE=ON
|
||||
export MQTT_C_CLIENT_TRACE_LEVEL=PROTOCOL
|
||||
```
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
Please open issues in the Github project: https://github.com/eclipse-paho/paho.mqtt.c/issues.
|
||||
|
||||
## More information
|
||||
|
||||
Discussion of the Paho clients takes place on the [Eclipse paho-dev mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev).
|
||||
|
||||
## Follow Eclipse Paho on Twitter: [@eclipsepaho](https://twitter.com/eclipsepaho)
|
||||
|
||||
General questions about the MQTT protocol are discussed in the [MQTT Google Group](https://groups.google.com/forum/?hl=en-US&fromgroups#!forum/mqtt).
|
||||
|
||||
There is more information available via the [MQTT community site](http://mqtt.org).
|
||||
|
||||
|
||||
## Building with CMake
|
||||
|
||||
The build process currently supports a number of Linux "flavors" including ARM and s390, OS X, AIX and Solaris as well as the Windows operating system. The build process requires the following tools:
|
||||
* [CMake](http://cmake.org)
|
||||
* [GNU Make](https://www.gnu.org/software/make/) or [Ninja](https://martine.github.io/ninja/)
|
||||
* A conforming C compiler, such as [gcc](https://gcc.gnu.org/), [Clang](https://clang.llvm.org/), etc
|
||||
|
||||
On Debian based systems this would mean that the following packages have to be installed:
|
||||
|
||||
```
|
||||
$ apt-get install build-essential gcc make cmake cmake-gui cmake-curses-gui
|
||||
```
|
||||
|
||||
Also, in order to build a debian package from the source code, the following packages have to be installed
|
||||
|
||||
```
|
||||
$ apt-get install fakeroot devscripts dh-make lsb-release
|
||||
```
|
||||
|
||||
Ninja can be downloaded from its github project page in the "releases" section. Optionally it is possible to build binaries with SSL/TLS support. This requires the OpenSSL libraries and includes to be available. E. g. on Debian:
|
||||
|
||||
```
|
||||
$ apt-get install libssl-dev
|
||||
```
|
||||
|
||||
The documentation requires doxygen and optionally graphviz:
|
||||
|
||||
```
|
||||
$ apt-get install doxygen graphviz
|
||||
```
|
||||
|
||||
### Building your application with CMake
|
||||
|
||||
If the Paho C library was built with CMake and is already installed on the system, it is relatively easy to set up a CMake build for your application. (If it's not already built and installed read the next section).
|
||||
|
||||
The library can be built with several options which create variations of the library for asynchronous or synchronous use; encryption (SSL/TLS) support or not; and whether the library is shared or static. CMake exports all of the libraries that were built as targets, and the user can chose which is best suited for an application.
|
||||
|
||||
The package is named: **eclipse-paho-mqtt-c**
|
||||
|
||||
The namespace for all the targets is also: **eclipse-paho-mqtt-c**
|
||||
|
||||
The target names are the same as the library names. The static libraries append *-static* to the target name even for platforms that use the same base name for shared and static libraries. So:
|
||||
|
||||
Target|Description
|
||||
------|-----------
|
||||
paho-mqtt3a | asynchronous, no encryption
|
||||
paho-mqtt3as | asynchronous with SSL/TLS support
|
||||
paho-mqtt3c | synchronous, no encryption
|
||||
paho-mqtt3cs | synchronous with SSL/TLS support
|
||||
paho-mqtt3a-static | asynchronous, no encryption, static linkage
|
||||
paho-mqtt3as-static | asynchronous with SSL/TLS support, static linkage
|
||||
paho-mqtt3c-static | synchronous, no encryption, static linkage
|
||||
paho-mqtt3cs-static | synchronous with SSL/TLS support, static linkage
|
||||
|
||||
Remember, though, that not all of these targets may be available. It depends on how the library was built.
|
||||
|
||||
A sample *CMakeLists.txt* for an application that uses the asynchronous library with encryption support *(paho-mqtt3as)* might look like this:
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(MyMQTTApp VERSION 1.0.0 LANGUAGES C)
|
||||
|
||||
find_package(eclipse-paho-mqtt-c REQUIRED)
|
||||
|
||||
add_executable(MyMQTTApp MyMQTTApp.c)
|
||||
target_link_libraries(MQTTVersion eclipse-paho-mqtt-c::paho-mqtt3as)
|
||||
```
|
||||
|
||||
If the library was installed to a non-traditional location, you may need to tell CMake where to find it using `CMAKE_PREFIX_PATH`. For example, if you installed it in */opt/mqtt/paho.mqtt.c*
|
||||
|
||||
```
|
||||
$ cmake -DCMAKE_PREFIX_PATH=/opt/mqtt/paho.mqtt.c ..
|
||||
```
|
||||
|
||||
### Building the Paho C library with CMake
|
||||
|
||||
Before compiling, determine the value of some variables in order to configure features, library locations, and other options:
|
||||
|
||||
Variable | Default Value | Description
|
||||
------------ | ------------- | -------------
|
||||
PAHO_BUILD_SHARED | TRUE | Build a shared version of the libraries
|
||||
PAHO_BUILD_STATIC | FALSE | Build a static version of the libraries
|
||||
PAHO_HIGH_PERFORMANCE | FALSE | When set to true, the debugging aids internal tracing and heap tracking are not included.
|
||||
PAHO_WITH_SSL | FALSE | Flag that defines whether to build ssl-enabled binaries too.
|
||||
OPENSSL_ROOT_DIR | "" (system default) | Directory containing your OpenSSL installation (i.e. `/usr/local` when headers are in `/usr/local/include` and libraries are in `/usr/local/lib`)
|
||||
PAHO_WITH_LIBRESSL | FALSE | Flag that defines whether to build ssl-enabled binaries with LibreSSL instead of OpenSSL.
|
||||
LIBRESSL_ROOT_DIR | "" (system default) | Directory containing your LibreSSL installation (i.e. `/usr/local` when headers are in `/usr/local/include` and libraries are in `/usr/local/lib`)
|
||||
PAHO_WITH_UNIX_SOCKETS | FALSE | (*nix systems only) Flag to enable support for UNIX-domain sockets
|
||||
PAHO_BUILD_DOCUMENTATION | FALSE | Create and install the HTML based API documentation (requires Doxygen)
|
||||
PAHO_BUILD_SAMPLES | FALSE | Build sample programs
|
||||
PAHO_ENABLE_TESTING | TRUE | Build test and run
|
||||
MQTT_TEST_BROKER | tcp://localhost:1883 | MQTT connection URL for a broker to use during test execution
|
||||
MQTT_TEST_PROXY | tcp://localhost:1883 | Hostname of the test proxy to use
|
||||
MQTT_SSL_HOSTNAME | localhost | Hostname of a test SSL MQTT broker to use
|
||||
PAHO_BUILD_DEB_PACKAGE | FALSE | Build debian package
|
||||
|
||||
Using these variables CMake can be used to generate your Ninja or Make files. Using CMake, building out-of-source is the default. Therefore it is recommended to invoke all build commands inside your chosen build directory but outside of the source tree.
|
||||
|
||||
An example build session targeting the build platform could look like this:
|
||||
|
||||
```
|
||||
$ mkdir /tmp/build.paho ; cd /tmp/build.paho
|
||||
$ cmake -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_DOCUMENTATION=TRUE \
|
||||
-DPAHO_BUILD_SAMPLES=TRUE ~/paho.mqtt.c
|
||||
```
|
||||
|
||||
Invoking cmake and specifying build options can also be performed using cmake-gui or ccmake (see https://cmake.org/runningcmake/). For example:
|
||||
|
||||
```
|
||||
$ ccmake ~/paho.mqtt.c
|
||||
```
|
||||
|
||||
To compile/link the binaries, to install, or to generate packages, use these commands:
|
||||
|
||||
```
|
||||
$ cmake --build .
|
||||
|
||||
$ cmake --build . --target install
|
||||
|
||||
$ cmake --build . --target package
|
||||
```
|
||||
|
||||
To build, install, or generate packages, you can also use the generated builder like _ninja_ or _make_ directly after invoking the initial CMake configuration step, such as `ninja package` or `make -j <number-of-jpbs> package`.
|
||||
|
||||
### Debug builds
|
||||
|
||||
Debug builds can be performed by defining the value of the `CMAKE_BUILD_TYPE` option to `Debug`. For example:
|
||||
|
||||
```
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Debug ~/paho.mqtt.c
|
||||
```
|
||||
|
||||
### Running the tests
|
||||
|
||||
Test code is available in the `test` directory. The tests can be built and executed with the CMake build system. The test execution requires a MQTT broker running. By default, the build system uses `localhost`, however it is possible to configure the build to use an external broker. These parameters are documented in the Build Requirements section above.
|
||||
|
||||
After ensuring a MQTT broker is available, it is possible to execute the tests by starting the proxy and running `ctest` as described below:
|
||||
|
||||
```
|
||||
$ python ../test/mqttsas.py &
|
||||
$ ctest -VV
|
||||
```
|
||||
|
||||
### Cross compilation
|
||||
|
||||
Cross compilation using CMake is performed by using so called "toolchain files" (see: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html).
|
||||
|
||||
The path to the toolchain file can be specified by using CMake's `-DCMAKE_TOOLCHAIN_FILE` option. In case no toolchain file is specified, the build is performed for the native build platform.
|
||||
|
||||
For your convenience toolchain files for the following platforms can be found in the `cmake` directory of Eclipse Paho:
|
||||
* Linux x86
|
||||
* Linux ARM11 (a.k.a. the Raspberry Pi)
|
||||
* Windows x86_64
|
||||
* Windows x86
|
||||
|
||||
The provided toolchain files assume that required compilers/linkers are to be found in the environment, i. e. the PATH-Variable of your user or system. If you prefer, you can also specify the absolute location of your compilers in the toolchain files.
|
||||
|
||||
Example invocation for the Raspberry Pi:
|
||||
|
||||
```
|
||||
$ cmake -GNinja -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_SAMPLES=TRUE \
|
||||
-DPAHO_BUILD_DOCUMENTATION=TRUE \
|
||||
-DOPENSSL_LIB_SEARCH_PATH=/tmp/libssl-dev/usr/lib/arm-linux-gnueabihf \
|
||||
-DOPENSSL_INC_SEARCH_PATH="/tmp/libssl-dev/usr/include/openssl;/tmp/libssl-dev/usr/include/arm-linux-gnueabihf" \
|
||||
-DCMAKE_TOOLCHAIN_FILE=~/paho.mqtt.c/cmake/toolchain.linux-arm11.cmake \
|
||||
~/paho.mqtt.c
|
||||
```
|
||||
|
||||
Compilers for the Raspberry Pi and other ARM targets can be obtained from ARM (https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/downloads)
|
||||
|
||||
This example assumes that OpenSSL-libraries and includes have been installed in the `/tmp/libssl-dev` directory.
|
||||
|
||||
Example invocation for Windows 64 bit:
|
||||
|
||||
```
|
||||
$ cmake -DPAHO_BUILD_SAMPLES=TRUE \
|
||||
-DCMAKE_TOOLCHAIN_FILE=~/paho.mqtt.c/cmake/toolchain.win64.cmake \
|
||||
~/paho.mqtt.c
|
||||
```
|
||||
|
||||
In this case the libraries and executable are not linked against OpenSSL Libraries. Cross compilers for the Windows platform can be installed on Debian like systems like this:
|
||||
|
||||
```
|
||||
$ apt-get install gcc-mingw-w64-x86-64 gcc-mingw-w64-i686
|
||||
```
|
||||
|
||||
## Build instructions for GNU Make
|
||||
|
||||
Ensure the OpenSSL development package is installed. Then from the client library base directory run:
|
||||
|
||||
```
|
||||
$ make
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
This will build and install the libraries. To uninstall:
|
||||
|
||||
```
|
||||
$ sudo make uninstall
|
||||
```
|
||||
|
||||
To build the documentation requires doxygen and optionally graphviz.
|
||||
|
||||
```
|
||||
$ make html
|
||||
```
|
||||
|
||||
The provided GNU Makefile is intended to perform all build steps in the ```build``` directory within the source-tree of Eclipse Paho. Generated binares, libraries, and the documentation can be found in the ```build/output``` directory after completion.
|
||||
|
||||
Options that are passed to the compiler/linker can be specified by typical Unix build variables:
|
||||
|
||||
Variable | Description
|
||||
------------ | -------------
|
||||
CC | Path to the C compiler
|
||||
CFLAGS | Flags passed to compiler calls
|
||||
LDFLAGS | Flags passed to linker calls
|
||||
|
||||
## Building paho-mqtt - Using vcpkg
|
||||
|
||||
You can download and install paho-mqtt using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
|
||||
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh
|
||||
./vcpkg integrate install
|
||||
./vcpkg install paho-mqtt
|
||||
|
||||
The paho-mqtt port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||
|
||||
## Fully static builds with musl libc
|
||||
|
||||
(By Frank Pagliughi)
|
||||
|
||||
[musl libc](https://musl.libc.org/) is is an implementation of the C standard library built on top of the Linux system call API, including interfaces defined in the base language standard, POSIX, and widely agreed-upon extensions.
|
||||
|
||||
Users of the Rust library, which wraps this one, had been complaining that they could not compile using the musl build tools. Musl is a small std C lib that can be statically linked. With the latest Paho C library (and a very minor tweak to the build), we're now able to build Rust apps using musl and Paho C that are fully static; no runtime dependencies on the platform; not even on the standard C lib.
|
||||
|
||||
$ ./async_publish
|
||||
Publishing a message on the 'test' topic
|
||||
|
||||
$ ldd async_publish
|
||||
not a dynamic executable
|
||||
|
||||
So, for example, if maintaining a suite of apps for some newer and older embedded Linux boards, the same executables could be deployed without worry about the C ABI on the particular boards.
|
||||
|
||||
Certainly C apps using the Paho library could do this also.
|
||||
|
||||
## Microsoft Windows
|
||||
|
||||
### Calling convention
|
||||
|
||||
As is normal for C programs on Windows, the calling convention is __cdecl. See the Microsoft documentation here:
|
||||
|
||||
https://docs.microsoft.com/en-us/cpp/cpp/cdecl?view=vs-2019
|
||||
|
||||
If you call this library from another language, you may need to take this into account.
|
||||
|
||||
16
3rd/paho.mqtt.c/SECURITY.md
Normal file
16
3rd/paho.mqtt.c/SECURITY.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Security Policy
|
||||
|
||||
This project follows the [Eclipse Vulnerability Reporting Policy](https://www.eclipse.org/security/policy.php).
|
||||
Vulnerabilities are tracked by the Eclipse security team, in cooperation with the project lead.
|
||||
Fixing vulnerabilities is taken care of by the project committers, with assistance and guidance of the security
|
||||
team.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Eclipse Paho provides security updates for the most recent version only.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We recommend that in case of suspected vulnerabilities you do not create a GitHub issue, but instead contact the
|
||||
Eclipse Security Team directly sending an email to security@eclipse.org.
|
||||
|
||||
28
3rd/paho.mqtt.c/about.html
Normal file
28
3rd/paho.mqtt.c/about.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"><head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>About</title>
|
||||
</head>
|
||||
<body lang="EN-US">
|
||||
<h2>About This Content</h2>
|
||||
|
||||
<p><em>April 6, 2020</em></p>
|
||||
<h3>License</h3>
|
||||
|
||||
<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
|
||||
indicated below, the Content is provided to you under the terms and conditions of the
|
||||
Eclipse Public License Version 2.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
|
||||
A copy of the EPL is available at
|
||||
<a href="https://www.eclipse.org/legal/epl-2.0/">https://www.eclipse.org/legal/epl-2.0/</a>
|
||||
and a copy of the EDL is available at
|
||||
<a href="http://www.eclipse.org/org/documents/edl-v10.php">http://www.eclipse.org/org/documents/edl-v10.php</a>.
|
||||
For purposes of the EPL, "Program" will mean the Content.</p>
|
||||
|
||||
<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
|
||||
being redistributed by another party ("Redistributor") and different terms and conditions may
|
||||
apply to your use of any object code in the Content. Check the Redistributor's license that was
|
||||
provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
|
||||
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
|
||||
and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
|
||||
|
||||
</body></html>
|
||||
140
3rd/paho.mqtt.c/android/Android.mk
Normal file
140
3rd/paho.mqtt.c/android/Android.mk
Normal file
@@ -0,0 +1,140 @@
|
||||
# Example: Android Native Library makefile for paho.mqtt.c
|
||||
# contributed by Bin Li <bin.li@windriver.com>
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
libpaho-mqtt3_lib_path := ../src
|
||||
libpaho-mqtt3_c_includes := $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path) \
|
||||
external/hdc/android-ifaddrs \
|
||||
external/openssl/include \
|
||||
external/zlib
|
||||
|
||||
# build sample util
|
||||
define build_sample_util
|
||||
__sample_module:= $1
|
||||
__sample_lib:= $2
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_C_INCLUDES := $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_SHARED_LIBRARIES := $$(__sample_lib)
|
||||
LOCAL_MODULE := $$(__sample_module)
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_lib_path)/samples/$$(__sample_module).c
|
||||
include $(BUILD_EXECUTABLE)
|
||||
endef
|
||||
|
||||
libpaho-mqtt3_local_src_c_files_common := \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTProtocolClient.c \
|
||||
$(libpaho-mqtt3_lib_path)/Tree.c \
|
||||
$(libpaho-mqtt3_lib_path)/Heap.c \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTPacket.c \
|
||||
$(libpaho-mqtt3_lib_path)/Clients.c \
|
||||
$(libpaho-mqtt3_lib_path)/Thread.c \
|
||||
$(libpaho-mqtt3_lib_path)/utf-8.c \
|
||||
$(libpaho-mqtt3_lib_path)/StackTrace.c \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTProtocolOut.c \
|
||||
$(libpaho-mqtt3_lib_path)/Socket.c \
|
||||
$(libpaho-mqtt3_lib_path)/Log.c \
|
||||
$(libpaho-mqtt3_lib_path)/Messages.c \
|
||||
$(libpaho-mqtt3_lib_path)/LinkedList.c \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTPersistence.c \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTPacketOut.c \
|
||||
$(libpaho-mqtt3_lib_path)/SocketBuffer.c \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTPersistenceDefault.c \
|
||||
|
||||
libpaho-mqtt3_local_src_c_files_c := \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTClient.c \
|
||||
|
||||
libpaho-mqtt3_local_src_c_files_cs := \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTClient.c \
|
||||
$(libpaho-mqtt3_lib_path)/SSLSocket.c \
|
||||
|
||||
libpaho-mqtt3_local_src_c_files_a := \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTAsync.c \
|
||||
|
||||
libpaho-mqtt3_local_src_c_files_as := \
|
||||
$(libpaho-mqtt3_lib_path)/MQTTAsync.c \
|
||||
$(libpaho-mqtt3_lib_path)/SSLSocket.c \
|
||||
|
||||
# update the header file which normally generated by cmake
|
||||
$(shell (cp -f $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path)/VersionInfo.h.in $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path)/VersionInfo.h))
|
||||
$(shell (sed -i "s/@CLIENT_VERSION@/1.2.0/g" $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path)/VersionInfo.h))
|
||||
$(shell ( sed -i "s/@BUILD_TIMESTAMP@/$(shell date)/g" $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path)/VersionInfo.h))
|
||||
|
||||
# building static libraries
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libpaho-mqtt3c
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path)
|
||||
LOCAL_C_INCLUDES:= $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_local_src_c_files_common) $(libpaho-mqtt3_local_src_c_files_c)
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libpaho-mqtt3cs
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path)
|
||||
LOCAL_C_INCLUDES:= $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_CFLAGS += -DOPENSSL
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_local_src_c_files_common) $(libpaho-mqtt3_local_src_c_files_cs)
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libpaho-mqtt3a
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/${libpaho-mqtt3_lib_path}
|
||||
LOCAL_C_INCLUDES:= $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_local_src_c_files_common) $(libpaho-mqtt3_local_src_c_files_a)
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libpaho-mqtt3as
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/${libpaho-mqtt3_lib_path}
|
||||
LOCAL_CFLAGS += -DOPENSSL
|
||||
LOCAL_C_INCLUDES:= $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_local_src_c_files_common) $(libpaho-mqtt3_local_src_c_files_as)
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# building shared libraries
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libpaho-mqtt3c
|
||||
LOCAL_SHARED_LIBRARIES := libdl
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path)
|
||||
LOCAL_C_INCLUDES:= $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_local_src_c_files_common) $(libpaho-mqtt3_local_src_c_files_c)
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libpaho-mqtt3cs
|
||||
LOCAL_SHARED_LIBRARIES := libcrypto libssl libdl
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/$(libpaho-mqtt3_lib_path)
|
||||
LOCAL_C_INCLUDES:= $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_CFLAGS += -DOPENSSL
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_local_src_c_files_common) $(libpaho-mqtt3_local_src_c_files_cs)
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libpaho-mqtt3a
|
||||
LOCAL_SHARED_LIBRARIES := libdl
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/${libpaho-mqtt3_lib_path}
|
||||
LOCAL_C_INCLUDES:= $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_local_src_c_files_common) $(libpaho-mqtt3_local_src_c_files_a)
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libpaho-mqtt3as
|
||||
LOCAL_SHARED_LIBRARIES := libcrypto libssl libdl
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/${libpaho-mqtt3_lib_path}
|
||||
LOCAL_CFLAGS += -DOPENSSL
|
||||
LOCAL_C_INCLUDES:= $(libpaho-mqtt3_c_includes)
|
||||
LOCAL_SRC_FILES := $(libpaho-mqtt3_local_src_c_files_common) $(libpaho-mqtt3_local_src_c_files_as)
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
# building samples
|
||||
|
||||
$(eval $(call build_sample_util, MQTTAsync_subscribe, libpaho-mqtt3a ) )
|
||||
$(eval $(call build_sample_util, MQTTAsync_publish, libpaho-mqtt3a ) )
|
||||
$(eval $(call build_sample_util, MQTTClient_publish, libpaho-mqtt3c ) )
|
||||
$(eval $(call build_sample_util, MQTTClient_publish_async, libpaho-mqtt3c ) )
|
||||
$(eval $(call build_sample_util, MQTTClient_subscribe, libpaho-mqtt3c ) )
|
||||
$(eval $(call build_sample_util, paho_c_pub, libpaho-mqtt3a ) )
|
||||
$(eval $(call build_sample_util, paho_c_sub, libpaho-mqtt3a ) )
|
||||
$(eval $(call build_sample_util, paho_cs_pub, libpaho-mqtt3c ) )
|
||||
$(eval $(call build_sample_util, paho_cs_sub, libpaho-mqtt3c ) )
|
||||
|
||||
100
3rd/paho.mqtt.c/appveyor.yml
Normal file
100
3rd/paho.mqtt.c/appveyor.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
version: 1.3.{build}
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
|
||||
PAHO_WINDOWS_BUILD_BIT: x64
|
||||
OPENSSL_ROOT_DIR: "C:/OpenSSL-v111-Win64"
|
||||
PAHO_BUILD_STATIC: FALSE
|
||||
PAHO_BUILD_SHARED: TRUE
|
||||
PAHO_HIGH_PERFORMANCE: FALSE
|
||||
PYTHON_VERSION: Python36
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
PAHO_WINDOWS_BUILD_BIT: x64
|
||||
OPENSSL_ROOT_DIR: "C:/OpenSSL-Win64"
|
||||
PAHO_BUILD_STATIC: TRUE
|
||||
PAHO_BUILD_SHARED: FALSE
|
||||
PAHO_HIGH_PERFORMANCE: TRUE
|
||||
PYTHON_VERSION: Python36
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
PAHO_WINDOWS_BUILD_BIT: x86
|
||||
OPENSSL_ROOT_DIR: "C:/OpenSSL-Win32"
|
||||
PAHO_BUILD_STATIC: FALSE
|
||||
PAHO_BUILD_SHARED: TRUE
|
||||
PAHO_HIGH_PERFORMANCE: TRUE
|
||||
PYTHON_VERSION: Python313
|
||||
|
||||
configuration: Debug
|
||||
install:
|
||||
- cmd: ver
|
||||
- cmd: openssl version
|
||||
- cmd: C:\%PYTHON_VERSION%\python --version
|
||||
- cmd: netsh advfirewall firewall add rule name="Python 3" dir=in action=allow program="C:\%PYTHON_VERSION%\python.exe" enable=yes
|
||||
- cmd: netsh advfirewall firewall add rule name="Open Port 1883" dir=in action=allow protocol=TCP localport=1883
|
||||
- cmd: netsh advfirewall set allprofiles state off
|
||||
- ps: Start-Process C:\$Env:PYTHON_VERSION\python -ArgumentList 'test\mqttsas.py'
|
||||
- cmd: git clone https://github.com/eclipse/paho.mqtt.testing.git
|
||||
- cmd: cd paho.mqtt.testing\interoperability
|
||||
- ps: Start-Process C:\$Env:PYTHON_VERSION\python -ArgumentList 'startbroker.py -c localhost_testing.conf'
|
||||
- cmd: cd ..\..
|
||||
|
||||
build_script:
|
||||
- cmd: >-
|
||||
mkdir build.paho
|
||||
|
||||
cd build.paho
|
||||
|
||||
echo %APPVEYOR_BUILD_WORKER_IMAGE%
|
||||
|
||||
if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2019" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %PAHO_WINDOWS_BUILD_BIT%
|
||||
|
||||
if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %PAHO_WINDOWS_BUILD_BIT%
|
||||
|
||||
if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2013" call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" %PAHO_WINDOWS_BUILD_BIT%
|
||||
|
||||
cmake -G "NMake Makefiles" -DPAHO_WITH_SSL=TRUE -DOPENSSL_ROOT_DIR=%OPENSSL_ROOT_DIR% -DPAHO_BUILD_DOCUMENTATION=FALSE -DPAHO_BUILD_SAMPLES=TRUE -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=TRUE -DPAHO_BUILD_STATIC=%PAHO_BUILD_STATIC% -DPAHO_BUILD_SHARED=%PAHO_BUILD_SHARED% -DPAHO_HIGH_PERFORMANCE=%PAHO_HIGH_PERFORMANCE% ..
|
||||
|
||||
nmake
|
||||
|
||||
ctest -T test -VV
|
||||
|
||||
cd ..
|
||||
|
||||
after_build:
|
||||
- cmd: >-
|
||||
set ZIPNAME=eclipse-paho-mqtt-c-windows.zip
|
||||
|
||||
7z a %ZIPNAME% %APPVEYOR_BUILD_FOLDER%\*.html
|
||||
|
||||
7z a %ZIPNAME% %APPVEYOR_BUILD_FOLDER%\*.md
|
||||
|
||||
7z a %ZIPNAME% %APPVEYOR_BUILD_FOLDER%\*-v10
|
||||
|
||||
7z a %ZIPNAME% build.paho\src\*.dll
|
||||
|
||||
7z a %ZIPNAME% build.paho\src\*.lib
|
||||
|
||||
7z rn %ZIPNAME% build.paho\src lib
|
||||
|
||||
7z a %ZIPNAME% build.paho\src\samples\*.exe
|
||||
|
||||
7z rn %ZIPNAME% build.paho\src\samples bin
|
||||
|
||||
if "%PAHO_BUILD_SHARED%" == "TRUE" 7z a %ZIPNAME% "%APPVEYOR_BUILD_FOLDER%\build.paho\src\MQTTVersion.exe"
|
||||
|
||||
7z rn %ZIPNAME% MQTTVersion.exe bin\MQTTVersion.exe
|
||||
|
||||
7z a %ZIPNAME% src\MQTTClient.h src\MQTTAsync.h src\MQTTClientPersistence.h src\MQTTProperties.h src\MQTTReasonCodes.h src\MQTTSubscribeOpts.h src\MQTTExportDeclarations.h
|
||||
|
||||
7z rn %ZIPNAME% src include
|
||||
|
||||
7z a %ZIPNAME% src\samples\*.c
|
||||
|
||||
7z rn %ZIPNAME% src\samples samples
|
||||
|
||||
artifacts:
|
||||
- path: eclipse-paho-mqtt-c-windows.zip
|
||||
name: paho-mqtt-c
|
||||
|
||||
test:
|
||||
assemblies: build/Testing/*/Test.xml
|
||||
319
3rd/paho.mqtt.c/build.xml
Normal file
319
3rd/paho.mqtt.c/build.xml
Normal file
@@ -0,0 +1,319 @@
|
||||
<!--****************************************************************************
|
||||
Copyright (c) 2012, 2020 IBM Corp.
|
||||
|
||||
All rights reserved. This program and the accompanying materials
|
||||
are made available under the terms of the Eclipse Public License v2.0
|
||||
and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
|
||||
The Eclipse Public License is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/
|
||||
and the Eclipse Distribution License is available at
|
||||
http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
|
||||
Contributors:
|
||||
Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************-->
|
||||
|
||||
<project name="MQTT C Client" default="full">
|
||||
|
||||
<taskdef resource="net/sf/antcontrib/antlib.xml">
|
||||
<classpath>
|
||||
<pathelement location="/opt/public/cbi/build/3rdPartyJars/ant-contrib.jar" />
|
||||
<pathelement location="/usr/share/java/ant-contrib.jar" />
|
||||
</classpath>
|
||||
</taskdef>
|
||||
|
||||
<property name="output.folder" value="build/output" />
|
||||
<loadfile property="version.major" srcFile="version.major"/>
|
||||
<loadfile property="version.minor" srcFile="version.minor"/>
|
||||
<loadfile property="version.patch" srcFile="version.patch"/>
|
||||
<property name="release.version" value="${version.major}.${version.minor}.${version.patch}" />
|
||||
|
||||
<property name="libname" value="mqttv3c" />
|
||||
<property name="libname.ssl" value="mqttv3cs" />
|
||||
<property name="libname.async" value="mqttv3a" />
|
||||
<property name="libname.async.ssl" value="mqttv3as" />
|
||||
<property name="ssl" value="yes" />
|
||||
<property name="windows.openssl.folder" value="c:\openssl\bin" />
|
||||
<property name="test.hostname" value="iot.eclipse.org"/>
|
||||
<property name="test.port" value="1883"/>
|
||||
<property name="proxy.port" value="18883"/>
|
||||
<if>
|
||||
<os family="windows"/>
|
||||
<then>
|
||||
<property name="os.family" value="windows" />
|
||||
</then>
|
||||
<else>
|
||||
<if>
|
||||
<os family="mac"/>
|
||||
<then>
|
||||
<property name="os.family" value="mac" />
|
||||
</then>
|
||||
<else>
|
||||
<property name="os.family" value="unix" />
|
||||
</else>
|
||||
</if>
|
||||
</else>
|
||||
</if>
|
||||
<echo message="os.family is '${os.family}'" />
|
||||
|
||||
<target name="init">
|
||||
<tstamp>
|
||||
<format property="buildTimestamp" pattern="yyyyMMddHHmm" />
|
||||
</tstamp>
|
||||
|
||||
<fileset id="sync.source.fileset" dir="src">
|
||||
<include name="*.c"/>
|
||||
<exclude name="MQTTAsync.c"/>
|
||||
<exclude name="MQTTVersion.c"/>
|
||||
</fileset>
|
||||
<pathconvert refid="sync.source.fileset" property="sync.source.files" pathsep=" "/>
|
||||
|
||||
<fileset id="async.source.fileset" dir="src">
|
||||
<include name="*.c"/>
|
||||
<exclude name="MQTTClient.c"/>
|
||||
<exclude name="MQTTVersion.c"/>
|
||||
</fileset>
|
||||
<pathconvert refid="async.source.fileset" property="async.source.files" pathsep=" "/>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="version" depends="init" description="replace tags with the right levels">
|
||||
<property name="build.level" value="${DSTAMP}${TSTAMP}" />
|
||||
<copy file="src/VersionInfo.h.in" tofile="src/VersionInfo.h" overwrite="true"/>
|
||||
<replace file="src/VersionInfo.h" token="@BUILD_TIMESTAMP@" value="${build.level}" />
|
||||
<replace file="src/VersionInfo.h" token="@CLIENT_VERSION@" value="${release.version}" />
|
||||
</target>
|
||||
|
||||
<target name="test" >
|
||||
<!-- display Python version -->
|
||||
<exec executable="python" failonerror="true">
|
||||
<arg line="-V"/>
|
||||
</exec>
|
||||
<exec executable="python" dir="test" spawn="true">
|
||||
<arg value="mqttsas2.py" />
|
||||
<arg value="${test.hostname}" />
|
||||
<arg value="${test.port}" />
|
||||
<arg value="${proxy.port}" />
|
||||
</exec>
|
||||
<if>
|
||||
<os family="windows"/>
|
||||
<then>
|
||||
<foreach target="runAtest" param="aTest" list="test1,test2,test4,test9"/>
|
||||
</then>
|
||||
<else>
|
||||
<foreach target="runAtest" param="aTest" list="test1,test2,test4,test9"/>
|
||||
</else>
|
||||
</if>
|
||||
<foreach target="runSSLtest" param="aTest" list="test3,test5"/>
|
||||
</target>
|
||||
|
||||
<target name="runAtest">
|
||||
<if>
|
||||
<os family="windows"/>
|
||||
<then>
|
||||
<exec executable="cmd.exe" failonerror="true" dir="${output.folder}/test" >
|
||||
<arg value="/c" />
|
||||
<arg value="${aTest}.exe" />
|
||||
<arg value="--connection" />
|
||||
<arg value="tcp://${test.hostname}:${test.port}" />
|
||||
<arg value="--proxy_connection" />
|
||||
<arg value="tcp://localhost:${proxy.port}" />
|
||||
<env key="PATH" path="${output.folder}" />
|
||||
</exec>
|
||||
</then>
|
||||
<else>
|
||||
<exec executable="./${aTest}" failonerror="true" dir="${output.folder}/test" >
|
||||
<arg value="--connection" />
|
||||
<arg value="tcp://${test.hostname}:${test.port}" />
|
||||
<arg value="--proxy_connection" />
|
||||
<arg value="tcp://localhost:${proxy.port}" />
|
||||
<env key="LD_LIBRARY_PATH" path="${output.folder}" />
|
||||
<env key="DYLD_LIBRARY_PATH" path="${output.folder}" />
|
||||
</exec>
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<target name="runSSLtest">
|
||||
<if>
|
||||
<os family="windows"/>
|
||||
<then>
|
||||
<exec executable="cmd.exe" failonerror="true" dir="${output.folder}/test" >
|
||||
<arg value="/c" />
|
||||
<arg value="${aTest}.exe" />
|
||||
<arg value="--hostname" />
|
||||
<arg value="${test.hostname}" />
|
||||
<env key="PATH" path="${output.folder};${windows.openssl.folder}" />
|
||||
</exec>
|
||||
</then>
|
||||
<else>
|
||||
<exec executable="./${aTest}" failonerror="true" dir="${output.folder}/test" >
|
||||
<arg value="--hostname" />
|
||||
<arg value="${test.hostname}" />
|
||||
<env key="LD_LIBRARY_PATH" path="${output.folder}" />
|
||||
<env key="DYLD_LIBRARY_PATH" path="${output.folder}" />
|
||||
</exec>
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<target name="doc" >
|
||||
<if>
|
||||
<available file="/usr/bin/doxygen"/>
|
||||
<then>
|
||||
<mkdir dir="${output.folder}/doc"/>
|
||||
<exec executable="doxygen" dir="src">
|
||||
<arg value="../doc/DoxyfileV3ClientAPI"/>
|
||||
</exec>
|
||||
<exec executable="doxygen" dir="src">
|
||||
<arg value="../doc/DoxyfileV3AsyncAPI"/>
|
||||
</exec>
|
||||
<zip destfile="${output.folder}/MQTTClient_doc.zip">
|
||||
<zipfileset dir="${output.folder}/doc/MQTTClient" />
|
||||
</zip>
|
||||
<zip destfile="${output.folder}/MQTTAsync_doc.zip">
|
||||
<zipfileset dir="${output.folder}/doc/MQTTAsync" prefix="MQTTAsync/"/>
|
||||
</zip>
|
||||
<delete dir="${output.folder}/doc" />
|
||||
</then>
|
||||
<else>
|
||||
<echo message="doxygen is not available" />
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<target name="build" >
|
||||
<if>
|
||||
<os family="unix"/>
|
||||
<then>
|
||||
<delete dir="${output.folder}" />
|
||||
<!-- display gcc version -->
|
||||
<exec executable="gcc" failonerror="true">
|
||||
<arg line="-v"/>
|
||||
</exec>
|
||||
<if>
|
||||
<available file="/usr/bin/make"/>
|
||||
<then>
|
||||
<exec executable="make" dir="."/>
|
||||
</then>
|
||||
</if>
|
||||
</then>
|
||||
</if>
|
||||
<if>
|
||||
<os family="windows"/>
|
||||
<then>
|
||||
<delete dir="${output.folder}" />
|
||||
<!-- display gcc version -->
|
||||
<exec executable="cl" failonerror="true">
|
||||
</exec>
|
||||
<exec executable="msbuild" dir=".">
|
||||
<arg line='"Windows Build\Paho C MQTT APIs.sln"'/>
|
||||
<arg line="/p:Configuration=Release"/>
|
||||
</exec>
|
||||
</then>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<target name="package">
|
||||
<mkdir dir="${output.folder}/include"/>
|
||||
<copy overwrite="true" todir="${output.folder}/include">
|
||||
<fileset dir="src" includes="MQTTClient.h,MQTTAsync.h,MQTTClientPersistence.h"/>
|
||||
</copy>
|
||||
<copy overwrite="true" todir="${output.folder}">
|
||||
<fileset dir="." includes="README.md,CONTRIBUTING.md,about.html,notice.html,edl-v10,epl-v20"/>
|
||||
</copy>
|
||||
<mkdir dir="${output.folder}/lib"/>
|
||||
<move overwrite="true" todir="${output.folder}/lib">
|
||||
<fileset dir="${output.folder}" includes="*paho*"/>
|
||||
</move>
|
||||
<mkdir dir="${output.folder}/bin"/>
|
||||
<move overwrite="true" todir="${output.folder}/bin">
|
||||
<fileset dir="${output.folder}/samples" includes="*"/>
|
||||
<fileset dir="${output.folder}" includes="MQTTVersion"/>
|
||||
</move>
|
||||
<copy overwrite="true" todir="${output.folder}/samples">
|
||||
<fileset dir="src/samples" includes="*"/>
|
||||
</copy>
|
||||
<delete>
|
||||
<fileset dir="." includes="eclipse-paho-mqtt-c-windows-${release.version}.zip"/>
|
||||
<fileset dir="." includes="eclipse-paho-mqtt-c-${os.family}-${release.version}.tar.gz"/>
|
||||
</delete>
|
||||
|
||||
<if>
|
||||
<os family="windows"/>
|
||||
<then>
|
||||
<exec executable="c:\cygwin\bin\zip.exe" failonerror="true" dir="${output.folder}">
|
||||
<arg value="-r"/>
|
||||
<arg value="eclipse-paho-mqtt-c-windows-${release.version}.zip"/>
|
||||
<arg value="about.html"/>
|
||||
<arg value="notice.html"/>
|
||||
<arg value="README.md"/>
|
||||
<arg value="CONTRIBUTING.md"/>
|
||||
<arg value="epl-v20"/>
|
||||
<arg value="edl-v10"/>
|
||||
<arg value="include"/>
|
||||
<arg value="samples"/>
|
||||
<arg value="lib"/>
|
||||
<arg value="bin"/>
|
||||
</exec>
|
||||
</then>
|
||||
<else>
|
||||
<exec executable="tar" failonerror="true" dir="${output.folder}">
|
||||
<arg value="czf"/>
|
||||
<arg value="eclipse-paho-mqtt-c-${os.family}-${release.version}.tar.gz"/>
|
||||
<arg value="about.html"/>
|
||||
<arg value="notice.html"/>
|
||||
<arg value="README.md"/>
|
||||
<arg value="CONTRIBUTING.md"/>
|
||||
<arg value="epl-v20"/>
|
||||
<arg value="edl-v10"/>
|
||||
<arg value="include"/>
|
||||
<arg value="samples"/>
|
||||
<arg value="lib"/>
|
||||
<arg value="bin"/>
|
||||
</exec>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<if>
|
||||
<os family="unix"/>
|
||||
<then>
|
||||
<exec executable="tar" failonerror="true" dir=".">
|
||||
<arg value="czf"/>
|
||||
<arg value="${output.folder}/eclipse-paho-mqtt-c-src-${release.version}.tar.gz"/>
|
||||
<arg value="about.html"/>
|
||||
<arg value="notice.html"/>
|
||||
<arg value="README.md"/>
|
||||
<arg value="CONTRIBUTING.md"/>
|
||||
<arg value="epl-v20"/>
|
||||
<arg value="edl-v10"/>
|
||||
<arg value="Makefile"/>
|
||||
<arg value="build.xml"/>
|
||||
<arg value="src"/>
|
||||
<arg value="test"/>
|
||||
<arg value="Windows Build"/>
|
||||
</exec>
|
||||
</then>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<target name="copy">
|
||||
<if>
|
||||
<available file="/shared/technology"/>
|
||||
<then>
|
||||
<mkdir dir="/shared/technology/paho/C/${release.version}/${build.level}"/>
|
||||
<echo message="Copying the build output to /shared" />
|
||||
<copy overwrite="true" todir="/shared/technology/paho/C/${release.version}/${build.level}">
|
||||
<fileset dir="${output.folder}">
|
||||
<include name="*.gz"/>
|
||||
<include name="*.zip"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
</then>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<target name="full" depends="init, version, build, test, doc, package, copy" />
|
||||
|
||||
</project>
|
||||
24
3rd/paho.mqtt.c/cbuild.bat
Normal file
24
3rd/paho.mqtt.c/cbuild.bat
Normal file
@@ -0,0 +1,24 @@
|
||||
REM start broker and proxy
|
||||
REM Start-Process C:\Python36\python -ArgumentList 'test\mqttsas.py'
|
||||
REM Start-Process C:\Python36\python -ArgumentList 'startbroker.py -c localhost_testing.conf'
|
||||
|
||||
setlocal
|
||||
set APPVEYOR_BUILD_FOLDER=%cd%
|
||||
|
||||
rmdir /s /q build.paho
|
||||
mkdir build.paho
|
||||
|
||||
cd build.paho
|
||||
|
||||
REM call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
|
||||
call "j:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||
|
||||
cmake -G "NMake Makefiles" -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_DOCUMENTATION=FALSE -DPAHO_BUILD_SAMPLES=TRUE -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=TRUE ..
|
||||
|
||||
nmake
|
||||
|
||||
ctest -T test -VV
|
||||
|
||||
cd ..
|
||||
|
||||
endlocal
|
||||
17
3rd/paho.mqtt.c/cmake-build.sh
Normal file
17
3rd/paho.mqtt.c/cmake-build.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf build.paho
|
||||
mkdir build.paho
|
||||
cd build.paho
|
||||
echo "travis build dir $TRAVIS_BUILD_DIR pwd $PWD with OpenSSL root $OPENSSL_ROOT_DIR"
|
||||
cmake -DPAHO_BUILD_STATIC=$PAHO_BUILD_STATIC -DPAHO_BUILD_SHARED=$PAHO_BUILD_SHARED -DCMAKE_BUILD_TYPE=Debug -DPAHO_WITH_SSL=TRUE -DOPENSSL_ROOT_DIR=$OPENSSL_ROOT_DIR -DPAHO_BUILD_DOCUMENTATION=FALSE -DPAHO_BUILD_SAMPLES=TRUE -DPAHO_HIGH_PERFORMANCE=$PAHO_HIGH_PERFORMANCE -DPAHO_USE_SELECT=$PAHO_USE_SELECT ..
|
||||
cmake --build .
|
||||
python3 ../test/mqttsas.py &
|
||||
ctest -VV --timeout 600
|
||||
killall python3 || true
|
||||
sleep 3 # allow broker time to terminate and report
|
||||
#killall mosquitto
|
||||
cpack --verbose
|
||||
|
||||
88
3rd/paho.mqtt.c/cmake/CPackDebConfig.cmake.in
Normal file
88
3rd/paho.mqtt.c/cmake/CPackDebConfig.cmake.in
Normal file
@@ -0,0 +1,88 @@
|
||||
IF (CPACK_GENERATOR MATCHES "DEB")
|
||||
FIND_PROGRAM(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
|
||||
IF (DPKG_PROGRAM)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${DPKG_PROGRAM} --print-architecture
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
ELSE (DPKG_PROGRAM)
|
||||
MESSAGE(FATAL_ERROR "Could not find an architecture for the package")
|
||||
ENDIF (DPKG_PROGRAM)
|
||||
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND lsb_release -si
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_DIST_NAME
|
||||
RESULT_VARIABLE DIST_NAME_STATUS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
IF (DIST_NAME_STATUS)
|
||||
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution name")
|
||||
ENDIF (DIST_NAME_STATUS)
|
||||
|
||||
IF (CPACK_DEBIAN_DIST_NAME STREQUAL "")
|
||||
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution name")
|
||||
ENDIF ()
|
||||
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND lsb_release -sc
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_DIST_CODE
|
||||
RESULT_VARIABLE DIST_CODE_STATUS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
IF (DIST_NAME_STATUS)
|
||||
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution codename")
|
||||
ENDIF (DIST_NAME_STATUS)
|
||||
|
||||
IF (CPACK_DEBIAN_DIST_CODE STREQUAL "")
|
||||
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution codename")
|
||||
ENDIF ()
|
||||
|
||||
SET(CPACK_PACKAGE_VERSION_MAJOR @PAHO_VERSION_MAJOR@)
|
||||
SET(CPACK_PACKAGE_VERSION_MINOR @PAHO_VERSION_MINOR@)
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH @PAHO_VERSION_PATCH@)
|
||||
SET(PACKAGE_VERSION
|
||||
"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
|
||||
|
||||
IF (PACKAGE_VERSION STREQUAL "")
|
||||
MESSAGE(FATAL_ERROR "Could not find a version number for the package")
|
||||
ENDIF ()
|
||||
|
||||
SET(PAHO_WITH_SSL @PAHO_WITH_SSL@)
|
||||
|
||||
MESSAGE("Package version: ${PACKAGE_VERSION}")
|
||||
MESSAGE("Package built for: ${CPACK_DEBIAN_DIST_NAME} ${CPACK_DEBIAN_DIST_CODE}")
|
||||
IF(PAHO_WITH_SSL)
|
||||
MESSAGE("Package built with ssl-enabled binaries too")
|
||||
ENDIF()
|
||||
|
||||
# Additional lines to a paragraph should start with " "; paragraphs should
|
||||
# be separated with a " ." line
|
||||
SET(CPACK_PACKAGE_NAME "libpaho-mqtt")
|
||||
SET(CPACK_PACKAGE_CONTACT "Eclipse")
|
||||
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Eclipse Paho MQTT C client")
|
||||
SET(CPACK_DEBIAN_PACKAGE_NAME ${CPACK_PACKAGE_NAME})
|
||||
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER
|
||||
"Genis Riera Perez <genis.riera.perez@gmail.com>")
|
||||
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Eclipse Paho MQTT C client library")
|
||||
SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
SET(CPACK_DEBIAN_PACKAGE_VERSION ${PACKAGE_VERSION})
|
||||
SET(CPACK_DEBIAN_PACKAGE_SECTION "net")
|
||||
SET(CPACK_DEBIAN_PACKAGE_CONFLICTS ${CPACK_PACKAGE_NAME})
|
||||
SET(CPACK_PACKAGE_FILE_NAME
|
||||
"${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
|
||||
UNSET(PACKAGE_VERSION CACHE)
|
||||
UNSET(CPACK_DEBIAN_PACKAGE_VERSION CACHE)
|
||||
|
||||
#
|
||||
# From CMakeDebHelper
|
||||
# See http://www.cmake.org/Wiki/CMake:CPackPackageGenerators#Overall_usage_.28common_to_all_generators.29
|
||||
#
|
||||
|
||||
# When the DEB-generator runs, we want him to run our install-script
|
||||
#set( CPACK_INSTALL_SCRIPT ${CPACK_DEBIAN_INSTALL_SCRIPT} )
|
||||
|
||||
ENDIF()
|
||||
227
3rd/paho.mqtt.c/cmake/modules/FindLibreSSL.cmake
Normal file
227
3rd/paho.mqtt.c/cmake/modules/FindLibreSSL.cmake
Normal file
@@ -0,0 +1,227 @@
|
||||
#[=======================================================================[
|
||||
|
||||
Copyright (c) 2019 John Norrbin <jlnorrbin@johnex.se>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
FindLibreSSL
|
||||
------------
|
||||
|
||||
Find the LibreSSL encryption library.
|
||||
|
||||
Optional Components
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This module supports two optional components: SSL and TLS. Both
|
||||
components have associated imported targets, as described below.
|
||||
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines the following imported targets:
|
||||
|
||||
LibreSSL::Crypto
|
||||
The LibreSSL crypto library, if found.
|
||||
|
||||
LibreSSL::SSL
|
||||
The LibreSSL ssl library, if found. Requires and includes LibreSSL::Crypto automatically.
|
||||
|
||||
LibreSSL::TLS
|
||||
The LibreSSL tls library, if found. Requires and includes LibreSSL::SSL and LibreSSL::Crypto automatically.
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module will set the following variables in your project:
|
||||
|
||||
LIBRESSL_FOUND
|
||||
System has the LibreSSL library. If no components are requested it only requires the crypto library.
|
||||
LIBRESSL_INCLUDE_DIR
|
||||
The LibreSSL include directory.
|
||||
LIBRESSL_CRYPTO_LIBRARY
|
||||
The LibreSSL crypto library.
|
||||
LIBRESSL_SSL_LIBRARY
|
||||
The LibreSSL SSL library.
|
||||
LIBRESSL_TLS_LIBRARY
|
||||
The LibreSSL TLS library.
|
||||
LIBRESSL_LIBRARIES
|
||||
All LibreSSL libraries.
|
||||
LIBRESSL_VERSION
|
||||
This is set to $major.$minor.$revision (e.g. 2.6.8).
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
Set LIBRESSL_ROOT_DIR to the root directory of an LibreSSL installation.
|
||||
|
||||
]=======================================================================]
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
|
||||
# Set Hints
|
||||
set(_LIBRESSL_ROOT_HINTS
|
||||
${LIBRESSL_ROOT_DIR}
|
||||
ENV LIBRESSL_ROOT_DIR
|
||||
)
|
||||
|
||||
# Set Paths
|
||||
if (WIN32)
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_LIBRESSL_ROOT_PATHS
|
||||
"${_programfiles}/LibreSSL"
|
||||
)
|
||||
unset(_programfiles)
|
||||
else()
|
||||
set(_LIBRESSL_ROOT_PATHS
|
||||
"/usr/local/"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Combine
|
||||
set(_LIBRESSL_ROOT_HINTS_AND_PATHS
|
||||
HINTS ${_LIBRESSL_ROOT_HINTS}
|
||||
PATHS ${_LIBRESSL_ROOT_PATHS}
|
||||
)
|
||||
|
||||
# Find Include Path
|
||||
find_path(LIBRESSL_INCLUDE_DIR
|
||||
NAMES
|
||||
tls.h
|
||||
${_LIBRESSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
include
|
||||
)
|
||||
|
||||
# Find Crypto Library
|
||||
find_library(LIBRESSL_CRYPTO_LIBRARY
|
||||
NAMES
|
||||
libcrypto
|
||||
crypto
|
||||
NAMES_PER_DIR
|
||||
${_LIBRESSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
lib
|
||||
)
|
||||
|
||||
# Find SSL Library
|
||||
find_library(LIBRESSL_SSL_LIBRARY
|
||||
NAMES
|
||||
libssl
|
||||
ssl
|
||||
NAMES_PER_DIR
|
||||
${_LIBRESSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
lib
|
||||
)
|
||||
|
||||
# Find TLS Library
|
||||
find_library(LIBRESSL_TLS_LIBRARY
|
||||
NAMES
|
||||
libtls
|
||||
tls
|
||||
NAMES_PER_DIR
|
||||
${_LIBRESSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
lib
|
||||
)
|
||||
|
||||
# Set Libraries
|
||||
set(LIBRESSL_LIBRARIES ${LIBRESSL_CRYPTO_LIBRARY} ${LIBRESSL_SSL_LIBRARY} ${LIBRESSL_TLS_LIBRARY})
|
||||
|
||||
# Mark Variables As Advanced
|
||||
mark_as_advanced(LIBRESSL_INCLUDE_DIR LIBRESSL_LIBRARIES LIBRESSL_CRYPTO_LIBRARY LIBRESSL_SSL_LIBRARY LIBRESSL_TLS_LIBRARY)
|
||||
|
||||
# Find Version File
|
||||
if(LIBRESSL_INCLUDE_DIR AND EXISTS "${LIBRESSL_INCLUDE_DIR}/openssl/opensslv.h")
|
||||
|
||||
# Get Version From File
|
||||
file(STRINGS "${LIBRESSL_INCLUDE_DIR}/openssl/opensslv.h" OPENSSLV.H REGEX "#define LIBRESSL_VERSION_TEXT[ ]+\".*\"")
|
||||
|
||||
# Match Version String
|
||||
string(REGEX REPLACE ".*\".*([0-9]+)\\.([0-9]+)\\.([0-9]+)\"" "\\1;\\2;\\3" LIBRESSL_VERSION_LIST "${OPENSSLV.H}")
|
||||
|
||||
# Split Parts
|
||||
list(GET LIBRESSL_VERSION_LIST 0 LIBRESSL_VERSION_MAJOR)
|
||||
list(GET LIBRESSL_VERSION_LIST 1 LIBRESSL_VERSION_MINOR)
|
||||
list(GET LIBRESSL_VERSION_LIST 2 LIBRESSL_VERSION_REVISION)
|
||||
|
||||
# Set Version String
|
||||
set(LIBRESSL_VERSION "${LIBRESSL_VERSION_MAJOR}.${LIBRESSL_VERSION_MINOR}.${LIBRESSL_VERSION_REVISION}")
|
||||
|
||||
endif()
|
||||
|
||||
# Set Find Package Arguments
|
||||
find_package_handle_standard_args(LibreSSL
|
||||
REQUIRED_VARS
|
||||
LIBRESSL_CRYPTO_LIBRARY
|
||||
LIBRESSL_INCLUDE_DIR
|
||||
VERSION_VAR
|
||||
LIBRESSL_VERSION
|
||||
HANDLE_COMPONENTS
|
||||
FAIL_MESSAGE
|
||||
"Could NOT find LibreSSL, try setting the path to LibreSSL using the LIBRESSL_ROOT_DIR environment variable"
|
||||
)
|
||||
|
||||
# LibreSSL Found
|
||||
if(LIBRESSL_FOUND)
|
||||
|
||||
# Set LibreSSL::Crypto
|
||||
if(NOT TARGET LibreSSL::Crypto AND EXISTS "${LIBRESSL_CRYPTO_LIBRARY}")
|
||||
|
||||
# Add Library
|
||||
add_library(LibreSSL::Crypto UNKNOWN IMPORTED)
|
||||
|
||||
# Set Properties
|
||||
set_target_properties(
|
||||
LibreSSL::Crypto
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBRESSL_INCLUDE_DIR}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${LIBRESSL_CRYPTO_LIBRARY}"
|
||||
)
|
||||
|
||||
endif() # LibreSSL::Crypto
|
||||
|
||||
# Set LibreSSL::SSL
|
||||
if(NOT TARGET LibreSSL::SSL AND EXISTS "${LIBRESSL_SSL_LIBRARY}")
|
||||
|
||||
# Add Library
|
||||
add_library(LibreSSL::SSL UNKNOWN IMPORTED)
|
||||
|
||||
# Set Properties
|
||||
set_target_properties(
|
||||
LibreSSL::SSL
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBRESSL_INCLUDE_DIR}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${LIBRESSL_SSL_LIBRARY}"
|
||||
INTERFACE_LINK_LIBRARIES LibreSSL::Crypto
|
||||
)
|
||||
|
||||
endif() # LibreSSL::SSL
|
||||
|
||||
# Set LibreSSL::TLS
|
||||
if(NOT TARGET LibreSSL::TLS AND EXISTS "${LIBRESSL_TLS_LIBRARY}")
|
||||
add_library(LibreSSL::TLS UNKNOWN IMPORTED)
|
||||
set_target_properties(
|
||||
LibreSSL::TLS
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBRESSL_INCLUDE_DIR}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${LIBRESSL_TLS_LIBRARY}"
|
||||
INTERFACE_LINK_LIBRARIES LibreSSL::SSL
|
||||
)
|
||||
|
||||
endif() # LibreSSL::TLS
|
||||
|
||||
endif(LIBRESSL_FOUND)
|
||||
10
3rd/paho.mqtt.c/cmake/toolchain.linux-arm11.cmake
Normal file
10
3rd/paho.mqtt.c/cmake/toolchain.linux-arm11.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
# path to compiler and utilities
|
||||
# specify the cross compiler
|
||||
SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
|
||||
|
||||
# Name of the target platform
|
||||
SET(CMAKE_SYSTEM_NAME Linux)
|
||||
SET(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
|
||||
# Version of the system
|
||||
SET(CMAKE_SYSTEM_VERSION 1)
|
||||
15
3rd/paho.mqtt.c/cmake/toolchain.win32.cmake
Normal file
15
3rd/paho.mqtt.c/cmake/toolchain.win32.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
# Name of the target platform
|
||||
SET(CMAKE_SYSTEM_NAME Windows)
|
||||
|
||||
# Version of the system
|
||||
SET(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# specify the cross compiler
|
||||
SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
|
||||
SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
|
||||
SET(CMAKE_RC_COMPILER_ENV_VAR "RC")
|
||||
SET(CMAKE_RC_COMPILER "")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS
|
||||
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS
|
||||
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
|
||||
15
3rd/paho.mqtt.c/cmake/toolchain.win64.cmake
Normal file
15
3rd/paho.mqtt.c/cmake/toolchain.win64.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
# Name of the target platform
|
||||
SET(CMAKE_SYSTEM_NAME Windows)
|
||||
|
||||
# Version of the system
|
||||
SET(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# specify the cross compiler
|
||||
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
|
||||
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
|
||||
SET(CMAKE_RC_COMPILER_ENV_VAR "RC")
|
||||
SET(CMAKE_RC_COMPILER "")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS
|
||||
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS
|
||||
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
|
||||
BIN
3rd/paho.mqtt.c/deploy_rsa.enc
Normal file
BIN
3rd/paho.mqtt.c/deploy_rsa.enc
Normal file
Binary file not shown.
11
3rd/paho.mqtt.c/dist/Makefile
vendored
Normal file
11
3rd/paho.mqtt.c/dist/Makefile
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
VERSION=1.3.0
|
||||
|
||||
check:
|
||||
rpmlint -i dist/paho-c.spec
|
||||
|
||||
rpm-prep:
|
||||
mkdir -p ${HOME}/rpmbuild/SOURCES/
|
||||
tar --transform="s/\./paho-c-${VERSION}/" -cf ${HOME}/rpmbuild/SOURCES/v${VERSION}.tar.gz --exclude=./build.paho --exclude=.git --exclude=*.bz ./ --gzip
|
||||
|
||||
rpm: rpm-prep
|
||||
rpmbuild -ba dist/paho-c.spec
|
||||
78
3rd/paho.mqtt.c/dist/paho-c.spec
vendored
Normal file
78
3rd/paho.mqtt.c/dist/paho-c.spec
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
Summary: MQTT C Client
|
||||
Name: paho-c
|
||||
Version: 1.3.2
|
||||
Release: 3%{?dist}
|
||||
License: Eclipse Distribution License 1.0 and Eclipse Public License 2.0
|
||||
Group: Development/Tools
|
||||
Source: https://github.com/eclipse/paho.mqtt.c/archive/v%{version}.tar.gz
|
||||
URL: https://eclipse.org/paho/clients/c/
|
||||
BuildRequires: cmake
|
||||
BuildRequires: gcc
|
||||
BuildRequires: graphviz
|
||||
BuildRequires: doxygen
|
||||
BuildRequires: openssl-devel
|
||||
Requires: openssl
|
||||
|
||||
|
||||
%description
|
||||
The Paho MQTT C Client is a fully fledged MQTT client written in ANSI standard C.
|
||||
|
||||
|
||||
%package devel
|
||||
Summary: MQTT C Client development kit
|
||||
Group: Development/Libraries
|
||||
Requires: paho-c
|
||||
|
||||
%description devel
|
||||
Development files and samples for the the Paho MQTT C Client.
|
||||
|
||||
|
||||
%package devel-docs
|
||||
Summary: MQTT C Client development kit documentation
|
||||
Group: Development/Libraries
|
||||
|
||||
%description devel-docs
|
||||
Development documentation files for the the Paho MQTT C Client.
|
||||
|
||||
%prep
|
||||
%autosetup -n paho-c-%{version}
|
||||
|
||||
%build
|
||||
mkdir build.paho && cd build.paho
|
||||
%cmake -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_DOCUMENTATION=TRUE -DPAHO_BUILD_SAMPLES=TRUE ..
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
cd build.paho
|
||||
make install DESTDIR=%{buildroot}
|
||||
|
||||
%files
|
||||
%doc edl-v10 epl-v20
|
||||
%{_libdir}/*
|
||||
|
||||
%files devel
|
||||
%{_bindir}/*
|
||||
%{_includedir}/*
|
||||
|
||||
%files devel-docs
|
||||
%{_datadir}/*
|
||||
|
||||
%changelog
|
||||
* Thu Jul 27 2017 Otavio R. Piske <opiske@redhat.com> - 1.2.0-4
|
||||
- Enabled generation of debuginfo package
|
||||
|
||||
* Thu Jul 27 2017 Otavio R. Piske <opiske@redhat.com> - 1.2.0-3
|
||||
- Fixed changelog issues pointed by rpmlint
|
||||
|
||||
* Thu Jul 27 2017 Otavio R. Piske <opiske@redhat.com> - 1.2.0-2
|
||||
- Updated changelog to comply with Fedora packaging guidelines
|
||||
|
||||
* Wed Jul 26 2017 Otavio R. Piske <opiske@redhat.com> - 1.2.0-1
|
||||
- Fixed rpmlint warnings: replaced cmake call with builtin macro
|
||||
- Fixed rpmlint warnings: removed buildroot reference from build section
|
||||
|
||||
* Fri Jun 30 2017 Otavio R. Piske <opiske@redhat.com> - 1.2.0
|
||||
- Updated package to version 1.2.0
|
||||
|
||||
* Sat Dec 31 2016 Otavio R. Piske <opiske@redhat.com> - 1.1.0
|
||||
- Initial packaging
|
||||
15
3rd/paho.mqtt.c/edl-v10
Normal file
15
3rd/paho.mqtt.c/edl-v10
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
Eclipse Distribution License - v 1.0
|
||||
|
||||
Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
277
3rd/paho.mqtt.c/epl-v20
Normal file
277
3rd/paho.mqtt.c/epl-v20
Normal file
@@ -0,0 +1,277 @@
|
||||
Eclipse Public License - v 2.0
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
|
||||
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
|
||||
OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial content
|
||||
Distributed under this Agreement, and
|
||||
|
||||
b) in the case of each subsequent Contributor:
|
||||
i) changes to the Program, and
|
||||
ii) additions to the Program;
|
||||
where such changes and/or additions to the Program originate from
|
||||
and are Distributed by that particular Contributor. A Contribution
|
||||
"originates" from a Contributor if it was added to the Program by
|
||||
such Contributor itself or anyone acting on such Contributor's behalf.
|
||||
Contributions do not include changes or additions to the Program that
|
||||
are not Modified Works.
|
||||
|
||||
"Contributor" means any person or entity that Distributes the Program.
|
||||
|
||||
"Licensed Patents" mean patent claims licensable by a Contributor which
|
||||
are necessarily infringed by the use or sale of its Contribution alone
|
||||
or when combined with the Program.
|
||||
|
||||
"Program" means the Contributions Distributed in accordance with this
|
||||
Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement
|
||||
or any Secondary License (as applicable), including Contributors.
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source Code or other
|
||||
form, that is based on (or derived from) the Program and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship.
|
||||
|
||||
"Modified Works" shall mean any work in Source Code or other form that
|
||||
results from an addition to, deletion from, or modification of the
|
||||
contents of the Program, including, for purposes of clarity any new file
|
||||
in Source Code form that contains any contents of the Program. Modified
|
||||
Works shall not include works that contain only declarations,
|
||||
interfaces, types, classes, structures, or files of the Program solely
|
||||
in each case in order to link to, bind by name, or subclass the Program
|
||||
or Modified Works thereof.
|
||||
|
||||
"Distribute" means the acts of a) distributing or b) making available
|
||||
in any manner that enables the transfer of a copy.
|
||||
|
||||
"Source Code" means the form of a Program preferred for making
|
||||
modifications, including but not limited to software source code,
|
||||
documentation source, and configuration files.
|
||||
|
||||
"Secondary License" means either the GNU General Public License,
|
||||
Version 2.0, or any later versions of that license, including any
|
||||
exceptions or additional permissions as identified by the initial
|
||||
Contributor.
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby
|
||||
grants Recipient a non-exclusive, worldwide, royalty-free copyright
|
||||
license to reproduce, prepare Derivative Works of, publicly display,
|
||||
publicly perform, Distribute and sublicense the Contribution of such
|
||||
Contributor, if any, and such Derivative Works.
|
||||
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby
|
||||
grants Recipient a non-exclusive, worldwide, royalty-free patent
|
||||
license under Licensed Patents to make, use, sell, offer to sell,
|
||||
import and otherwise transfer the Contribution of such Contributor,
|
||||
if any, in Source Code or other form. This patent license shall
|
||||
apply to the combination of the Contribution and the Program if, at
|
||||
the time the Contribution is added by the Contributor, such addition
|
||||
of the Contribution causes such combination to be covered by the
|
||||
Licensed Patents. The patent license shall not apply to any other
|
||||
combinations which include the Contribution. No hardware per se is
|
||||
licensed hereunder.
|
||||
|
||||
c) Recipient understands that although each Contributor grants the
|
||||
licenses to its Contributions set forth herein, no assurances are
|
||||
provided by any Contributor that the Program does not infringe the
|
||||
patent or other intellectual property rights of any other entity.
|
||||
Each Contributor disclaims any liability to Recipient for claims
|
||||
brought by any other entity based on infringement of intellectual
|
||||
property rights or otherwise. As a condition to exercising the
|
||||
rights and licenses granted hereunder, each Recipient hereby
|
||||
assumes sole responsibility to secure any other intellectual
|
||||
property rights needed, if any. For example, if a third party
|
||||
patent license is required to allow Recipient to Distribute the
|
||||
Program, it is Recipient's responsibility to acquire that license
|
||||
before distributing the Program.
|
||||
|
||||
d) Each Contributor represents that to its knowledge it has
|
||||
sufficient copyright rights in its Contribution, if any, to grant
|
||||
the copyright license set forth in this Agreement.
|
||||
|
||||
e) Notwithstanding the terms of any Secondary License, no
|
||||
Contributor makes additional grants to any Recipient (other than
|
||||
those set forth in this Agreement) as a result of such Recipient's
|
||||
receipt of the Program under the terms of a Secondary License
|
||||
(if permitted under the terms of Section 3).
|
||||
|
||||
3. REQUIREMENTS
|
||||
|
||||
3.1 If a Contributor Distributes the Program in any form, then:
|
||||
|
||||
a) the Program must also be made available as Source Code, in
|
||||
accordance with section 3.2, and the Contributor must accompany
|
||||
the Program with a statement that the Source Code for the Program
|
||||
is available under this Agreement, and informs Recipients how to
|
||||
obtain it in a reasonable manner on or through a medium customarily
|
||||
used for software exchange; and
|
||||
|
||||
b) the Contributor may Distribute the Program under a license
|
||||
different than this Agreement, provided that such license:
|
||||
i) effectively disclaims on behalf of all other Contributors all
|
||||
warranties and conditions, express and implied, including
|
||||
warranties or conditions of title and non-infringement, and
|
||||
implied warranties or conditions of merchantability and fitness
|
||||
for a particular purpose;
|
||||
|
||||
ii) effectively excludes on behalf of all other Contributors all
|
||||
liability for damages, including direct, indirect, special,
|
||||
incidental and consequential damages, such as lost profits;
|
||||
|
||||
iii) does not attempt to limit or alter the recipients' rights
|
||||
in the Source Code under section 3.2; and
|
||||
|
||||
iv) requires any subsequent distribution of the Program by any
|
||||
party to be under a license that satisfies the requirements
|
||||
of this section 3.
|
||||
|
||||
3.2 When the Program is Distributed as Source Code:
|
||||
|
||||
a) it must be made available under this Agreement, or if the
|
||||
Program (i) is combined with other material in a separate file or
|
||||
files made available under a Secondary License, and (ii) the initial
|
||||
Contributor attached to the Source Code the notice described in
|
||||
Exhibit A of this Agreement, then the Program may be made available
|
||||
under the terms of such Secondary Licenses, and
|
||||
|
||||
b) a copy of this Agreement must be included with each copy of
|
||||
the Program.
|
||||
|
||||
3.3 Contributors may not remove or alter any copyright, patent,
|
||||
trademark, attribution notices, disclaimers of warranty, or limitations
|
||||
of liability ("notices") contained within the Program from any copy of
|
||||
the Program which they Distribute, provided that Contributors may add
|
||||
their own appropriate notices.
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities
|
||||
with respect to end users, business partners and the like. While this
|
||||
license is intended to facilitate the commercial use of the Program,
|
||||
the Contributor who includes the Program in a commercial product
|
||||
offering should do so in a manner which does not create potential
|
||||
liability for other Contributors. Therefore, if a Contributor includes
|
||||
the Program in a commercial product offering, such Contributor
|
||||
("Commercial Contributor") hereby agrees to defend and indemnify every
|
||||
other Contributor ("Indemnified Contributor") against any losses,
|
||||
damages and costs (collectively "Losses") arising from claims, lawsuits
|
||||
and other legal actions brought by a third party against the Indemnified
|
||||
Contributor to the extent caused by the acts or omissions of such
|
||||
Commercial Contributor in connection with its distribution of the Program
|
||||
in a commercial product offering. The obligations in this section do not
|
||||
apply to any claims or Losses relating to any actual or alleged
|
||||
intellectual property infringement. In order to qualify, an Indemnified
|
||||
Contributor must: a) promptly notify the Commercial Contributor in
|
||||
writing of such claim, and b) allow the Commercial Contributor to control,
|
||||
and cooperate with the Commercial Contributor in, the defense and any
|
||||
related settlement negotiations. The Indemnified Contributor may
|
||||
participate in any such claim at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial
|
||||
product offering, Product X. That Contributor is then a Commercial
|
||||
Contributor. If that Commercial Contributor then makes performance
|
||||
claims, or offers warranties related to Product X, those performance
|
||||
claims and warranties are such Commercial Contributor's responsibility
|
||||
alone. Under this section, the Commercial Contributor would have to
|
||||
defend claims against the other Contributors related to those performance
|
||||
claims and warranties, and if a court requires any other Contributor to
|
||||
pay any damages as a result, the Commercial Contributor must pay
|
||||
those damages.
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
|
||||
PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
|
||||
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
|
||||
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
|
||||
TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
|
||||
PURPOSE. Each Recipient is solely responsible for determining the
|
||||
appropriateness of using and distributing the Program and assumes all
|
||||
risks associated with its exercise of rights under this Agreement,
|
||||
including but not limited to the risks and costs of program errors,
|
||||
compliance with applicable laws, damage to or loss of data, programs
|
||||
or equipment, and unavailability or interruption of operations.
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
|
||||
PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
|
||||
SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
|
||||
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
|
||||
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. GENERAL
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under
|
||||
applicable law, it shall not affect the validity or enforceability of
|
||||
the remainder of the terms of this Agreement, and without further
|
||||
action by the parties hereto, such provision shall be reformed to the
|
||||
minimum extent necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that the
|
||||
Program itself (excluding combinations of the Program with other software
|
||||
or hardware) infringes such Recipient's patent(s), then such Recipient's
|
||||
rights granted under Section 2(b) shall terminate as of the date such
|
||||
litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it
|
||||
fails to comply with any of the material terms or conditions of this
|
||||
Agreement and does not cure such failure in a reasonable period of
|
||||
time after becoming aware of such noncompliance. If all Recipient's
|
||||
rights under this Agreement terminate, Recipient agrees to cease use
|
||||
and distribution of the Program as soon as reasonably practicable.
|
||||
However, Recipient's obligations under this Agreement and any licenses
|
||||
granted by Recipient relating to the Program shall continue and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement,
|
||||
but in order to avoid inconsistency the Agreement is copyrighted and
|
||||
may only be modified in the following manner. The Agreement Steward
|
||||
reserves the right to publish new versions (including revisions) of
|
||||
this Agreement from time to time. No one other than the Agreement
|
||||
Steward has the right to modify this Agreement. The Eclipse Foundation
|
||||
is the initial Agreement Steward. The Eclipse Foundation may assign the
|
||||
responsibility to serve as the Agreement Steward to a suitable separate
|
||||
entity. Each new version of the Agreement will be given a distinguishing
|
||||
version number. The Program (including Contributions) may always be
|
||||
Distributed subject to the version of the Agreement under which it was
|
||||
received. In addition, after a new version of the Agreement is published,
|
||||
Contributor may elect to Distribute the Program (including its
|
||||
Contributions) under the new version.
|
||||
|
||||
Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
|
||||
receives no rights or licenses to the intellectual property of any
|
||||
Contributor under this Agreement, whether expressly, by implication,
|
||||
estoppel or otherwise. All rights in the Program not expressly granted
|
||||
under this Agreement are reserved. Nothing in this Agreement is intended
|
||||
to be enforceable by any entity that is not a Contributor or Recipient.
|
||||
No third-party beneficiary rights are created under this Agreement.
|
||||
|
||||
Exhibit A - Form of Secondary Licenses Notice
|
||||
|
||||
"This Source Code may also be made available under the following
|
||||
Secondary Licenses when the conditions for such availability set forth
|
||||
in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
|
||||
version(s), and exceptions or additional permissions here}."
|
||||
|
||||
Simply including a copy of this Agreement, including this Exhibit A
|
||||
is not sufficient to license the Source Code under Secondary Licenses.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to
|
||||
look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
7
3rd/paho.mqtt.c/fmt.sh
Normal file
7
3rd/paho.mqtt.c/fmt.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Runs clang format over the whole project tree, excluding the 'build/' directory.
|
||||
#
|
||||
|
||||
find . -type d \( -path './build' \) -prune -iname '*.h' -o -iname '*.c' | xargs clang-format -i
|
||||
|
||||
108
3rd/paho.mqtt.c/notice.html
Normal file
108
3rd/paho.mqtt.c/notice.html
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
|
||||
<title>Eclipse Foundation Software User Agreement</title>
|
||||
</head>
|
||||
|
||||
<body lang="EN-US">
|
||||
<h2>Eclipse Foundation Software User Agreement</h2>
|
||||
<p>April 6, 2020</p>
|
||||
|
||||
<h3>Usage Of Content</h3>
|
||||
|
||||
<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
|
||||
(COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
|
||||
CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
|
||||
OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
|
||||
NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
|
||||
CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p>
|
||||
|
||||
<h3>Applicable Licenses</h3>
|
||||
|
||||
<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 2.0
|
||||
("EPL"). A copy of the EPL is provided with this Content and is also available at <a href="https://www.eclipse.org/legal/epl-2.0/">https://www.eclipse.org/legal/epl-2.0/</a>.
|
||||
For purposes of the EPL, "Program" will mean the Content.</p>
|
||||
|
||||
<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
|
||||
repository ("Repository") in software modules ("Modules") and made available as downloadable archives ("Downloads").</p>
|
||||
|
||||
<ul>
|
||||
<li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").</li>
|
||||
<li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".</li>
|
||||
<li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named "features". Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of the Plug-ins
|
||||
and/or Fragments associated with that Feature.</li>
|
||||
<li>Features may also include other Features ("Included Features"). Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of Included Features.</li>
|
||||
</ul>
|
||||
|
||||
<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and
|
||||
Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module
|
||||
including, but not limited to the following locations:</p>
|
||||
|
||||
<ul>
|
||||
<li>The top-level (root) directory</li>
|
||||
<li>Plug-in and Fragment directories</li>
|
||||
<li>Inside Plug-ins and Fragments packaged as JARs</li>
|
||||
<li>Sub-directories of the directory named "src" of certain Plug-ins</li>
|
||||
<li>Feature directories</li>
|
||||
</ul>
|
||||
|
||||
<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license ("Feature Update License") during the
|
||||
installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
|
||||
inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature.
|
||||
Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
|
||||
that directory.</p>
|
||||
|
||||
<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
|
||||
OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p>
|
||||
|
||||
<ul>
|
||||
<li>Eclipse Distribution License Version 1.0 (available at <a href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)</li>
|
||||
<li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
|
||||
<li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
|
||||
<li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
|
||||
<li>Metro Link Public License 1.00 (available at <a href="http://www.opengroup.org/openmotif/supporters/metrolink/license.html">http://www.opengroup.org/openmotif/supporters/metrolink/license.html</a>)</li>
|
||||
<li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
|
||||
</ul>
|
||||
|
||||
<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
|
||||
contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p>
|
||||
|
||||
|
||||
<h3>Use of Provisioning Technology</h3>
|
||||
|
||||
<p>The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
|
||||
Update Manager ("Provisioning Technology") for the purpose of allowing users to install software, documentation, information and/or
|
||||
other materials (collectively "Installable Software"). This capability is provided with the intent of allowing such users to
|
||||
install, extend and update Eclipse-based products. Information about packaging Installable Software is available at <a
|
||||
href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
|
||||
("Specification").</p>
|
||||
|
||||
<p>You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
|
||||
applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
|
||||
in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
|
||||
Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:</p>
|
||||
|
||||
<ol>
|
||||
<li>A series of actions may occur ("Provisioning Process") in which a user may execute the Provisioning Technology
|
||||
on a machine ("Target Machine") with the intent of installing, extending or updating the functionality of an Eclipse-based
|
||||
product.</li>
|
||||
<li>During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
|
||||
accessed and copied to the Target Machine.</li>
|
||||
<li>Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
|
||||
Software ("Installable Software Agreement") and such Installable Software Agreement shall be accessed from the Target
|
||||
Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
|
||||
the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
|
||||
indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.</li>
|
||||
</ol>
|
||||
|
||||
<h3>Cryptography</h3>
|
||||
|
||||
<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
|
||||
another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
|
||||
possession, or use, and re-export of encryption software, to see if this is permitted.</p>
|
||||
|
||||
<p><small>Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.</small></p>
|
||||
</body>
|
||||
</html>
|
||||
357
3rd/paho.mqtt.c/src/Base64.c
Normal file
357
3rd/paho.mqtt.c/src/Base64.c
Normal file
@@ -0,0 +1,357 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018, 2026 Wind River Systems, Inc., and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Keith Holman - initial implementation and documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Base64.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#pragma comment(lib, "crypt32.lib")
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
|
||||
{
|
||||
b64_size_t ret = 0u;
|
||||
DWORD dw_out_len = (DWORD)out_len;
|
||||
if ( CryptStringToBinaryA( in, in_len, CRYPT_STRING_BASE64, out, &dw_out_len, NULL, NULL ) )
|
||||
ret = (b64_size_t)dw_out_len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
|
||||
{
|
||||
b64_size_t ret = 0u;
|
||||
DWORD dw_out_len = (DWORD)out_len;
|
||||
if ( CryptBinaryToStringA( in, in_len, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, out, &dw_out_len ) )
|
||||
ret = (b64_size_t)dw_out_len;
|
||||
return ret;
|
||||
}
|
||||
#else /* if defined(_WIN32) */
|
||||
|
||||
#if defined(OPENSSL)
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
static b64_size_t Base64_encodeDecode(
|
||||
char *out, b64_size_t out_len, const char *in, b64_size_t in_len, int encode )
|
||||
{
|
||||
b64_size_t ret = 0u;
|
||||
if ( in_len > 0u )
|
||||
{
|
||||
if ( encode )
|
||||
{
|
||||
/* For encoding: output length is 4 * (input_length / 3) rounded up to nearest multiple of 4 */
|
||||
b64_size_t required_len = ((in_len + 2) / 3) * 4;
|
||||
if (out_len >= required_len + 1) /* +1 for null terminator */
|
||||
{
|
||||
int encoded_len = EVP_EncodeBlock((unsigned char*)out, (const unsigned char*)in, (int)in_len);
|
||||
if (encoded_len > 0)
|
||||
{
|
||||
ret = (b64_size_t)encoded_len;
|
||||
if (out_len > ret)
|
||||
out[ret] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For decoding: output length is at most 3 * (input_length / 4) */
|
||||
b64_size_t max_out_len = (in_len / 4) * 3;
|
||||
if (out_len >= max_out_len + 1) /* +1 for null terminator */
|
||||
{
|
||||
EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
|
||||
if (ctx)
|
||||
{
|
||||
int decoded_len = 0;
|
||||
int final_len = 0;
|
||||
unsigned char *temp_out = (unsigned char*)out;
|
||||
|
||||
EVP_DecodeInit(ctx);
|
||||
EVP_DecodeUpdate(ctx, temp_out, &decoded_len, (const unsigned char*)in, (int)in_len);
|
||||
EVP_DecodeFinal(ctx, temp_out + decoded_len, &final_len);
|
||||
EVP_ENCODE_CTX_free(ctx);
|
||||
|
||||
ret = (b64_size_t)(decoded_len + final_len);
|
||||
if (out_len > ret)
|
||||
out[ret] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
|
||||
{
|
||||
return Base64_encodeDecode( (char*)out, out_len, in, in_len, 0 );
|
||||
}
|
||||
|
||||
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
|
||||
{
|
||||
return Base64_encodeDecode( out, out_len, (const char*)in, in_len, 1 );
|
||||
}
|
||||
|
||||
#else /* if defined(OPENSSL) */
|
||||
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
|
||||
{
|
||||
#define NV 64
|
||||
static const unsigned char BASE64_DECODE_TABLE[] =
|
||||
{
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 0-15 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 16-31 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, 62, NV, NV, NV, 63, /* 32-47 */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, NV, NV, NV, NV, NV, NV, /* 48-63 */
|
||||
NV, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 64-79 */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, NV, NV, NV, NV, NV, /* 80-95 */
|
||||
NV, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 96-111 */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, NV, NV, NV, NV, NV, /* 112-127 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 128-143 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 144-159 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 160-175 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 176-191 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 192-207 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 208-223 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 224-239 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV /* 240-255 */
|
||||
};
|
||||
|
||||
b64_size_t ret = 0u;
|
||||
b64_size_t out_count = 0u;
|
||||
|
||||
/* in valid base64, length must be multiple of 4's: 0, 4, 8, 12, etc */
|
||||
while ( in_len > 3u && out_count < out_len )
|
||||
{
|
||||
int i;
|
||||
unsigned char c[4];
|
||||
for ( i = 0; i < 4; ++i, ++in )
|
||||
c[i] = BASE64_DECODE_TABLE[(int)(*in)];
|
||||
in_len -= 4u;
|
||||
|
||||
/* first byte */
|
||||
*out = c[0] << 2;
|
||||
*out |= (c[1] & ~0xF) >> 4;
|
||||
++out;
|
||||
++out_count;
|
||||
|
||||
if ( out_count < out_len )
|
||||
{
|
||||
/* second byte */
|
||||
*out = (c[1] & 0xF) << 4;
|
||||
if ( c[2] < NV )
|
||||
{
|
||||
*out |= (c[2] & ~0x3) >> 2;
|
||||
++out;
|
||||
++out_count;
|
||||
|
||||
if ( out_count < out_len )
|
||||
{
|
||||
/* third byte */
|
||||
*out = (c[2] & 0x3) << 6;
|
||||
if ( c[3] < NV )
|
||||
{
|
||||
*out |= c[3];
|
||||
++out;
|
||||
++out_count;
|
||||
}
|
||||
else
|
||||
in_len = 0u;
|
||||
}
|
||||
}
|
||||
else
|
||||
in_len = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
if ( out_count <= out_len )
|
||||
{
|
||||
ret = out_count;
|
||||
if ( out_count < out_len )
|
||||
*out = '\0';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
|
||||
{
|
||||
static const char BASE64_ENCODE_TABLE[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/=";
|
||||
b64_size_t ret = 0u;
|
||||
b64_size_t out_count = 0u;
|
||||
|
||||
while ( in_len > 0u && out_count < out_len )
|
||||
{
|
||||
int i;
|
||||
unsigned char c[] = { 0, 0, 64, 64 }; /* index of '=' char */
|
||||
|
||||
/* first character */
|
||||
i = *in;
|
||||
c[0] = (i & ~0x3) >> 2;
|
||||
|
||||
/* second character */
|
||||
c[1] = (i & 0x3) << 4;
|
||||
--in_len;
|
||||
if ( in_len > 0u )
|
||||
{
|
||||
++in;
|
||||
i = *in;
|
||||
c[1] |= (i & ~0xF) >> 4;
|
||||
|
||||
/* third character */
|
||||
c[2] = (i & 0xF) << 2;
|
||||
--in_len;
|
||||
if ( in_len > 0u )
|
||||
{
|
||||
++in;
|
||||
i = *in;
|
||||
c[2] |= (i & ~0x3F) >> 6;
|
||||
|
||||
/* fourth character */
|
||||
c[3] = (i & 0x3F);
|
||||
--in_len;
|
||||
++in;
|
||||
}
|
||||
}
|
||||
|
||||
/* encode the characters */
|
||||
out_count += 4u;
|
||||
for ( i = 0; i < 4 && out_count <= out_len; ++i, ++out )
|
||||
*out = BASE64_ENCODE_TABLE[c[i]];
|
||||
}
|
||||
|
||||
if ( out_count <= out_len )
|
||||
{
|
||||
if ( out_count < out_len )
|
||||
*out = '\0';
|
||||
ret = out_count;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* else if defined(OPENSSL) */
|
||||
#endif /* if else defined(_WIN32) */
|
||||
|
||||
b64_size_t Base64_decodeLength( const char *in, b64_size_t in_len )
|
||||
{
|
||||
b64_size_t pad = 0u;
|
||||
|
||||
if ( in && in_len > 1u )
|
||||
pad += ( in[in_len - 2u] == '=' ? 1u : 0u );
|
||||
if ( in && in_len > 0u )
|
||||
pad += ( in[in_len - 1u] == '=' ? 1u : 0u );
|
||||
return (in_len / 4u * 3u) - pad;
|
||||
}
|
||||
|
||||
b64_size_t Base64_encodeLength( const b64_data_t *in, b64_size_t in_len )
|
||||
{
|
||||
return ((4u * in_len / 3u) + 3u) & ~0x3;
|
||||
}
|
||||
|
||||
#if defined(BASE64_TEST)
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TEST_EXPECT(i,x) if (!(x)) {fprintf( stderr, "failed test: %s (for i == %d)\n", #x, i ); ++fails;}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct _td
|
||||
{
|
||||
const char *in;
|
||||
const char *out;
|
||||
};
|
||||
|
||||
int i;
|
||||
unsigned int fails = 0u;
|
||||
struct _td test_data[] = {
|
||||
{ "", "" },
|
||||
{ "p", "cA==" },
|
||||
{ "pa", "cGE=" },
|
||||
{ "pah", "cGFo" },
|
||||
{ "paho", "cGFobw==" },
|
||||
{ "paho ", "cGFobyA=" },
|
||||
{ "paho w", "cGFobyB3" },
|
||||
{ "paho wi", "cGFobyB3aQ==" },
|
||||
{ "paho wit", "cGFobyB3aXQ=" },
|
||||
{ "paho with", "cGFobyB3aXRo" },
|
||||
{ "paho with ", "cGFobyB3aXRoIA==" },
|
||||
{ "paho with w", "cGFobyB3aXRoIHc=" },
|
||||
{ "paho with we", "cGFobyB3aXRoIHdl" },
|
||||
{ "paho with web", "cGFobyB3aXRoIHdlYg==" },
|
||||
{ "paho with webs", "cGFobyB3aXRoIHdlYnM=" },
|
||||
{ "paho with webso", "cGFobyB3aXRoIHdlYnNv" },
|
||||
{ "paho with websoc", "cGFobyB3aXRoIHdlYnNvYw==" },
|
||||
{ "paho with websock", "cGFobyB3aXRoIHdlYnNvY2s=" },
|
||||
{ "paho with websocke", "cGFobyB3aXRoIHdlYnNvY2tl" },
|
||||
{ "paho with websocket", "cGFobyB3aXRoIHdlYnNvY2tldA==" },
|
||||
{ "paho with websockets", "cGFobyB3aXRoIHdlYnNvY2tldHM=" },
|
||||
{ "paho with websockets.", "cGFobyB3aXRoIHdlYnNvY2tldHMu" },
|
||||
{ "The quick brown fox jumps over the lazy dog",
|
||||
"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==" },
|
||||
{ "Man is distinguished, not only by his reason, but by this singular passion from\n"
|
||||
"other animals, which is a lust of the mind, that by a perseverance of delight\n"
|
||||
"in the continued and indefatigable generation of knowledge, exceeds the short\n"
|
||||
"vehemence of any carnal pleasure.",
|
||||
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
|
||||
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
|
||||
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu"
|
||||
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
|
||||
"ZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* decode tests */
|
||||
i = 0;
|
||||
while ( test_data[i].in != NULL )
|
||||
{
|
||||
int r;
|
||||
char out[512u];
|
||||
r = Base64_decode( out, sizeof(out), test_data[i].out, strlen(test_data[i].out) );
|
||||
TEST_EXPECT( i, r == strlen(test_data[i].in) && strncmp(out, test_data[i].in, strlen(test_data[i].in)) == 0 );
|
||||
++i;
|
||||
}
|
||||
|
||||
/* decode length tests */
|
||||
i = 0;
|
||||
while ( test_data[i].in != NULL )
|
||||
{
|
||||
TEST_EXPECT( i, Base64_decodeLength(test_data[i].out, strlen(test_data[i].out)) == strlen(test_data[i].in));
|
||||
++i;
|
||||
}
|
||||
|
||||
/* encode tests */
|
||||
i = 0;
|
||||
while ( test_data[i].in != NULL )
|
||||
{
|
||||
int r;
|
||||
char out[512u];
|
||||
r = Base64_encode( out, sizeof(out), test_data[i].in, strlen(test_data[i].in) );
|
||||
TEST_EXPECT( i, r == strlen(test_data[i].out) && strncmp(out, test_data[i].out, strlen(test_data[i].out)) == 0 );
|
||||
++i;
|
||||
}
|
||||
|
||||
/* encode length tests */
|
||||
i = 0;
|
||||
while ( test_data[i].in != NULL )
|
||||
{
|
||||
TEST_EXPECT( i, Base64_encodeLength(test_data[i].in, strlen(test_data[i].in)) == strlen(test_data[i].out) );
|
||||
++i;
|
||||
}
|
||||
|
||||
if ( fails )
|
||||
printf( "%u test failed!\n", fails );
|
||||
else
|
||||
printf( "all tests passed\n" );
|
||||
return fails;
|
||||
}
|
||||
#endif /* if defined(BASE64_TEST) */
|
||||
83
3rd/paho.mqtt.c/src/Base64.h
Normal file
83
3rd/paho.mqtt.c/src/Base64.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Wind River Systems, Inc. All Rights Reserved.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Keith Holman - initial implementation and documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(BASE64_H)
|
||||
#define BASE64_H
|
||||
|
||||
/** type for size of a buffer, it saves passing around @p size_t (unsigned long long or unsigned long int) */
|
||||
typedef unsigned int b64_size_t;
|
||||
/** type for raw base64 data */
|
||||
typedef unsigned char b64_data_t;
|
||||
|
||||
/**
|
||||
* Decodes base64 data
|
||||
*
|
||||
* @param[out] out decoded data
|
||||
* @param[in] out_len length of output buffer
|
||||
* @param[in] in base64 string to decode
|
||||
* @param[in] in_len length of input buffer
|
||||
*
|
||||
* @return the amount of data decoded
|
||||
*
|
||||
* @see Base64_decodeLength
|
||||
* @see Base64_encode
|
||||
*/
|
||||
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len,
|
||||
const char *in, b64_size_t in_len );
|
||||
|
||||
/**
|
||||
* Size of buffer required to decode base64 data
|
||||
*
|
||||
* @param[in] in base64 string to decode
|
||||
* @param[in] in_len length of input buffer
|
||||
*
|
||||
* @return the size of buffer the decoded string would require
|
||||
*
|
||||
* @see Base64_decode
|
||||
* @see Base64_encodeLength
|
||||
*/
|
||||
b64_size_t Base64_decodeLength( const char *in, b64_size_t in_len );
|
||||
|
||||
/**
|
||||
* Encodes base64 data
|
||||
*
|
||||
* @param[out] out encode base64 string
|
||||
* @param[in] out_len length of output buffer
|
||||
* @param[in] in raw data to encode
|
||||
* @param[in] in_len length of input buffer
|
||||
*
|
||||
* @return the amount of data encoded
|
||||
*
|
||||
* @see Base64_decode
|
||||
* @see Base64_encodeLength
|
||||
*/
|
||||
b64_size_t Base64_encode( char *out, b64_size_t out_len,
|
||||
const b64_data_t *in, b64_size_t in_len );
|
||||
|
||||
/**
|
||||
* Size of buffer required to encode base64 data
|
||||
*
|
||||
* @param[in] in raw data to encode
|
||||
* @param[in] in_len length of input buffer
|
||||
*
|
||||
* @return the size of buffer the encoded string would require
|
||||
*
|
||||
* @see Base64_decodeLength
|
||||
* @see Base64_encode
|
||||
*/
|
||||
b64_size_t Base64_encodeLength( const b64_data_t *in, b64_size_t in_len );
|
||||
|
||||
#endif /* BASE64_H */
|
||||
433
3rd/paho.mqtt.c/src/CMakeLists.txt
Normal file
433
3rd/paho.mqtt.c/src/CMakeLists.txt
Normal file
@@ -0,0 +1,433 @@
|
||||
#*******************************************************************************
|
||||
# Copyright (c) 2015, 2026 logi.cals GmbH, Frank Pagliughi <fpagliughi@mindspring.com> and others
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v2.0
|
||||
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
#
|
||||
# The Eclipse Public License is available at
|
||||
# https://www.eclipse.org/legal/epl-2.0/
|
||||
# and the Eclipse Distribution License is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# Contributors:
|
||||
# Rainer Poisel - initial version
|
||||
# Ian Craggs (IBM Corp.) - merge master
|
||||
# Ian Craggs - update for MQTTV5 support
|
||||
#*******************************************************************************/
|
||||
|
||||
## compilation/linkage settings
|
||||
|
||||
configure_file(VersionInfo.h.in
|
||||
${CMAKE_BINARY_DIR}/VersionInfo.h
|
||||
@ONLY
|
||||
)
|
||||
|
||||
set(common_src
|
||||
MQTTTime.c
|
||||
MQTTProtocolClient.c
|
||||
Clients.c
|
||||
utf-8.c
|
||||
MQTTPacket.c
|
||||
MQTTPacketOut.c
|
||||
Messages.c
|
||||
Tree.c
|
||||
Socket.c
|
||||
Log.c
|
||||
MQTTPersistence.c
|
||||
Thread.c
|
||||
MQTTProtocolOut.c
|
||||
MQTTPersistenceDefault.c
|
||||
SocketBuffer.c
|
||||
LinkedList.c
|
||||
MQTTProperties.c
|
||||
MQTTReasonCodes.c
|
||||
Base64.c
|
||||
SHA1.c
|
||||
WebSocket.c
|
||||
Proxy.c
|
||||
)
|
||||
|
||||
if(NOT PAHO_HIGH_PERFORMANCE)
|
||||
set(common_src ${common_src}
|
||||
StackTrace.c
|
||||
Heap.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(PAHO_WITH_LIBRESSL)
|
||||
set(LIBS_SYSTEM ws2_32 crypt32 rpcrt4 bcrypt)
|
||||
else()
|
||||
set(LIBS_SYSTEM ws2_32 crypt32 rpcrt4)
|
||||
endif()
|
||||
elseif(UNIX)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(LIBS_SYSTEM c dl pthread rt)
|
||||
# anl is only available with glibc so check if it is found before using
|
||||
# it or build will fail on uclibc or musl
|
||||
find_library(LIB_ANL anl)
|
||||
if(LIB_ANL)
|
||||
set(LIBS_SYSTEM "${LIBS_SYSTEM}" anl)
|
||||
endif()
|
||||
if(PAHO_WITH_LIBUUID)
|
||||
set(LIBS_SYSTEM "${LIBS_SYSTEM}" uuid)
|
||||
endif()
|
||||
add_definitions(-D_GNU_SOURCE -fvisibility=hidden)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
set(LIBS_SYSTEM c dl)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
set(LIBS_SYSTEM compat pthread)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "QNX")
|
||||
set(LIBS_SYSTEM c socket)
|
||||
else()
|
||||
set(LIBS_SYSTEM c pthread)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_SHARED)
|
||||
# common compilation for libpaho-mqtt3c and libpaho-mqtt3a
|
||||
add_library(common_obj OBJECT ${common_src})
|
||||
set_target_properties(common_obj PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_definitions(common_obj PRIVATE PAHO_MQTT_EXPORTS=1)
|
||||
|
||||
add_executable(MQTTVersion MQTTVersion.c)
|
||||
target_compile_definitions(MQTTVersion PUBLIC PAHO_MQTT_IMPORTS=1)
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_STATIC)
|
||||
add_library(common_obj_static OBJECT ${common_src})
|
||||
set_target_properties(common_obj_static PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_definitions(common_obj_static PRIVATE PAHO_MQTT_STATIC=1)
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_SHARED)
|
||||
add_library(paho-mqtt3c SHARED
|
||||
$<TARGET_OBJECTS:common_obj>
|
||||
MQTTClient.c
|
||||
)
|
||||
add_library(paho-mqtt3a SHARED
|
||||
$<TARGET_OBJECTS:common_obj>
|
||||
MQTTAsync.c
|
||||
MQTTAsyncUtils.c
|
||||
)
|
||||
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3c ALIAS paho-mqtt3c)
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3a ALIAS paho-mqtt3a)
|
||||
|
||||
target_link_libraries(paho-mqtt3c ${LIBS_SYSTEM})
|
||||
target_link_libraries(paho-mqtt3a ${LIBS_SYSTEM})
|
||||
target_link_libraries(MQTTVersion paho-mqtt3a paho-mqtt3c ${LIBS_SYSTEM})
|
||||
|
||||
set_target_properties(paho-mqtt3c paho-mqtt3a PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
COMPILE_DEFINITIONS "PAHO_MQTT_EXPORTS=1"
|
||||
)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
set(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
|
||||
set(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
|
||||
elseif(NOT WIN32)
|
||||
set(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
|
||||
set(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
set_target_properties(paho-mqtt3c PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}"
|
||||
)
|
||||
set_target_properties(paho-mqtt3a PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}"
|
||||
)
|
||||
endif()
|
||||
|
||||
foreach(TARGET paho-mqtt3c paho-mqtt3a)
|
||||
target_include_directories(${TARGET}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR})
|
||||
endforeach()
|
||||
|
||||
install(TARGETS paho-mqtt3c paho-mqtt3a
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
install(TARGETS MQTTVersion
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_STATIC)
|
||||
add_library(paho-mqtt3c-static STATIC
|
||||
$<TARGET_OBJECTS:common_obj_static>
|
||||
MQTTClient.c
|
||||
)
|
||||
add_library(paho-mqtt3a-static STATIC
|
||||
$<TARGET_OBJECTS:common_obj_static>
|
||||
MQTTAsync.c
|
||||
MQTTAsyncUtils.c
|
||||
)
|
||||
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3c-static ALIAS paho-mqtt3c-static)
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3a-static ALIAS paho-mqtt3a-static)
|
||||
|
||||
target_link_libraries(paho-mqtt3c-static ${LIBS_SYSTEM})
|
||||
target_link_libraries(paho-mqtt3a-static ${LIBS_SYSTEM})
|
||||
|
||||
if(NOT WIN32)
|
||||
set_target_properties(paho-mqtt3c-static PROPERTIES OUTPUT_NAME paho-mqtt3c)
|
||||
set_target_properties(paho-mqtt3a-static PROPERTIES OUTPUT_NAME paho-mqtt3a)
|
||||
endif()
|
||||
|
||||
set_target_properties(paho-mqtt3c-static paho-mqtt3a-static PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
COMPILE_DEFINITIONS "PAHO_MQTT_STATIC=1"
|
||||
)
|
||||
|
||||
foreach(TARGET paho-mqtt3c-static paho-mqtt3a-static)
|
||||
target_include_directories(${TARGET}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR})
|
||||
endforeach()
|
||||
|
||||
if(NOT PAHO_BUILD_SHARED)
|
||||
install(TARGETS paho-mqtt3c-static paho-mqtt3a-static
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
else()
|
||||
install(TARGETS paho-mqtt3c-static paho-mqtt3a-static
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(
|
||||
FILES
|
||||
MQTTAsync.h
|
||||
MQTTClient.h
|
||||
MQTTClientPersistence.h
|
||||
MQTTProperties.h
|
||||
MQTTReasonCodes.h
|
||||
MQTTSubscribeOpts.h
|
||||
MQTTExportDeclarations.h
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
if(PAHO_WITH_SSL OR PAHO_WITH_LIBRESSL)
|
||||
if(PAHO_WITH_LIBRESSL)
|
||||
find_package(LibreSSL REQUIRED)
|
||||
set(SSL_LIBRARY_NAME LibreSSL)
|
||||
set(SSL_INCLUDE_DIR ${LIBRESSL_INCLUDE_DIR})
|
||||
set(SSL_ROOT_DIR ${LIBRESSL_ROOT_DIR})
|
||||
else()
|
||||
find_package(OpenSSL REQUIRED)
|
||||
set(SSL_LIBRARY_NAME OpenSSL)
|
||||
set(SSL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR})
|
||||
set(SSL_ROOT_DIR ${OPENSSL_ROOT_DIR})
|
||||
endif()
|
||||
message(STATUS "Using ${SSL_LIBRARY_NAME} with headers at ${SSL_INCLUDE_DIR}")
|
||||
|
||||
if(PAHO_BUILD_SHARED)
|
||||
## common compilation for libpaho-mqtt3cs and libpaho-mqtt3as
|
||||
## Note: SSL libraries must be recompiled due to ifdefs
|
||||
add_library(common_ssl_obj OBJECT ${common_src})
|
||||
target_include_directories(common_ssl_obj PUBLIC ${SSL_INCLUDE_DIR})
|
||||
|
||||
set_property(TARGET common_ssl_obj PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_definitions(common_ssl_obj PRIVATE OPENSSL=1 PAHO_MQTT_EXPORTS=1)
|
||||
|
||||
add_library(paho-mqtt3cs SHARED
|
||||
$<TARGET_OBJECTS:common_ssl_obj>
|
||||
MQTTClient.c
|
||||
SSLSocket.c
|
||||
)
|
||||
add_library(paho-mqtt3as SHARED
|
||||
$<TARGET_OBJECTS:common_ssl_obj>
|
||||
MQTTAsync.c
|
||||
MQTTAsyncUtils.c
|
||||
SSLSocket.c
|
||||
)
|
||||
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3cs ALIAS paho-mqtt3cs)
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3as ALIAS paho-mqtt3as)
|
||||
|
||||
set_target_properties(paho-mqtt3cs paho-mqtt3as PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_EXPORTS=1"
|
||||
)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
set(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
|
||||
set(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
|
||||
elseif(NOT WIN32)
|
||||
set(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
|
||||
set(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
set_target_properties(paho-mqtt3cs PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}"
|
||||
)
|
||||
set_target_properties(paho-mqtt3as PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}")
|
||||
endif()
|
||||
|
||||
foreach(TARGET paho-mqtt3cs paho-mqtt3as)
|
||||
target_include_directories(${TARGET}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR}
|
||||
)
|
||||
target_link_libraries(${TARGET}
|
||||
PUBLIC
|
||||
${SSL_LIBRARY_NAME}::SSL ${SSL_LIBRARY_NAME}::Crypto ${LIBS_SYSTEM}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
install(TARGETS paho-mqtt3cs paho-mqtt3as
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_STATIC)
|
||||
## common compilation for libpaho-mqtt3cs and libpaho-mqtt3as
|
||||
## Note: SSL libraries must be recompiled due ifdefs
|
||||
add_library(common_ssl_obj_static OBJECT ${common_src})
|
||||
target_include_directories(common_ssl_obj_static PUBLIC ${SSL_INCLUDE_DIR})
|
||||
|
||||
set_property(TARGET common_ssl_obj_static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_definitions(common_ssl_obj_static PRIVATE OPENSSL=1 PAHO_MQTT_STATIC=1)
|
||||
|
||||
add_library(paho-mqtt3cs-static STATIC
|
||||
$<TARGET_OBJECTS:common_ssl_obj_static>
|
||||
MQTTClient.c
|
||||
SSLSocket.c
|
||||
)
|
||||
add_library(paho-mqtt3as-static STATIC
|
||||
$<TARGET_OBJECTS:common_ssl_obj_static>
|
||||
MQTTAsync.c
|
||||
MQTTAsyncUtils.c
|
||||
SSLSocket.c
|
||||
)
|
||||
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3cs-static ALIAS paho-mqtt3cs-static)
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3as-static ALIAS paho-mqtt3as-static)
|
||||
|
||||
set_target_properties(paho-mqtt3cs-static paho-mqtt3as-static PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_STATIC=1"
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
set_target_properties(paho-mqtt3cs-static PROPERTIES OUTPUT_NAME paho-mqtt3cs)
|
||||
set_target_properties(paho-mqtt3as-static PROPERTIES OUTPUT_NAME paho-mqtt3as)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
set(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
|
||||
set(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
|
||||
elseif(NOT WIN32)
|
||||
set(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
|
||||
set(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
set_target_properties(paho-mqtt3cs-static PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}"
|
||||
)
|
||||
set_target_properties(paho-mqtt3as-static PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT PAHO_BUILD_SHARED)
|
||||
install(TARGETS paho-mqtt3cs-static paho-mqtt3as-static
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
else()
|
||||
install(TARGETS paho-mqtt3cs-static paho-mqtt3as-static
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
foreach(TARGET paho-mqtt3cs-static paho-mqtt3as-static)
|
||||
target_include_directories(${TARGET}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR}
|
||||
)
|
||||
target_link_libraries(${TARGET}
|
||||
PUBLIC
|
||||
${SSL_LIBRARY_NAME}::SSL ${SSL_LIBRARY_NAME}::Crypto ${LIBS_SYSTEM}
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(EXPORT eclipse-paho-mqtt-cTargets
|
||||
FILE eclipse-paho-mqtt-cConfig.cmake
|
||||
NAMESPACE eclipse-paho-mqtt-c::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eclipse-paho-mqtt-c
|
||||
)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
write_basic_package_version_file("eclipse-paho-mqtt-cConfigVersion.cmake"
|
||||
VERSION ${CLIENT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/eclipse-paho-mqtt-cConfigVersion.cmake"
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}/cmake/eclipse-paho-mqtt-c
|
||||
)
|
||||
|
||||
# Base64 test
|
||||
add_executable(Base64Test EXCLUDE_FROM_ALL Base64.c Base64.h)
|
||||
target_compile_definitions(Base64Test PUBLIC BASE64_TEST)
|
||||
|
||||
if(PAHO_WITH_SSL OR PAHO_WITH_LIBRESSL)
|
||||
add_executable(Base64TestOpenSSL EXCLUDE_FROM_ALL Base64.c Base64.h )
|
||||
target_link_libraries(Base64TestOpenSSL ${SSL_LIBRARY_NAME}::SSL ${SSL_LIBRARY_NAME}::Crypto)
|
||||
target_compile_definitions(Base64TestOpenSSL PUBLIC BASE64_TEST OPENSSL=1)
|
||||
endif()
|
||||
|
||||
# SHA1 test
|
||||
add_executable(Sha1Test EXCLUDE_FROM_ALL SHA1.c SHA1.h)
|
||||
target_compile_definitions(Sha1Test PUBLIC SHA1_TEST)
|
||||
|
||||
if(PAHO_WITH_SSL OR PAHO_WITH_LIBRESSL)
|
||||
add_executable(Sha1TestOpenSSL EXCLUDE_FROM_ALL SHA1.c SHA1.h)
|
||||
target_link_libraries(Sha1TestOpenSSL ${SSL_LIBRARY_NAME}::SSL ${SSL_LIBRARY_NAME}::Crypto)
|
||||
target_compile_definitions(Sha1TestOpenSSL PUBLIC SHA1_TEST OPENSSL=1)
|
||||
endif()
|
||||
55
3rd/paho.mqtt.c/src/Clients.c
Normal file
55
3rd/paho.mqtt.c/src/Clients.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - add SSL support
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions which apply to client structures
|
||||
* */
|
||||
|
||||
|
||||
#include "Clients.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing clients by clientid
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int clientIDCompare(void* a, void* b)
|
||||
{
|
||||
Clients* client = (Clients*)a;
|
||||
/*printf("comparing clientdIDs %s with %s\n", client->clientID, (char*)b);*/
|
||||
return strcmp(client->clientID, (char*)b) == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing clients by socket
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int clientSocketCompare(void* a, void* b)
|
||||
{
|
||||
Clients* client = (Clients*)a;
|
||||
/*printf("comparing %d with %d\n", (char*)a, (char*)b); */
|
||||
return client->net.socket == *(SOCKET*)b;
|
||||
}
|
||||
176
3rd/paho.mqtt.c/src/Clients.h
Normal file
176
3rd/paho.mqtt.c/src/Clients.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2024 IBM Corp. and Ian Craggs
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - add SSL support
|
||||
* Ian Craggs - fix for bug 413429 - connectionLost not called
|
||||
* Ian Craggs - change will payload to binary
|
||||
* Ian Craggs - password to binary
|
||||
* Ian Craggs - MQTT 5 support
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(CLIENTS_H)
|
||||
#define CLIENTS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "MQTTTime.h"
|
||||
#if defined(_WIN32)
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#if defined(OPENSSL)
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
#include "MQTTClient.h"
|
||||
#include "LinkedList.h"
|
||||
#include "MQTTClientPersistence.h"
|
||||
#include "Socket.h"
|
||||
|
||||
/**
|
||||
* Stored publication data to minimize copying
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *topic;
|
||||
int topiclen;
|
||||
char* payload;
|
||||
int payloadlen;
|
||||
int refcount;
|
||||
uint8_t mask[4];
|
||||
} Publications;
|
||||
|
||||
/**
|
||||
* Client publication message data
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int qos;
|
||||
int retain;
|
||||
int msgid;
|
||||
int MQTTVersion;
|
||||
MQTTProperties properties;
|
||||
Publications *publish;
|
||||
START_TIME_TYPE lastTouch; /**> used for retry and expiry */
|
||||
char nextMessageType; /**> PUBREC, PUBREL, PUBCOMP */
|
||||
int len; /**> length of the whole structure+data */
|
||||
} Messages;
|
||||
|
||||
/**
|
||||
* Client will message data
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *topic;
|
||||
int payloadlen;
|
||||
void *payload;
|
||||
int retained;
|
||||
int qos;
|
||||
} willMessages;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SOCKET socket;
|
||||
START_TIME_TYPE lastSent;
|
||||
START_TIME_TYPE lastReceived;
|
||||
START_TIME_TYPE lastPing;
|
||||
#if defined(OPENSSL)
|
||||
SSL* ssl;
|
||||
SSL_CTX* ctx;
|
||||
char *https_proxy;
|
||||
char *https_proxy_auth;
|
||||
#endif
|
||||
char *http_proxy;
|
||||
char *http_proxy_auth;
|
||||
int websocket; /**< socket has been upgraded to use web sockets */
|
||||
char *websocket_key;
|
||||
const MQTTClient_nameValue* httpHeaders;
|
||||
} networkHandles;
|
||||
|
||||
|
||||
/* connection states */
|
||||
/** no connection in progress, see connected value */
|
||||
#define NOT_IN_PROGRESS 0x0
|
||||
/** TCP connection in progress */
|
||||
#define TCP_IN_PROGRESS 0x1
|
||||
/** SSL connection in progress */
|
||||
#define SSL_IN_PROGRESS 0x2
|
||||
/** Websocket connection in progress */
|
||||
#define WEBSOCKET_IN_PROGRESS 0x3
|
||||
/** TCP completed, waiting for MQTT ACK */
|
||||
#define WAIT_FOR_CONNACK 0x4
|
||||
/** Proxy connection in progress */
|
||||
#define PROXY_CONNECT_IN_PROGRESS 0x5
|
||||
/** Disconnecting */
|
||||
#define DISCONNECTING -2
|
||||
|
||||
/**
|
||||
* Data related to one client
|
||||
* The entire structure is initialized to 0 on creation, so all fields default to 0.
|
||||
*/
|
||||
typedef struct Clients
|
||||
{
|
||||
char* clientID; /**< the string id of the client */
|
||||
const char* username; /**< MQTT v3.1 user name */
|
||||
int passwordlen; /**< MQTT password length */
|
||||
const void* password; /**< MQTT v3.1 binary password */
|
||||
unsigned int cleansession : 1; /**< MQTT V3 clean session flag */
|
||||
unsigned int cleanstart : 1; /**< MQTT V5 clean start flag */
|
||||
unsigned int connected : 1; /**< whether it is currently connected */
|
||||
unsigned int good : 1; /**< if we have an error on the socket we turn this off */
|
||||
unsigned int ping_outstanding : 1;
|
||||
unsigned int ping_due : 1; /**< we couldn't send a ping so we should send one when we can */
|
||||
signed int connect_state : 4;
|
||||
START_TIME_TYPE ping_due_time; /**< the time at which the ping should have been sent (ping_due) */
|
||||
networkHandles net; /**< network info for this client */
|
||||
int msgID; /**< the MQTT message id */
|
||||
int keepAliveInterval; /**< the MQTT keep alive interval */
|
||||
int savedKeepAliveInterval; /**< saved keep alive interval, in case reset by server keep alive */
|
||||
int retryInterval; /**< the MQTT retry interval for QoS > 0 */
|
||||
int maxInflightMessages; /**< the max number of inflight outbound messages we allow */
|
||||
willMessages* will; /**< the MQTT will message, if any */
|
||||
List* inboundMsgs; /**< inbound in flight messages */
|
||||
List* outboundMsgs; /**< outbound in flight messages */
|
||||
int connect_count; /**< the number of outbound messages on reconnect - to ensure we send them all */
|
||||
int connect_sent; /**< the current number of outbound messages on reconnect that we've sent */
|
||||
List* messageQueue; /**< inbound complete but undelivered messages */
|
||||
List* outboundQueue; /**< outbound queued messages */
|
||||
unsigned int qentry_seqno;
|
||||
void* phandle; /**< the persistence handle */
|
||||
MQTTClient_persistence* persistence; /**< a persistence implementation */
|
||||
MQTTPersistence_beforeWrite* beforeWrite; /**< persistence write callback */
|
||||
MQTTPersistence_afterRead* afterRead; /**< persistence read callback */
|
||||
void* beforeWrite_context; /**< context to be used with the persistence beforeWrite callbacks */
|
||||
void* afterRead_context; /**< context to be used with the persistence afterRead callback */
|
||||
void* context; /**< calling context - used when calling disconnect_internal */
|
||||
int MQTTVersion; /**< the version of MQTT being used, 3, 4 or 5 */
|
||||
unsigned int sessionExpiry; /**< MQTT 5 session expiry */
|
||||
char* httpProxy; /**< HTTP proxy */
|
||||
char* httpsProxy; /**< HTTPS proxy */
|
||||
#if defined(OPENSSL)
|
||||
MQTTClient_SSLOptions *sslopts; /**< the SSL/TLS connect options */
|
||||
SSL_SESSION* session; /**< SSL session pointer for fast handhake */
|
||||
#endif
|
||||
} Clients;
|
||||
|
||||
int clientIDCompare(void* a, void* b);
|
||||
int clientSocketCompare(void* a, void* b);
|
||||
|
||||
/**
|
||||
* Configuration data related to all clients
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
const char* version;
|
||||
List* clients;
|
||||
} ClientStates;
|
||||
|
||||
#endif
|
||||
538
3rd/paho.mqtt.c/src/Heap.c
Normal file
538
3rd/paho.mqtt.c/src/Heap.c
Normal file
@@ -0,0 +1,538 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2024 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - use tree data structure instead of list
|
||||
* Ian Craggs - change roundup to Heap_roundup to avoid macro name clash on MacOSX
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions to manage the heap with the goal of eliminating memory leaks
|
||||
*
|
||||
* For any module to use these functions transparently, simply include the Heap.h
|
||||
* header file. Malloc and free will be redefined, but will behave in exactly the same
|
||||
* way as normal, so no recoding is necessary.
|
||||
*
|
||||
* */
|
||||
|
||||
#include "Tree.h"
|
||||
#include "Log.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#if defined(HEAP_UNIT_TESTS)
|
||||
char* Broker_recordFFDC(char* symptoms);
|
||||
#endif /* HEAP_UNIT_TESTS */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
|
||||
#undef malloc
|
||||
#undef realloc
|
||||
#undef free
|
||||
|
||||
#if defined(_WIN32)
|
||||
mutex_type heap_mutex;
|
||||
#else
|
||||
static pthread_mutex_t heap_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type heap_mutex = &heap_mutex_store;
|
||||
#endif
|
||||
|
||||
static heap_info state = {0, 0}; /**< global heap state information */
|
||||
|
||||
typedef uint64_t eyecatcherType;
|
||||
static eyecatcherType eyecatcher = (eyecatcherType)0x8888888888888888;
|
||||
#define PRIeyecatcher PRIx64 /**< print eyecatcher in HEX notation */
|
||||
|
||||
/*#define HEAP_STACK 1 */
|
||||
|
||||
/**
|
||||
* Each item on the heap is recorded with this structure.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char* file; /**< the name of the source file where the storage was allocated */
|
||||
int line; /**< the line no in the source file where it was allocated */
|
||||
void* ptr; /**< pointer to the allocated storage */
|
||||
size_t size; /**< size of the allocated storage */
|
||||
#if defined(HEAP_STACK)
|
||||
char* stack;
|
||||
#endif
|
||||
} storageElement;
|
||||
|
||||
static Tree heap; /**< Tree that holds the allocation records */
|
||||
static const char *errmsg = "Memory allocation error";
|
||||
|
||||
|
||||
static size_t Heap_roundup(size_t size);
|
||||
static int ptrCompare(void* a, void* b, int value);
|
||||
/*static void Heap_check(char* string, void* ptr);*/
|
||||
static void checkEyecatchers(char* file, int line, void* p, size_t size);
|
||||
static int Internal_heap_unlink(char* file, int line, void* p);
|
||||
static void HeapScan(enum LOG_LEVELS log_level);
|
||||
|
||||
|
||||
/**
|
||||
* Round allocation size up to a multiple of the size of an int. Apart from possibly reducing fragmentation,
|
||||
* on the old v3 gcc compilers I was hitting some weird behaviour, which might have been errors in
|
||||
* sizeof() used on structures and related to packing. In any case, this fixes that too.
|
||||
* @param size the size actually needed
|
||||
* @return the rounded up size
|
||||
*/
|
||||
static size_t Heap_roundup(size_t size)
|
||||
{
|
||||
static int multsize = 4*sizeof(int);
|
||||
|
||||
if (size % multsize != 0)
|
||||
size += multsize - (size % multsize);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing storage elements
|
||||
* @param a pointer to the current content in the tree (storageElement*)
|
||||
* @param b pointer to the memory to free
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
static int ptrCompare(void* a, void* b, int value)
|
||||
{
|
||||
a = ((storageElement*)a)->ptr;
|
||||
if (value)
|
||||
b = ((storageElement*)b)->ptr;
|
||||
|
||||
return (a > b) ? -1 : (a == b) ? 0 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
static void Heap_check(char* string, void* ptr)
|
||||
{
|
||||
Node* curnode = NULL;
|
||||
storageElement* prev, *s = NULL;
|
||||
|
||||
printf("Heap_check start %p\n", ptr);
|
||||
while ((curnode = TreeNextElement(&heap, curnode)) != NULL)
|
||||
{
|
||||
prev = s;
|
||||
s = (storageElement*)(curnode->content);
|
||||
|
||||
if (prev)
|
||||
{
|
||||
if (ptrCompare(s, prev, 1) != -1)
|
||||
{
|
||||
printf("%s: heap order error %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
|
||||
exit(99);
|
||||
}
|
||||
else
|
||||
printf("%s: heap order good %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
* Allocates a block of memory. A direct replacement for malloc, but keeps track of items
|
||||
* allocated in a list, so that free can check that a item is being freed correctly and that
|
||||
* we can check that all memory is freed at shutdown.
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param size the size of the item to be allocated
|
||||
* @return pointer to the allocated item, or NULL if there was an error
|
||||
*/
|
||||
void* mymalloc(char* file, int line, size_t size)
|
||||
{
|
||||
storageElement* s = NULL;
|
||||
size_t space = sizeof(storageElement);
|
||||
size_t filenamelen = strlen(file)+1;
|
||||
void* rc = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
size = Heap_roundup(size);
|
||||
if ((s = malloc(sizeof(storageElement))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
goto exit;
|
||||
}
|
||||
memset(s, 0, sizeof(storageElement));
|
||||
|
||||
s->size = size; /* size without eyecatchers */
|
||||
if ((s->file = malloc(filenamelen)) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
free(s);
|
||||
goto exit;
|
||||
}
|
||||
memset(s->file, 0, filenamelen);
|
||||
|
||||
space += filenamelen;
|
||||
strcpy(s->file, file);
|
||||
#if defined(HEAP_STACK)
|
||||
#define STACK_LEN 300
|
||||
if ((s->stack = malloc(STACK_LEN)) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
free(s->file);
|
||||
free(s);
|
||||
goto exit;
|
||||
}
|
||||
memset(s->stack, 0, STACK_LEN);
|
||||
StackTrace_get(Paho_thread_getid(), s->stack, STACK_LEN);
|
||||
#endif
|
||||
s->line = line;
|
||||
/* Add space for eyecatcher at each end */
|
||||
if ((s->ptr = malloc(size + 2*sizeof(eyecatcherType))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
free(s->file);
|
||||
free(s);
|
||||
goto exit;
|
||||
}
|
||||
memset(s->ptr, 0, size + 2*sizeof(eyecatcherType));
|
||||
space += size + 2*sizeof(eyecatcherType);
|
||||
*(eyecatcherType*)(s->ptr) = eyecatcher; /* start eyecatcher */
|
||||
*(eyecatcherType*)(((char*)(s->ptr)) + (sizeof(eyecatcherType) + size)) = eyecatcher; /* end eyecatcher */
|
||||
Log(TRACE_MAX, -1, "Allocating %d bytes in heap at file %s line %d ptr %p\n", (int)size, file, line, s->ptr);
|
||||
TreeAdd(&heap, s, space);
|
||||
state.current_size += size;
|
||||
if (state.current_size > state.max_size)
|
||||
state.max_size = state.current_size;
|
||||
rc = ((eyecatcherType*)(s->ptr)) + 1; /* skip start eyecatcher */
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void checkEyecatchers(char* file, int line, void* p, size_t size)
|
||||
{
|
||||
eyecatcherType *sp = (eyecatcherType*)p;
|
||||
char *cp = (char*)p;
|
||||
eyecatcherType us;
|
||||
static const char *msg = "Invalid %s eyecatcher %" PRIeyecatcher " in heap item at file %s line %d";
|
||||
|
||||
if ((us = *--sp) != eyecatcher)
|
||||
Log(LOG_ERROR, 13, msg, "start", us, file, line);
|
||||
|
||||
cp += size;
|
||||
if ((us = *(eyecatcherType*)cp) != eyecatcher)
|
||||
Log(LOG_ERROR, 13, msg, "end", us, file, line);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from the recorded heap without actually freeing it.
|
||||
* Use sparingly!
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be removed
|
||||
*/
|
||||
static int Internal_heap_unlink(char* file, int line, void* p)
|
||||
{
|
||||
Node* e = NULL;
|
||||
int rc = 0;
|
||||
|
||||
e = TreeFind(&heap, ((eyecatcherType*)p)-1);
|
||||
if (e == NULL)
|
||||
Log(LOG_ERROR, 13, "Failed to remove heap item at file %s line %d", file, line);
|
||||
else
|
||||
{
|
||||
storageElement* s = (storageElement*)(e->content);
|
||||
Log(TRACE_MAX, -1, "Freeing %d bytes in heap at file %s line %d, heap use now %d bytes\n",
|
||||
(int)s->size, file, line, (int)state.current_size);
|
||||
checkEyecatchers(file, line, p, s->size);
|
||||
/* free(s->ptr); */
|
||||
free(s->file);
|
||||
state.current_size -= s->size;
|
||||
TreeRemoveNodeIndex(&heap, e, 0);
|
||||
free(s);
|
||||
rc = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees a block of memory. A direct replacement for free, but checks that a item is in
|
||||
* the allocates list first.
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be freed
|
||||
*/
|
||||
void myfree(char* file, int line, void* p)
|
||||
{
|
||||
if (p) /* it is legal und usual to call free(NULL) */
|
||||
{
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
if (Internal_heap_unlink(file, line, p))
|
||||
free(((eyecatcherType*)p)-1);
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(LOG_ERROR, -1, "Call of free(NULL) in %s,%d",file,line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from the recorded heap without actually freeing it.
|
||||
* Use sparingly!
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be removed
|
||||
*/
|
||||
void Heap_unlink(char* file, int line, void* p)
|
||||
{
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
Internal_heap_unlink(file, line, p);
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reallocates a block of memory. A direct replacement for realloc, but keeps track of items
|
||||
* allocated in a list, so that free can check that a item is being freed correctly and that
|
||||
* we can check that all memory is freed at shutdown.
|
||||
* We have to remove the item from the tree, as the memory is in order and so it needs to
|
||||
* be reinserted in the correct place.
|
||||
* @param file use the __FILE__ macro to indicate which file this item was reallocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was reallocated at
|
||||
* @param p pointer to the item to be reallocated
|
||||
* @param size the new size of the item
|
||||
* @return pointer to the allocated item, or NULL if there was an error
|
||||
*/
|
||||
void *myrealloc(char* file, int line, void* p, size_t size)
|
||||
{
|
||||
void* rc = NULL;
|
||||
storageElement* s = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
s = TreeRemoveKey(&heap, ((eyecatcherType*)p)-1);
|
||||
if (s == NULL)
|
||||
Log(LOG_ERROR, 13, "Failed to reallocate heap item at file %s line %d", file, line);
|
||||
else
|
||||
{
|
||||
size_t space = sizeof(storageElement);
|
||||
size_t filenamelen = strlen(file)+1;
|
||||
|
||||
checkEyecatchers(file, line, p, s->size);
|
||||
size = Heap_roundup(size);
|
||||
state.current_size += size - s->size;
|
||||
if (state.current_size > state.max_size)
|
||||
state.max_size = state.current_size;
|
||||
void* newPtr = realloc(s->ptr, size + 2*sizeof(eyecatcherType));
|
||||
if (newPtr == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
goto exit;
|
||||
}
|
||||
s->ptr = newPtr;
|
||||
space += size + 2*sizeof(eyecatcherType) - s->size;
|
||||
*(eyecatcherType*)(s->ptr) = eyecatcher; /* start eyecatcher */
|
||||
*(eyecatcherType*)(((char*)(s->ptr)) + (sizeof(eyecatcherType) + size)) = eyecatcher; /* end eyecatcher */
|
||||
s->size = size;
|
||||
space -= strlen(s->file);
|
||||
newPtr = realloc(s->file, filenamelen);
|
||||
if (newPtr == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
goto exit;
|
||||
}
|
||||
s->file = newPtr;
|
||||
space += filenamelen;
|
||||
strcpy(s->file, file);
|
||||
s->line = line;
|
||||
rc = s->ptr;
|
||||
TreeAdd(&heap, s, space);
|
||||
}
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
return (rc == NULL) ? NULL : ((eyecatcherType*)(rc)) + 1; /* skip start eyecatcher */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility to find an item in the heap. Lets you know if the heap already contains
|
||||
* the memory location in question.
|
||||
* @param p pointer to a memory location
|
||||
* @return pointer to the storage element if found, or NULL
|
||||
*/
|
||||
void* Heap_findItem(void* p)
|
||||
{
|
||||
Node* e = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
e = TreeFind(&heap, ((eyecatcherType*)p)-1);
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
return (e == NULL) ? NULL : e->content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scans the heap and reports any items currently allocated.
|
||||
* To be used at shutdown if any heap items have not been freed.
|
||||
*/
|
||||
static void HeapScan(enum LOG_LEVELS log_level)
|
||||
{
|
||||
Node* current = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
Log(log_level, -1, "Heap scan start, total %d bytes", (int)state.current_size);
|
||||
while ((current = TreeNextElement(&heap, current)) != NULL)
|
||||
{
|
||||
storageElement* s = (storageElement*)(current->content);
|
||||
Log(log_level, -1, "Heap element size %d, line %d, file %s, ptr %p", (int)s->size, s->line, s->file, s->ptr);
|
||||
Log(log_level, -1, " Content %.*s", (10 > current->size) ? (int)s->size : 10, (char*)(((eyecatcherType*)s->ptr) + 1));
|
||||
#if defined(HEAP_STACK)
|
||||
Log(log_level, -1, " Stack:\n%s", s->stack);
|
||||
#endif
|
||||
}
|
||||
Log(log_level, -1, "Heap scan end");
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Heap initialization.
|
||||
*/
|
||||
int Heap_initialize(void)
|
||||
{
|
||||
TreeInitializeNoMalloc(&heap, ptrCompare);
|
||||
heap.heap_tracking = 0; /* no recursive heap tracking! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Heap termination.
|
||||
*/
|
||||
void Heap_terminate(void)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Maximum heap use was %d bytes", (int)state.max_size);
|
||||
if (state.current_size > 20) /* One log list is freed after this function is called */
|
||||
{
|
||||
Log(LOG_ERROR, -1, "Some memory not freed at shutdown, possible memory leak");
|
||||
HeapScan(LOG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Access to heap state
|
||||
* @return pointer to the heap state structure
|
||||
*/
|
||||
heap_info* Heap_get_info(void)
|
||||
{
|
||||
return &state;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump a string from the heap so that it can be displayed conveniently
|
||||
* @param file file handle to dump the heap contents to
|
||||
* @param str the string to dump, could be NULL
|
||||
*/
|
||||
int HeapDumpString(FILE* file, char* str)
|
||||
{
|
||||
int rc = 0;
|
||||
size_t len = str ? strlen(str) + 1 : 0; /* include the trailing null */
|
||||
|
||||
if (fwrite(&(str), sizeof(char*), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(&(len), sizeof(int), 1 ,file) != 1)
|
||||
rc = -1;
|
||||
else if (len > 0 && fwrite(str, len, 1, file) != 1)
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump the state of the heap
|
||||
* @param file file handle to dump the heap contents to
|
||||
*/
|
||||
int HeapDump(FILE* file)
|
||||
{
|
||||
int rc = 0;
|
||||
Node* current = NULL;
|
||||
|
||||
while (rc == 0 && (current = TreeNextElement(&heap, current)))
|
||||
{
|
||||
storageElement* s = (storageElement*)(current->content);
|
||||
|
||||
if (fwrite(&(s->ptr), sizeof(s->ptr), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(&(current->size), sizeof(current->size), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(s->ptr, current->size, 1, file) != 1)
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(HEAP_UNIT_TESTS)
|
||||
|
||||
void Log(enum LOG_LEVELS log_level, int msgno, char* format, ...)
|
||||
{
|
||||
printf("Log %s", format);
|
||||
}
|
||||
|
||||
char* Broker_recordFFDC(char* symptoms)
|
||||
{
|
||||
printf("recordFFDC");
|
||||
return "";
|
||||
}
|
||||
|
||||
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
|
||||
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
|
||||
#define free(x) myfree(__FILE__, __LINE__, x)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char* h = NULL;
|
||||
Heap_initialize();
|
||||
|
||||
h = malloc(12);
|
||||
free(h);
|
||||
printf("freed h\n");
|
||||
|
||||
h = malloc(12);
|
||||
h = realloc(h, 14);
|
||||
h = realloc(h, 25);
|
||||
h = realloc(h, 255);
|
||||
h = realloc(h, 2225);
|
||||
h = realloc(h, 22225);
|
||||
printf("freeing h\n");
|
||||
free(h);
|
||||
Heap_terminate();
|
||||
printf("Finishing\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HEAP_UNIT_TESTS */
|
||||
|
||||
/* Local Variables: */
|
||||
/* indent-tabs-mode: t */
|
||||
/* c-basic-offset: 8 */
|
||||
/* End: */
|
||||
90
3rd/paho.mqtt.c/src/Heap.h
Normal file
90
3rd/paho.mqtt.c/src/Heap.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2020 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - use tree data structure instead of list
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if !defined(HEAP_H)
|
||||
#define HEAP_H
|
||||
|
||||
#if defined(HIGH_PERFORMANCE)
|
||||
#define NO_HEAP_TRACKING 1
|
||||
#endif
|
||||
|
||||
#define PAHO_MEMORY_ERROR -99
|
||||
|
||||
#include "MQTTExportDeclarations.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
|
||||
#if !defined(TREE_C)
|
||||
/**
|
||||
* redefines malloc to use "mymalloc" so that heap allocation can be tracked
|
||||
* @param x the size of the item to be allocated
|
||||
* @return the pointer to the item allocated, or NULL
|
||||
*/
|
||||
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
|
||||
|
||||
/**
|
||||
* redefines realloc to use "myrealloc" so that heap allocation can be tracked
|
||||
* @param a the heap item to be reallocated
|
||||
* @param b the new size of the item
|
||||
* @return the new pointer to the heap item
|
||||
*/
|
||||
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
|
||||
|
||||
/**
|
||||
* redefines free to use "myfree" so that heap allocation can be tracked
|
||||
* @param x the pointer to the item to be freed
|
||||
*/
|
||||
#define free(x) myfree(__FILE__, __LINE__, x)
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Information about the state of the heap.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
size_t current_size; /**< current size of the heap in bytes */
|
||||
size_t max_size; /**< max size the heap has reached in bytes */
|
||||
} heap_info;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void* mymalloc(char*, int, size_t size);
|
||||
void* myrealloc(char*, int, void* p, size_t size);
|
||||
void myfree(char*, int, void* p);
|
||||
|
||||
void Heap_scan(FILE* file);
|
||||
int Heap_initialize(void);
|
||||
void Heap_terminate(void);
|
||||
LIBMQTT_API heap_info* Heap_get_info(void);
|
||||
int HeapDump(FILE* file);
|
||||
int HeapDumpString(FILE* file, char* str);
|
||||
void* Heap_findItem(void* p);
|
||||
void Heap_unlink(char* file, int line, void* p);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
22
3rd/paho.mqtt.c/src/Keysight_ws_add
Normal file
22
3rd/paho.mqtt.c/src/Keysight_ws_add
Normal file
@@ -0,0 +1,22 @@
|
||||
#if WINVER <= _WIN32_WINNT_WIN8
|
||||
#define HTON(x) hton((uint64_t) (x), sizeof(x))
|
||||
uint64_t hton(uint64_t x, size_t n)
|
||||
{
|
||||
uint64_t y = 0;
|
||||
size_t i = 0;
|
||||
|
||||
for (i=0; i < n; ++i)
|
||||
{
|
||||
y = (y << 8) | (x & 0xff);
|
||||
x = (x >> 8);
|
||||
}
|
||||
return y;
|
||||
}
|
||||
#define htons(x) (uint16_t) HTON(x)
|
||||
#define htonl(x) (uint32_t) HTON(x)
|
||||
#define htonll(x) (uint64_t) HTON(x)
|
||||
|
||||
#define ntohs(x) htons(x)
|
||||
#define ntohl(x) htonl(x)
|
||||
#define ntohll(x) htonll(x)
|
||||
#endif
|
||||
508
3rd/paho.mqtt.c/src/LinkedList.c
Normal file
508
3rd/paho.mqtt.c/src/LinkedList.c
Normal file
@@ -0,0 +1,508 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2020 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions which apply to linked list structures.
|
||||
*
|
||||
* These linked lists can hold data of any sort, pointed to by the content pointer of the
|
||||
* ListElement structure. ListElements hold the points to the next and previous items in the
|
||||
* list.
|
||||
* */
|
||||
|
||||
#include "LinkedList.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
|
||||
static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent);
|
||||
|
||||
|
||||
/**
|
||||
* Sets a list structure to empty - all null values. Does not remove any items from the list.
|
||||
* @param newl a pointer to the list structure to be initialized
|
||||
*/
|
||||
void ListZero(List* newl)
|
||||
{
|
||||
memset(newl, '\0', sizeof(List));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new list structure.
|
||||
* @return a pointer to the new list structure
|
||||
*/
|
||||
List* ListInitialize(void)
|
||||
{
|
||||
List* newl = malloc(sizeof(List));
|
||||
if (newl)
|
||||
ListZero(newl);
|
||||
return newl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append an already allocated ListElement and content to a list. Can be used to move
|
||||
* an item from one list to another.
|
||||
* @param aList the list to which the item is to be added
|
||||
* @param content the list item content itself
|
||||
* @param newel the ListElement to be used in adding the new item
|
||||
* @param size the size of the element
|
||||
*/
|
||||
void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size)
|
||||
{ /* for heap use */
|
||||
newel->content = content;
|
||||
newel->next = NULL;
|
||||
newel->prev = aList->last;
|
||||
if (aList->first == NULL)
|
||||
aList->first = newel;
|
||||
else
|
||||
aList->last->next = newel;
|
||||
aList->last = newel;
|
||||
++(aList->count);
|
||||
aList->size += size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append an item to a list.
|
||||
* @param aList the list to which the item is to be added
|
||||
* @param content the list item content itself
|
||||
* @param size the size of the element
|
||||
*/
|
||||
ListElement* ListAppend(List* aList, void* content, size_t size)
|
||||
{
|
||||
ListElement* newel = malloc(sizeof(ListElement));
|
||||
if (newel)
|
||||
ListAppendNoMalloc(aList, content, newel, size);
|
||||
return newel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert an item to a list at a specific position.
|
||||
* @param aList the list to which the item is to be added
|
||||
* @param content the list item content itself
|
||||
* @param size the size of the element
|
||||
* @param index the position in the list. If NULL, this function is equivalent
|
||||
* to ListAppend.
|
||||
*/
|
||||
ListElement* ListInsert(List* aList, void* content, size_t size, ListElement* index)
|
||||
{
|
||||
ListElement* newel = malloc(sizeof(ListElement));
|
||||
|
||||
if (newel == NULL)
|
||||
return newel;
|
||||
if ( index == NULL )
|
||||
ListAppendNoMalloc(aList, content, newel, size);
|
||||
else
|
||||
{
|
||||
newel->content = content;
|
||||
newel->next = index;
|
||||
newel->prev = index->prev;
|
||||
|
||||
index->prev = newel;
|
||||
if ( newel->prev != NULL )
|
||||
newel->prev->next = newel;
|
||||
else
|
||||
aList->first = newel;
|
||||
|
||||
++(aList->count);
|
||||
aList->size += size;
|
||||
}
|
||||
return newel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds an element in a list by comparing the content pointers, rather than the contents
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the list item content itself
|
||||
* @return the list item found, or NULL
|
||||
*/
|
||||
ListElement* ListFind(List* aList, void* content)
|
||||
{
|
||||
return ListFindItem(aList, content, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds an element in a list by comparing the content or pointer to the content. A callback
|
||||
* function is used to define the method of comparison for each element.
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @param callback pointer to a function which compares each element (NULL means compare by content pointer)
|
||||
* @return the list element found, or NULL
|
||||
*/
|
||||
ListElement* ListFindItem(List* aList, void* content, int(*callback)(void*, void*))
|
||||
{
|
||||
ListElement* rc = NULL;
|
||||
|
||||
if (aList->current != NULL && ((callback == NULL && aList->current->content == content) ||
|
||||
(callback != NULL && callback(aList->current->content, content))))
|
||||
rc = aList->current;
|
||||
else
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
|
||||
/* find the content */
|
||||
while (ListNextElement(aList, ¤t) != NULL)
|
||||
{
|
||||
if (callback == NULL)
|
||||
{
|
||||
if (current->content == content)
|
||||
{
|
||||
rc = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (callback(current->content, content))
|
||||
{
|
||||
rc = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc != NULL)
|
||||
aList->current = rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and optionally frees an element in a list by comparing the content.
|
||||
* A callback function is used to define the method of comparison for each element.
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @param callback pointer to a function which compares each element
|
||||
* @param freeContent boolean value to indicate whether the item found is to be freed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent)
|
||||
{
|
||||
ListElement* next = NULL;
|
||||
ListElement* saved = aList->current;
|
||||
int saveddeleted = 0;
|
||||
|
||||
if (!ListFindItem(aList, content, callback))
|
||||
return 0; /* false, did not remove item */
|
||||
|
||||
if (aList->current->prev == NULL)
|
||||
/* so this is the first element, and we have to update the "first" pointer */
|
||||
aList->first = aList->current->next;
|
||||
else
|
||||
aList->current->prev->next = aList->current->next;
|
||||
|
||||
if (aList->current->next == NULL)
|
||||
aList->last = aList->current->prev;
|
||||
else
|
||||
aList->current->next->prev = aList->current->prev;
|
||||
|
||||
next = aList->current->next;
|
||||
if (freeContent)
|
||||
{
|
||||
free(aList->current->content);
|
||||
aList->current->content = NULL;
|
||||
}
|
||||
if (saved == aList->current)
|
||||
saveddeleted = 1;
|
||||
free(aList->current);
|
||||
if (saveddeleted)
|
||||
aList->current = next;
|
||||
else
|
||||
aList->current = saved;
|
||||
--(aList->count);
|
||||
return 1; /* successfully removed item */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes but does not free an item in a list by comparing the pointer to the content.
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListDetach(List* aList, void* content)
|
||||
{
|
||||
return ListUnlink(aList, content, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an item in a list by comparing the pointer to the content.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @param content pointer to the content to look for
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListRemove(List* aList, void* content)
|
||||
{
|
||||
return ListUnlink(aList, content, NULL, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an the first item in a list.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
void* ListDetachHead(List* aList)
|
||||
{
|
||||
void *content = NULL;
|
||||
if (aList->count > 0)
|
||||
{
|
||||
ListElement* first = aList->first;
|
||||
if (aList->current == first)
|
||||
aList->current = first->next;
|
||||
if (aList->last == first) /* i.e. no of items in list == 1 */
|
||||
aList->last = NULL;
|
||||
content = first->content;
|
||||
aList->first = aList->first->next;
|
||||
if (aList->first)
|
||||
aList->first->prev = NULL;
|
||||
free(first);
|
||||
--(aList->count);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an the first item in a list.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListRemoveHead(List* aList)
|
||||
{
|
||||
free(ListDetachHead(aList));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes but does not free the last item in a list.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @return the last item removed (or NULL if none was)
|
||||
*/
|
||||
void* ListPopTail(List* aList)
|
||||
{
|
||||
void* content = NULL;
|
||||
if (aList->count > 0)
|
||||
{
|
||||
ListElement* last = aList->last;
|
||||
if (aList->current == last)
|
||||
aList->current = last->prev;
|
||||
if (aList->first == last) /* i.e. no of items in list == 1 */
|
||||
aList->first = NULL;
|
||||
content = last->content;
|
||||
aList->last = aList->last->prev;
|
||||
if (aList->last)
|
||||
aList->last->next = NULL;
|
||||
free(last);
|
||||
--(aList->count);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes but does not free an element in a list by comparing the content.
|
||||
* A callback function is used to define the method of comparison for each element.
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @param callback pointer to a function which compares each element
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListDetachItem(List* aList, void* content, int(*callback)(void*, void*))
|
||||
{ /* do not free the content */
|
||||
return ListUnlink(aList, content, callback, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an element in a list by comparing the content.
|
||||
* A callback function is used to define the method of comparison for each element
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @param callback pointer to a function which compares each element
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*))
|
||||
{ /* remove from list and free the content */
|
||||
return ListUnlink(aList, content, callback, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees all items in a list, leaving the list ready for new items.
|
||||
* @param aList the list to which the operation is to be applied
|
||||
*/
|
||||
void ListEmpty(List* aList)
|
||||
{
|
||||
while (aList->first != NULL)
|
||||
{
|
||||
ListElement* first = aList->first;
|
||||
if (first->content != NULL)
|
||||
{
|
||||
free(first->content);
|
||||
first->content = NULL;
|
||||
}
|
||||
aList->first = first->next;
|
||||
free(first);
|
||||
}
|
||||
aList->count = 0;
|
||||
aList->size = 0;
|
||||
aList->current = aList->first = aList->last = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and frees all items in a list, and frees the list itself
|
||||
* @param aList the list to which the operation is to be applied
|
||||
*/
|
||||
void ListFree(List* aList)
|
||||
{
|
||||
ListEmpty(aList);
|
||||
free(aList);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and but does not free all items in a list, and frees the list itself
|
||||
* @param aList the list to which the operation is to be applied
|
||||
*/
|
||||
void ListFreeNoContent(List* aList)
|
||||
{
|
||||
while (aList->first != NULL)
|
||||
{
|
||||
ListElement* first = aList->first;
|
||||
aList->first = first->next;
|
||||
free(first);
|
||||
}
|
||||
free(aList);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Forward iteration through a list
|
||||
* @param aList the list to which the operation is to be applied
|
||||
* @param pos pointer to the current position in the list. NULL means start from the beginning of the list
|
||||
* This is updated on return to the same value as that returned from this function
|
||||
* @return pointer to the current list element
|
||||
*/
|
||||
ListElement* ListNextElement(List* aList, ListElement** pos)
|
||||
{
|
||||
return *pos = (*pos == NULL) ? aList->first : (*pos)->next;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Backward iteration through a list
|
||||
* @param aList the list to which the operation is to be applied
|
||||
* @param pos pointer to the current position in the list. NULL means start from the end of the list
|
||||
* This is updated on return to the same value as that returned from this function
|
||||
* @return pointer to the current list element
|
||||
*/
|
||||
ListElement* ListPrevElement(List* aList, ListElement** pos)
|
||||
{
|
||||
return *pos = (*pos == NULL) ? aList->last : (*pos)->prev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing integers
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int intcompare(void* a, void* b)
|
||||
{
|
||||
return *((int*)a) == *((int*)b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing C strings
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int stringcompare(void* a, void* b)
|
||||
{
|
||||
return strcmp((char*)a, (char*)b) == 0;
|
||||
}
|
||||
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, *ip, *todelete;
|
||||
ListElement* current = NULL;
|
||||
List* l = ListInitialize();
|
||||
printf("List initialized\n");
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
ip = malloc(sizeof(int));
|
||||
*ip = i;
|
||||
ListAppend(l, (void*)ip, sizeof(int));
|
||||
if (i==5)
|
||||
todelete = ip;
|
||||
printf("List element appended %d\n", *((int*)(l->last->content)));
|
||||
}
|
||||
|
||||
printf("List contents:\n");
|
||||
current = NULL;
|
||||
while (ListNextElement(l, ¤t) != NULL)
|
||||
printf("List element: %d\n", *((int*)(current->content)));
|
||||
|
||||
printf("List contents in reverse order:\n");
|
||||
current = NULL;
|
||||
while (ListPrevElement(l, ¤t) != NULL)
|
||||
printf("List element: %d\n", *((int*)(current->content)));
|
||||
|
||||
/* if ListFindItem(l, *ip, intcompare)->content */
|
||||
|
||||
printf("List contents having deleted element %d:\n", *todelete);
|
||||
ListRemove(l, todelete);
|
||||
current = NULL;
|
||||
while (ListNextElement(l, ¤t) != NULL)
|
||||
printf("List element: %d\n", *((int*)(current->content)));
|
||||
|
||||
i = 9;
|
||||
ListRemoveItem(l, &i, intcompare);
|
||||
printf("List contents having deleted another element, %d, size now %d:\n", i, l->size);
|
||||
current = NULL;
|
||||
while (ListNextElement(l, ¤t) != NULL)
|
||||
printf("List element: %d\n", *((int*)(current->content)));
|
||||
|
||||
ListFree(l);
|
||||
printf("List freed\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
105
3rd/paho.mqtt.c/src/LinkedList.h
Normal file
105
3rd/paho.mqtt.c/src/LinkedList.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2020 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
* Ian Craggs - change size types from int to size_t
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(LINKEDLIST_H)
|
||||
#define LINKEDLIST_H
|
||||
|
||||
#include <stdlib.h> /* for size_t definition */
|
||||
|
||||
/*BE
|
||||
defm defList(T)
|
||||
|
||||
def T concat Item
|
||||
{
|
||||
at 4
|
||||
n32 ptr T concat Item suppress "next"
|
||||
at 0
|
||||
n32 ptr T concat Item suppress "prev"
|
||||
at 8
|
||||
n32 ptr T id2str(T)
|
||||
}
|
||||
|
||||
def T concat List
|
||||
{
|
||||
n32 ptr T concat Item suppress "first"
|
||||
n32 ptr T concat Item suppress "last"
|
||||
n32 ptr T concat Item suppress "current"
|
||||
n32 dec "count"
|
||||
n32 suppress "size"
|
||||
}
|
||||
endm
|
||||
|
||||
defList(INT)
|
||||
defList(STRING)
|
||||
defList(TMP)
|
||||
|
||||
BE*/
|
||||
|
||||
/**
|
||||
* Structure to hold all data for one list element
|
||||
*/
|
||||
typedef struct ListElementStruct
|
||||
{
|
||||
struct ListElementStruct *prev, /**< pointer to previous list element */
|
||||
*next; /**< pointer to next list element */
|
||||
void* content; /**< pointer to element content */
|
||||
} ListElement;
|
||||
|
||||
|
||||
/**
|
||||
* Structure to hold all data for one list
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ListElement *first, /**< first element in the list */
|
||||
*last, /**< last element in the list */
|
||||
*current; /**< current element in the list, for iteration */
|
||||
int count; /**< no of items */
|
||||
size_t size; /**< heap storage used */
|
||||
} List;
|
||||
|
||||
void ListZero(List*);
|
||||
List* ListInitialize(void);
|
||||
|
||||
ListElement* ListAppend(List* aList, void* content, size_t size);
|
||||
void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size);
|
||||
ListElement* ListInsert(List* aList, void* content, size_t size, ListElement* index);
|
||||
|
||||
int ListRemove(List* aList, void* content);
|
||||
int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*));
|
||||
void* ListDetachHead(List* aList);
|
||||
int ListRemoveHead(List* aList);
|
||||
void* ListPopTail(List* aList);
|
||||
|
||||
int ListDetach(List* aList, void* content);
|
||||
int ListDetachItem(List* aList, void* content, int(*callback)(void*, void*));
|
||||
|
||||
void ListFree(List* aList);
|
||||
void ListEmpty(List* aList);
|
||||
void ListFreeNoContent(List* aList);
|
||||
|
||||
ListElement* ListNextElement(List* aList, ListElement** pos);
|
||||
ListElement* ListPrevElement(List* aList, ListElement** pos);
|
||||
|
||||
ListElement* ListFind(List* aList, void* content);
|
||||
ListElement* ListFindItem(List* aList, void* content, int(*callback)(void*, void*));
|
||||
|
||||
int intcompare(void* a, void* b);
|
||||
int stringcompare(void* a, void* b);
|
||||
|
||||
#endif
|
||||
567
3rd/paho.mqtt.c/src/Log.c
Normal file
567
3rd/paho.mqtt.c/src/Log.c
Normal file
@@ -0,0 +1,567 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2024 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
* Ian Craggs - fix for bug #427028
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief Logging and tracing module
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Log.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include "MQTTProtocol.h"
|
||||
#include "MQTTProtocolClient.h"
|
||||
#include "Messages.h"
|
||||
#include "LinkedList.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#define GETTIMEOFDAY 1
|
||||
#else
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#if defined(GETTIMEOFDAY)
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
/**
|
||||
* _unlink mapping for linux
|
||||
*/
|
||||
#define _unlink unlink
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(min)
|
||||
#define min(A,B) ( (A) < (B) ? (A):(B))
|
||||
#endif
|
||||
|
||||
trace_settings_type trace_settings =
|
||||
{
|
||||
#if defined(HIGH_PERFORMANCE)
|
||||
LOG_ERROR,
|
||||
#else
|
||||
TRACE_MINIMUM,
|
||||
#endif
|
||||
400,
|
||||
INVALID_LEVEL
|
||||
};
|
||||
|
||||
#define MAX_FUNCTION_NAME_LENGTH 256
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#if defined(GETTIMEOFDAY)
|
||||
struct timeval ts;
|
||||
#else
|
||||
struct timeb ts;
|
||||
#endif
|
||||
int number;
|
||||
thread_id_type thread_id;
|
||||
int depth;
|
||||
char name[MAX_FUNCTION_NAME_LENGTH + 1];
|
||||
int line;
|
||||
int has_rc;
|
||||
int rc;
|
||||
enum LOG_LEVELS level;
|
||||
} traceEntry;
|
||||
|
||||
static int start_index = -1,
|
||||
next_index = 0;
|
||||
static traceEntry* trace_queue = NULL;
|
||||
static int trace_queue_size = 0;
|
||||
|
||||
static FILE* trace_destination = NULL; /**< flag to indicate if trace is to be sent to a stream */
|
||||
static char* trace_destination_name = NULL; /**< the name of the trace file */
|
||||
static char* trace_destination_backup_name = NULL; /**< the name of the backup trace file */
|
||||
static int lines_written = 0; /**< number of lines written to the current output file */
|
||||
static int max_lines_per_file = 1000; /**< maximum number of lines to write to one trace file */
|
||||
static enum LOG_LEVELS trace_output_level = INVALID_LEVEL;
|
||||
static Log_traceCallback* trace_callback = NULL;
|
||||
static traceEntry* Log_pretrace(void);
|
||||
static char* Log_formatTraceEntry(traceEntry* cur_entry);
|
||||
static void Log_output(enum LOG_LEVELS log_level, const char *msg);
|
||||
static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry);
|
||||
static void Log_trace(enum LOG_LEVELS log_level, const char *buf);
|
||||
#if 0
|
||||
static FILE* Log_destToFile(const char *dest);
|
||||
static int Log_compareEntries(const char *entry1, const char *entry2);
|
||||
#endif
|
||||
|
||||
#if defined(GETTIMEOFDAY)
|
||||
struct timeval now_ts;
|
||||
#else
|
||||
struct timeb now_ts;
|
||||
#endif
|
||||
static char msg_buf[512];
|
||||
|
||||
#if defined(_WIN32)
|
||||
mutex_type log_mutex;
|
||||
#else
|
||||
static pthread_mutex_t log_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type log_mutex = &log_mutex_store;
|
||||
#endif
|
||||
|
||||
|
||||
int Log_initialize(Log_nameValue* info)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
char* envval = NULL;
|
||||
#if !defined(_WIN32)
|
||||
struct stat buf;
|
||||
#endif
|
||||
|
||||
if ((trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries)) == NULL)
|
||||
goto exit;
|
||||
trace_queue_size = trace_settings.max_trace_entries;
|
||||
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "w")) == NULL)
|
||||
trace_destination = stdout;
|
||||
else
|
||||
{
|
||||
size_t namelen = 0;
|
||||
|
||||
if ((trace_destination_name = malloc(strlen(envval) + 1)) == NULL)
|
||||
{
|
||||
free(trace_queue);
|
||||
goto exit;
|
||||
}
|
||||
strcpy(trace_destination_name, envval);
|
||||
namelen = strlen(envval) + 3;
|
||||
if ((trace_destination_backup_name = malloc(namelen)) == NULL)
|
||||
{
|
||||
free(trace_queue);
|
||||
free(trace_destination_name);
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(trace_destination_backup_name, namelen, "%s.0", trace_destination_name) >= namelen)
|
||||
trace_destination_backup_name[namelen-1] = '\0';
|
||||
}
|
||||
}
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE_MAX_LINES")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
max_lines_per_file = atoi(envval);
|
||||
if (max_lines_per_file <= 0)
|
||||
max_lines_per_file = 1000;
|
||||
}
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE_LEVEL")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
if (strcmp(envval, "MAXIMUM") == 0 || strcmp(envval, "TRACE_MAXIMUM") == 0)
|
||||
trace_settings.trace_level = TRACE_MAXIMUM;
|
||||
else if (strcmp(envval, "MEDIUM") == 0 || strcmp(envval, "TRACE_MEDIUM") == 0)
|
||||
trace_settings.trace_level = TRACE_MEDIUM;
|
||||
else if (strcmp(envval, "MINIMUM") == 0 || strcmp(envval, "TRACE_MINIMUM") == 0)
|
||||
trace_settings.trace_level = TRACE_MINIMUM;
|
||||
else if (strcmp(envval, "PROTOCOL") == 0 || strcmp(envval, "TRACE_PROTOCOL") == 0)
|
||||
trace_output_level = TRACE_PROTOCOL;
|
||||
else if (strcmp(envval, "ERROR") == 0 || strcmp(envval, "TRACE_ERROR") == 0)
|
||||
trace_output_level = LOG_ERROR;
|
||||
}
|
||||
Log_output(TRACE_MINIMUM, "=========================================================");
|
||||
Log_output(TRACE_MINIMUM, " Trace Output");
|
||||
if (info)
|
||||
{
|
||||
while (info->name)
|
||||
{
|
||||
snprintf(msg_buf, sizeof(msg_buf), "%s: %s", info->name, info->value);
|
||||
Log_output(TRACE_MINIMUM, msg_buf);
|
||||
info++;
|
||||
}
|
||||
}
|
||||
#if !defined(_WIN32)
|
||||
if (stat("/proc/version", &buf) != -1)
|
||||
{
|
||||
FILE* vfile;
|
||||
|
||||
if ((vfile = fopen("/proc/version", "r")) != NULL)
|
||||
{
|
||||
int len;
|
||||
|
||||
strcpy(msg_buf, "/proc/version: ");
|
||||
len = strlen(msg_buf);
|
||||
if (fgets(&msg_buf[len], sizeof(msg_buf) - len, vfile))
|
||||
Log_output(TRACE_MINIMUM, msg_buf);
|
||||
fclose(vfile);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Log_output(TRACE_MINIMUM, "=========================================================");
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void Log_setTraceCallback(Log_traceCallback* callback)
|
||||
{
|
||||
trace_callback = callback;
|
||||
}
|
||||
|
||||
|
||||
void Log_setTraceLevel(enum LOG_LEVELS level)
|
||||
{
|
||||
if (level <= LOG_ERROR) /* the lowest we can go is LOG_ERROR */
|
||||
trace_settings.trace_level = level;
|
||||
trace_output_level = level;
|
||||
}
|
||||
|
||||
|
||||
void Log_terminate(void)
|
||||
{
|
||||
free(trace_queue);
|
||||
trace_queue = NULL;
|
||||
trace_queue_size = 0;
|
||||
if (trace_destination)
|
||||
{
|
||||
if (trace_destination != stdout)
|
||||
fclose(trace_destination);
|
||||
trace_destination = NULL;
|
||||
}
|
||||
if (trace_destination_name) {
|
||||
free(trace_destination_name);
|
||||
trace_destination_name = NULL;
|
||||
}
|
||||
if (trace_destination_backup_name) {
|
||||
free(trace_destination_backup_name);
|
||||
trace_destination_backup_name = NULL;
|
||||
}
|
||||
start_index = -1;
|
||||
next_index = 0;
|
||||
trace_output_level = INVALID_LEVEL;
|
||||
}
|
||||
|
||||
|
||||
static traceEntry* Log_pretrace(void)
|
||||
{
|
||||
traceEntry *cur_entry = NULL;
|
||||
|
||||
#if defined(GETTIMEOFDAY)
|
||||
gettimeofday(&now_ts, NULL);
|
||||
#else
|
||||
ftime(&now_ts);
|
||||
#endif
|
||||
|
||||
if (trace_queue_size != trace_settings.max_trace_entries)
|
||||
{
|
||||
traceEntry* new_trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries);
|
||||
|
||||
if (new_trace_queue == NULL)
|
||||
goto exit;
|
||||
memcpy(new_trace_queue, trace_queue, min(trace_queue_size, trace_settings.max_trace_entries) * sizeof(traceEntry));
|
||||
free(trace_queue);
|
||||
trace_queue = new_trace_queue;
|
||||
trace_queue_size = trace_settings.max_trace_entries;
|
||||
|
||||
if (start_index > trace_settings.max_trace_entries + 1 ||
|
||||
next_index > trace_settings.max_trace_entries + 1)
|
||||
{
|
||||
start_index = -1;
|
||||
next_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* add to trace buffer */
|
||||
cur_entry = &trace_queue[next_index];
|
||||
if (next_index == start_index) /* means the buffer is full */
|
||||
{
|
||||
if (++start_index == trace_settings.max_trace_entries)
|
||||
start_index = 0;
|
||||
} else if (start_index == -1)
|
||||
start_index = 0;
|
||||
if (++next_index == trace_settings.max_trace_entries)
|
||||
next_index = 0;
|
||||
exit:
|
||||
return cur_entry;
|
||||
}
|
||||
|
||||
static char* Log_formatTraceEntry(traceEntry* cur_entry)
|
||||
{
|
||||
struct tm *timeinfo;
|
||||
int buf_pos = 31;
|
||||
|
||||
#if defined(GETTIMEOFDAY)
|
||||
timeinfo = localtime((time_t *)&cur_entry->ts.tv_sec);
|
||||
#else
|
||||
timeinfo = localtime(&cur_entry->ts.time);
|
||||
#endif
|
||||
strftime(&msg_buf[7], 80, "%Y%m%d %H%M%S ", timeinfo);
|
||||
#if defined(GETTIMEOFDAY)
|
||||
snprintf(&msg_buf[22], sizeof(msg_buf)-22, ".%.3lu ", cur_entry->ts.tv_usec / 1000L);
|
||||
#else
|
||||
snprintf(&msg_buf[22], sizeof(msg_buf)-22, ".%.3hu ", cur_entry->ts.millitm);
|
||||
#endif
|
||||
buf_pos = 27;
|
||||
msg_buf[6] = ' ';
|
||||
|
||||
if (cur_entry->has_rc == 2)
|
||||
strncpy(&msg_buf[buf_pos], cur_entry->name, sizeof(msg_buf)-buf_pos);
|
||||
else
|
||||
{
|
||||
const char *format = Messages_get(cur_entry->number, cur_entry->level);
|
||||
if (cur_entry->has_rc == 1)
|
||||
snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
|
||||
cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line, cur_entry->rc);
|
||||
else
|
||||
snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
|
||||
cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line);
|
||||
}
|
||||
return msg_buf;
|
||||
}
|
||||
|
||||
|
||||
static void Log_output(enum LOG_LEVELS log_level, const char *msg)
|
||||
{
|
||||
if (trace_destination)
|
||||
{
|
||||
fprintf(trace_destination, "%s\n", msg);
|
||||
|
||||
if (trace_destination != stdout && ++lines_written >= max_lines_per_file)
|
||||
{
|
||||
|
||||
fclose(trace_destination);
|
||||
_unlink(trace_destination_backup_name); /* remove any old backup trace file */
|
||||
rename(trace_destination_name, trace_destination_backup_name); /* rename recently closed to backup */
|
||||
trace_destination = fopen(trace_destination_name, "w"); /* open new trace file */
|
||||
if (trace_destination == NULL)
|
||||
trace_destination = stdout;
|
||||
lines_written = 0;
|
||||
}
|
||||
else
|
||||
fflush(trace_destination);
|
||||
}
|
||||
|
||||
if (trace_callback)
|
||||
(*trace_callback)(log_level, msg);
|
||||
}
|
||||
|
||||
|
||||
static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry)
|
||||
{
|
||||
if (((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
|
||||
{
|
||||
char* msg = NULL;
|
||||
|
||||
if (trace_destination || trace_callback)
|
||||
msg = &Log_formatTraceEntry(cur_entry)[7];
|
||||
|
||||
Log_output(log_level, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Log_trace(enum LOG_LEVELS log_level, const char *buf)
|
||||
{
|
||||
traceEntry *cur_entry = NULL;
|
||||
|
||||
if (trace_queue == NULL)
|
||||
return;
|
||||
|
||||
cur_entry = Log_pretrace();
|
||||
|
||||
memcpy(&(cur_entry->ts), &now_ts, sizeof(now_ts));
|
||||
cur_entry->has_rc = 2;
|
||||
strncpy(cur_entry->name, buf, sizeof(cur_entry->name));
|
||||
cur_entry->name[MAX_FUNCTION_NAME_LENGTH] = '\0';
|
||||
|
||||
Log_posttrace(log_level, cur_entry);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log a message. If possible, all messages should be indexed by message number, and
|
||||
* the use of the format string should be minimized or negated altogether. If format is
|
||||
* provided, the message number is only used as a message label.
|
||||
* @param log_level the log level of the message
|
||||
* @param msgno the id of the message to use if the format string is NULL
|
||||
* @param aFormat the printf format string to be used if the message id does not exist
|
||||
* @param ... the printf inserts
|
||||
*/
|
||||
void Log(enum LOG_LEVELS log_level, int msgno, const char *format, ...)
|
||||
{
|
||||
if (log_level >= trace_settings.trace_level)
|
||||
{
|
||||
const char *temp = NULL;
|
||||
va_list args;
|
||||
|
||||
/* we're using a static character buffer, so we need to make sure only one thread uses it at a time */
|
||||
Paho_thread_lock_mutex(log_mutex);
|
||||
if (format == NULL && (temp = Messages_get(msgno, log_level)) != NULL)
|
||||
format = temp;
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(msg_buf, sizeof(msg_buf), format, args);
|
||||
|
||||
Log_trace(log_level, msg_buf);
|
||||
va_end(args);
|
||||
Paho_thread_unlock_mutex(log_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The reason for this function is to make trace logging as fast as possible so that the
|
||||
* function exit/entry history can be captured by default without unduly impacting
|
||||
* performance. Therefore it must do as little as possible.
|
||||
* @param log_level the log level of the message
|
||||
* @param msgno the id of the message to use if the format string is NULL
|
||||
* @param aFormat the printf format string to be used if the message id does not exist
|
||||
* @param ... the printf inserts
|
||||
*/
|
||||
void Log_stackTrace(enum LOG_LEVELS log_level, int msgno, thread_id_type thread_id, int current_depth, const char* name, int line, int* rc)
|
||||
{
|
||||
traceEntry *cur_entry = NULL;
|
||||
|
||||
if (trace_queue == NULL)
|
||||
return;
|
||||
|
||||
if (log_level < trace_settings.trace_level)
|
||||
return;
|
||||
|
||||
Paho_thread_lock_mutex(log_mutex);
|
||||
cur_entry = Log_pretrace();
|
||||
|
||||
memcpy(&(cur_entry->ts), &now_ts, sizeof(now_ts));
|
||||
cur_entry->number = msgno;
|
||||
cur_entry->thread_id = thread_id;
|
||||
cur_entry->depth = current_depth;
|
||||
strcpy(cur_entry->name, name);
|
||||
cur_entry->level = log_level;
|
||||
cur_entry->line = line;
|
||||
if (rc == NULL)
|
||||
cur_entry->has_rc = 0;
|
||||
else
|
||||
{
|
||||
cur_entry->has_rc = 1;
|
||||
cur_entry->rc = *rc;
|
||||
}
|
||||
|
||||
Log_posttrace(log_level, cur_entry);
|
||||
Paho_thread_unlock_mutex(log_mutex);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static FILE* Log_destToFile(const char *dest)
|
||||
{
|
||||
FILE* file = NULL;
|
||||
|
||||
if (strcmp(dest, "stdout") == 0)
|
||||
file = stdout;
|
||||
else if (strcmp(dest, "stderr") == 0)
|
||||
file = stderr;
|
||||
else
|
||||
{
|
||||
if (strstr(dest, "FFDC"))
|
||||
file = fopen(dest, "ab");
|
||||
else
|
||||
file = fopen(dest, "wb");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
static int Log_compareEntries(const char *entry1, const char *entry2)
|
||||
{
|
||||
int comp = strncmp(&entry1[7], &entry2[7], 19);
|
||||
|
||||
/* if timestamps are equal, use the sequence numbers */
|
||||
if (comp == 0)
|
||||
comp = strncmp(&entry1[1], &entry2[1], 4);
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write the contents of the stored trace to a stream
|
||||
* @param dest string which contains a file name or the special strings stdout or stderr
|
||||
*/
|
||||
int Log_dumpTrace(char* dest)
|
||||
{
|
||||
FILE* file = NULL;
|
||||
ListElement* cur_trace_entry = NULL;
|
||||
const int msgstart = 7;
|
||||
int rc = -1;
|
||||
int trace_queue_index = 0;
|
||||
|
||||
if ((file = Log_destToFile(dest)) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 9, NULL, "trace", dest, "trace entries");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fprintf(file, "=========== Start of trace dump ==========\n");
|
||||
/* Interleave the log and trace entries together appropriately */
|
||||
ListNextElement(trace_buffer, &cur_trace_entry);
|
||||
trace_queue_index = start_index;
|
||||
if (trace_queue_index == -1)
|
||||
trace_queue_index = next_index;
|
||||
else
|
||||
{
|
||||
Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
|
||||
if (trace_queue_index == trace_settings.max_trace_entries)
|
||||
trace_queue_index = 0;
|
||||
}
|
||||
while (cur_trace_entry || trace_queue_index != next_index)
|
||||
{
|
||||
if (cur_trace_entry && trace_queue_index != -1)
|
||||
{ /* compare these timestamps */
|
||||
if (Log_compareEntries((char*)cur_trace_entry->content, msg_buf) > 0)
|
||||
cur_trace_entry = NULL;
|
||||
}
|
||||
|
||||
if (cur_trace_entry)
|
||||
{
|
||||
fprintf(file, "%s\n", &((char*)(cur_trace_entry->content))[msgstart]);
|
||||
ListNextElement(trace_buffer, &cur_trace_entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "%s\n", &msg_buf[7]);
|
||||
if (trace_queue_index != next_index)
|
||||
{
|
||||
Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
|
||||
if (trace_queue_index == trace_settings.max_trace_entries)
|
||||
trace_queue_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(file, "========== End of trace dump ==========\n\n");
|
||||
if (file != stdout && file != stderr && file != NULL)
|
||||
fclose(file);
|
||||
rc = 0;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
93
3rd/paho.mqtt.c/src/Log.h
Normal file
93
3rd/paho.mqtt.c/src/Log.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(LOG_H)
|
||||
#define LOG_H
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#define thread_id_type DWORD
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#define thread_id_type pthread_t
|
||||
#endif
|
||||
|
||||
/*BE
|
||||
map LOG_LEVELS
|
||||
{
|
||||
"TRACE_MAXIMUM" 1
|
||||
"TRACE_MEDIUM" 2
|
||||
"TRACE_MINIMUM" 3
|
||||
"TRACE_PROTOCOL" 4
|
||||
|
||||
"ERROR" 5
|
||||
"SEVERE" 6
|
||||
"FATAL" 7
|
||||
}
|
||||
BE*/
|
||||
|
||||
enum LOG_LEVELS {
|
||||
INVALID_LEVEL = -1,
|
||||
TRACE_MAXIMUM = 1,
|
||||
TRACE_MEDIUM,
|
||||
TRACE_MINIMUM,
|
||||
TRACE_PROTOCOL,
|
||||
LOG_ERROR,
|
||||
LOG_SEVERE,
|
||||
LOG_FATAL,
|
||||
};
|
||||
|
||||
|
||||
/*BE
|
||||
def trace_settings_type
|
||||
{
|
||||
n32 map LOG_LEVELS "trace_level"
|
||||
n32 dec "max_trace_entries"
|
||||
n32 dec "trace_output_level"
|
||||
}
|
||||
BE*/
|
||||
typedef struct
|
||||
{
|
||||
enum LOG_LEVELS trace_level; /**< trace level */
|
||||
int max_trace_entries; /**< max no of entries in the trace buffer */
|
||||
enum LOG_LEVELS trace_output_level; /**< trace level to output to destination */
|
||||
} trace_settings_type;
|
||||
|
||||
extern trace_settings_type trace_settings;
|
||||
|
||||
#define LOG_PROTOCOL TRACE_PROTOCOL
|
||||
#define TRACE_MAX TRACE_MAXIMUM
|
||||
#define TRACE_MIN TRACE_MINIMUM
|
||||
#define TRACE_MED TRACE_MEDIUM
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* value;
|
||||
} Log_nameValue;
|
||||
|
||||
int Log_initialize(Log_nameValue*);
|
||||
void Log_terminate(void);
|
||||
|
||||
void Log(enum LOG_LEVELS, int, const char *, ...);
|
||||
void Log_stackTrace(enum LOG_LEVELS, int, thread_id_type, int, const char*, int, int*);
|
||||
|
||||
typedef void Log_traceCallback(enum LOG_LEVELS level, const char *message);
|
||||
void Log_setTraceCallback(Log_traceCallback* callback);
|
||||
void Log_setTraceLevel(enum LOG_LEVELS level);
|
||||
|
||||
#endif
|
||||
1955
3rd/paho.mqtt.c/src/MQTTAsync.c
Normal file
1955
3rd/paho.mqtt.c/src/MQTTAsync.c
Normal file
@@ -0,0 +1,1955 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2026 IBM Corp., Ian Craggs and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial implementation and documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL support
|
||||
* Ian Craggs - multiple server connection support
|
||||
* Ian Craggs - fix for bug 413429 - connectionLost not called
|
||||
* Ian Craggs - fix for bug 415042 - using already freed structure
|
||||
* Ian Craggs - fix for bug 419233 - mutexes not reporting errors
|
||||
* Ian Craggs - fix for bug 420851
|
||||
* Ian Craggs - fix for bug 432903 - queue persistence
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Rong Xiang, Ian Craggs - C++ compatibility
|
||||
* Ian Craggs - fix for bug 442400: reconnecting after network cable unplugged
|
||||
* Ian Craggs - fix for bug 444934 - incorrect free in freeCommand1
|
||||
* Ian Craggs - fix for bug 445891 - assigning msgid is not thread safe
|
||||
* Ian Craggs - fix for bug 465369 - longer latency than expected
|
||||
* Ian Craggs - fix for bug 444103 - success/failure callbacks not invoked
|
||||
* Ian Craggs - fix for bug 484363 - segfault in getReadySocket
|
||||
* Ian Craggs - automatic reconnect and offline buffering (send while disconnected)
|
||||
* Ian Craggs - fix for bug 472250
|
||||
* Ian Craggs - fix for bug 486548
|
||||
* Ian Craggs - SNI support
|
||||
* Ian Craggs - auto reconnect timing fix #218
|
||||
* Ian Craggs - fix for issue #190
|
||||
* Ian Craggs - check for NULL SSL options #334
|
||||
* Ian Craggs - allocate username/password buffers #431
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
* Ian Craggs - refactor to reduce module size
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
#include "MQTTPersistence.h"
|
||||
#endif
|
||||
#include "MQTTAsync.h"
|
||||
#include "MQTTAsyncUtils.h"
|
||||
#include "utf-8.h"
|
||||
#include "MQTTProtocol.h"
|
||||
#include "MQTTProtocolOut.h"
|
||||
#include "Thread.h"
|
||||
#include "SocketBuffer.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Heap.h"
|
||||
#include "OsWrapper.h"
|
||||
#include "WebSocket.h"
|
||||
|
||||
static void MQTTAsync_freeServerURIs(MQTTAsyncs* m);
|
||||
|
||||
#include "VersionInfo.h"
|
||||
|
||||
const char *client_timestamp_eye = "MQTTAsyncV3_Timestamp " BUILD_TIMESTAMP;
|
||||
const char *client_version_eye = "MQTTAsyncV3_Version " CLIENT_VERSION;
|
||||
|
||||
volatile int global_initialized = 0;
|
||||
List* MQTTAsync_handles = NULL;
|
||||
List* MQTTAsync_commands = NULL;
|
||||
int MQTTAsync_tostop = 0;
|
||||
|
||||
static ClientStates ClientState =
|
||||
{
|
||||
CLIENT_VERSION, /* version */
|
||||
NULL /* client list */
|
||||
};
|
||||
|
||||
MQTTProtocol state;
|
||||
ClientStates* bstate = &ClientState;
|
||||
|
||||
enum MQTTAsync_threadStates sendThread_state = STOPPED;
|
||||
enum MQTTAsync_threadStates receiveThread_state = STOPPED;
|
||||
thread_id_type sendThread_id = 0,
|
||||
receiveThread_id = 0;
|
||||
|
||||
// global objects init declaration
|
||||
int MQTTAsync_init(void);
|
||||
|
||||
void MQTTAsync_global_init(MQTTAsync_init_options* inits)
|
||||
{
|
||||
MQTTAsync_init();
|
||||
#if defined(OPENSSL)
|
||||
SSLSocket_handleOpensslInit(inits->do_openssl_init);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(min)
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
void MQTTAsync_init_rand(void)
|
||||
{
|
||||
START_TIME_TYPE now = MQTTTime_start_clock();
|
||||
srand((unsigned int)now);
|
||||
}
|
||||
#elif defined(AIX)
|
||||
void MQTTAsync_init_rand(void)
|
||||
{
|
||||
START_TIME_TYPE now = MQTTTime_start_clock();
|
||||
srand(now.tv_nsec);
|
||||
}
|
||||
#else
|
||||
void MQTTAsync_init_rand(void)
|
||||
{
|
||||
START_TIME_TYPE now = MQTTTime_start_clock();
|
||||
srand(now.tv_usec);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
mutex_type mqttasync_mutex = NULL;
|
||||
mutex_type socket_mutex = NULL;
|
||||
mutex_type mqttcommand_mutex = NULL;
|
||||
evt_type send_evt = NULL;
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
extern mutex_type stack_mutex;
|
||||
extern mutex_type heap_mutex;
|
||||
#endif
|
||||
extern mutex_type log_mutex;
|
||||
|
||||
int MQTTAsync_init(void)
|
||||
{
|
||||
DWORD rc = 0;
|
||||
|
||||
if (mqttasync_mutex == NULL)
|
||||
{
|
||||
mqttasync_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("mqttasync_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
mqttcommand_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("mqttcommand_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
send_evt = Thread_create_evt(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("send_evt error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
stack_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("stack_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
heap_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("heap_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
log_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("log_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
socket_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("socket_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(TRACE_MAX, -1, "Library already initialized");
|
||||
}
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void MQTTAsync_cleanup(void)
|
||||
{
|
||||
if (send_evt)
|
||||
Thread_destroy_evt(send_evt);
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
if (stack_mutex)
|
||||
Paho_thread_destroy_mutex(stack_mutex);
|
||||
if (heap_mutex)
|
||||
Paho_thread_destroy_mutex(heap_mutex);
|
||||
#endif
|
||||
if (log_mutex)
|
||||
Paho_thread_destroy_mutex(log_mutex);
|
||||
if (socket_mutex)
|
||||
Paho_thread_destroy_mutex(socket_mutex);
|
||||
if (mqttasync_mutex)
|
||||
Paho_thread_destroy_mutex(mqttasync_mutex);
|
||||
}
|
||||
|
||||
#if defined(PAHO_MQTT_STATIC)
|
||||
static INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT; /* Global for one time initialization */
|
||||
|
||||
/* This runs at most once */
|
||||
BOOL CALLBACK InitMutexesOnce (
|
||||
PINIT_ONCE InitOnce, /* Pointer to one-time initialization structure */
|
||||
PVOID Parameter, /* Optional parameter */
|
||||
PVOID *lpContext) /* Return data, if any */
|
||||
{
|
||||
int rc = MQTTAsync_init();
|
||||
return rc == 0;
|
||||
}
|
||||
#else
|
||||
BOOL APIENTRY DllMain(HANDLE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
MQTTAsync_init();
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
break;
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (lpReserved)
|
||||
MQTTAsync_cleanup();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#else
|
||||
static pthread_mutex_t mqttasync_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
mutex_type mqttasync_mutex = &mqttasync_mutex_store;
|
||||
|
||||
static pthread_mutex_t socket_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
mutex_type socket_mutex = &socket_mutex_store;
|
||||
|
||||
static pthread_mutex_t mqttcommand_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
mutex_type mqttcommand_mutex = &mqttcommand_mutex_store;
|
||||
|
||||
static evt_type_struct send_evt_store = { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 0 };
|
||||
evt_type send_evt = &send_evt_store;
|
||||
|
||||
int MQTTAsync_init(void)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
int rc;
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
#if !defined(_WRS_KERNEL)
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
|
||||
#else
|
||||
/* #warning "no pthread_mutexattr_settype" */
|
||||
#endif
|
||||
if ((rc = pthread_mutex_init(mqttasync_mutex, &attr)) != 0)
|
||||
printf("MQTTAsync: error %d initializing async_mutex\n", rc);
|
||||
else if ((rc = pthread_mutex_init(mqttcommand_mutex, &attr)) != 0)
|
||||
printf("MQTTAsync: error %d initializing command_mutex\n", rc);
|
||||
else if ((rc = pthread_mutex_init(socket_mutex, &attr)) != 0)
|
||||
printf("MQTTClient: error %d initializing socket_mutex\n", rc);
|
||||
else if ((rc = pthread_cond_init(&send_evt->cond, NULL)) != 0)
|
||||
printf("MQTTAsync: error %d initializing send_evt cond\n", rc);
|
||||
else if ((rc = pthread_mutex_init(&send_evt->mutex, &attr)) != 0)
|
||||
printf("MQTTAsync: error %d initializing send_evt mutex\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int MQTTAsync_createWithOptions(MQTTAsync* handle, const char* serverURI, const char* clientId,
|
||||
int persistence_type, void* persistence_context, MQTTAsync_createOptions* options)
|
||||
{
|
||||
int rc = 0;
|
||||
MQTTAsyncs *m = NULL;
|
||||
|
||||
#if (defined(_WIN32)) && defined(PAHO_MQTT_STATIC)
|
||||
/* intializes mutexes once. Must come before FUNC_ENTRY */
|
||||
BOOL bStatus = InitOnceExecuteOnce(&g_InitOnce, InitMutexesOnce, NULL, NULL);
|
||||
#endif
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (serverURI == NULL || clientId == NULL)
|
||||
{
|
||||
rc = MQTTASYNC_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!UTF8_validateString(clientId))
|
||||
{
|
||||
rc = MQTTASYNC_BAD_UTF8_STRING;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strlen(clientId) == 0 && persistence_type == MQTTCLIENT_PERSISTENCE_DEFAULT)
|
||||
{
|
||||
rc = MQTTASYNC_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strstr(serverURI, "://") != NULL)
|
||||
{
|
||||
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) != 0
|
||||
&& strncmp(URI_MQTT, serverURI, strlen(URI_MQTT)) != 0
|
||||
#if defined(UNIXSOCK)
|
||||
&& strncmp(URI_UNIX, serverURI, strlen(URI_UNIX)) != 0
|
||||
#endif
|
||||
&& strncmp(URI_WS, serverURI, strlen(URI_WS)) != 0
|
||||
#if defined(OPENSSL)
|
||||
&& strncmp(URI_SSL, serverURI, strlen(URI_SSL)) != 0
|
||||
&& strncmp(URI_TLS, serverURI, strlen(URI_TLS)) != 0
|
||||
&& strncmp(URI_MQTTS, serverURI, strlen(URI_MQTTS)) != 0
|
||||
&& strncmp(URI_WSS, serverURI, strlen(URI_WSS)) != 0
|
||||
#endif
|
||||
)
|
||||
{
|
||||
rc = MQTTASYNC_BAD_PROTOCOL;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (options && options->maxBufferedMessages <= 0)
|
||||
{
|
||||
rc = MQTTASYNC_MAX_BUFFERED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (options && (strncmp(options->struct_id, "MQCO", 4) != 0 ||
|
||||
options->struct_version < 0 || options->struct_version > 3))
|
||||
{
|
||||
rc = MQTTASYNC_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!global_initialized)
|
||||
{
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
Heap_initialize();
|
||||
#endif
|
||||
Log_initialize((Log_nameValue*)MQTTAsync_getVersionInfo());
|
||||
bstate->clients = ListInitialize();
|
||||
Socket_outInitialize();
|
||||
Socket_setWriteContinueCallback(MQTTAsync_writeContinue);
|
||||
Socket_setWriteCompleteCallback(MQTTAsync_writeComplete);
|
||||
Socket_setWriteAvailableCallback(MQTTProtocol_writeAvailable);
|
||||
MQTTAsync_handles = ListInitialize();
|
||||
MQTTAsync_commands = ListInitialize();
|
||||
#if defined(OPENSSL)
|
||||
SSLSocket_initialize();
|
||||
#endif
|
||||
global_initialized = 1;
|
||||
}
|
||||
if ((m = malloc(sizeof(MQTTAsyncs))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
*handle = m;
|
||||
memset(m, '\0', sizeof(MQTTAsyncs));
|
||||
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
|
||||
serverURI += strlen(URI_TCP);
|
||||
else if (strncmp(URI_MQTT, serverURI, strlen(URI_MQTT)) == 0)
|
||||
serverURI += strlen(URI_MQTT);
|
||||
#if defined(UNIXSOCK)
|
||||
else if (strncmp(URI_UNIX, serverURI, strlen(URI_UNIX)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_UNIX);
|
||||
m->unixsock = 1;
|
||||
}
|
||||
#endif
|
||||
else if (strncmp(URI_WS, serverURI, strlen(URI_WS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WS);
|
||||
m->websocket = 1;
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_SSL);
|
||||
m->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_TLS, serverURI, strlen(URI_TLS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_TLS);
|
||||
m->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_MQTTS, serverURI, strlen(URI_MQTTS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_MQTTS);
|
||||
m->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_WSS, serverURI, strlen(URI_WSS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WSS);
|
||||
m->ssl = 1;
|
||||
m->websocket = 1;
|
||||
}
|
||||
#endif
|
||||
if ((m->serverURI = MQTTStrdup(serverURI)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
m->responses = ListInitialize();
|
||||
ListAppend(MQTTAsync_handles, m, sizeof(MQTTAsyncs));
|
||||
|
||||
if ((m->c = malloc(sizeof(Clients))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(m->c, '\0', sizeof(Clients));
|
||||
m->c->context = m;
|
||||
m->c->outboundMsgs = ListInitialize();
|
||||
m->c->inboundMsgs = ListInitialize();
|
||||
m->c->messageQueue = ListInitialize();
|
||||
m->c->outboundQueue = ListInitialize();
|
||||
m->c->clientID = MQTTStrdup(clientId);
|
||||
if (m->c->context == NULL || m->c->outboundMsgs == NULL || m->c->inboundMsgs == NULL ||
|
||||
m->c->messageQueue == NULL || m->c->outboundQueue == NULL || m->c->clientID == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
m->c->MQTTVersion = MQTTVERSION_DEFAULT;
|
||||
|
||||
m->shouldBeConnected = 0;
|
||||
if (options)
|
||||
{
|
||||
if ((m->createOptions = malloc(sizeof(MQTTAsync_createOptions))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(m->createOptions, options, sizeof(MQTTAsync_createOptions));
|
||||
if (options->struct_version > 0)
|
||||
m->c->MQTTVersion = options->MQTTVersion;
|
||||
}
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
rc = MQTTPersistence_create(&(m->c->persistence), persistence_type, persistence_context);
|
||||
if (rc == 0)
|
||||
{
|
||||
rc = MQTTPersistence_initialize(m->c, m->serverURI); /* inflight messages restored here */
|
||||
if (rc == 0)
|
||||
{
|
||||
if (m->createOptions && m->createOptions->struct_version >= 2 && m->createOptions->restoreMessages == 0)
|
||||
MQTTAsync_unpersistCommandsAndMessages(m->c);
|
||||
else
|
||||
{
|
||||
MQTTAsync_restoreCommands(m);
|
||||
MQTTPersistence_restoreMessageQueue(m->c);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ListAppend(bstate->clients, m->c, sizeof(Clients) + 3*sizeof(List));
|
||||
|
||||
exit:
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_create(MQTTAsync* handle, const char* serverURI, const char* clientId,
|
||||
int persistence_type, void* persistence_context)
|
||||
{
|
||||
MQTTAsync_init_rand();
|
||||
|
||||
return MQTTAsync_createWithOptions(handle, serverURI, clientId, persistence_type,
|
||||
persistence_context, NULL);
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_destroy(MQTTAsync* handle)
|
||||
{
|
||||
MQTTAsyncs* m = *handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL)
|
||||
goto exit;
|
||||
|
||||
MQTTAsync_closeSession(m->c, MQTTREASONCODE_SUCCESS, NULL);
|
||||
|
||||
MQTTAsync_NULLPublishResponses(m);
|
||||
MQTTAsync_freeResponses(m);
|
||||
MQTTAsync_freeCommands(m);
|
||||
ListFree(m->responses);
|
||||
|
||||
if (m->c)
|
||||
{
|
||||
SOCKET saved_socket = m->c->net.socket;
|
||||
char* saved_clientid = MQTTStrdup(m->c->clientID);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
MQTTPersistence_close(m->c);
|
||||
#endif
|
||||
MQTTAsync_emptyMessageQueue(m->c);
|
||||
MQTTProtocol_freeClient(m->c);
|
||||
if (!ListRemove(bstate->clients, m->c))
|
||||
Log(LOG_ERROR, 0, NULL);
|
||||
else
|
||||
Log(TRACE_MIN, 1, NULL, saved_clientid, saved_socket);
|
||||
free(saved_clientid);
|
||||
}
|
||||
|
||||
if (m->serverURI)
|
||||
free(m->serverURI);
|
||||
if (m->createOptions)
|
||||
free(m->createOptions);
|
||||
MQTTAsync_freeServerURIs(m);
|
||||
if (m->connectProps)
|
||||
{
|
||||
MQTTProperties_free(m->connectProps);
|
||||
free(m->connectProps);
|
||||
m->connectProps = NULL;
|
||||
}
|
||||
if (m->willProps)
|
||||
{
|
||||
MQTTProperties_free(m->willProps);
|
||||
free(m->willProps);
|
||||
m->willProps = NULL;
|
||||
}
|
||||
if (!ListRemove(MQTTAsync_handles, m))
|
||||
Log(LOG_ERROR, -1, "free error");
|
||||
*handle = NULL;
|
||||
if (bstate->clients->count == 0)
|
||||
MQTTAsync_terminate();
|
||||
|
||||
exit:
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options)
|
||||
{
|
||||
MQTTAsyncs* m = handle;
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsync_queuedCommand* conn;
|
||||
thread_id_type thread_id = 0;
|
||||
int locked = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (options == NULL)
|
||||
{
|
||||
rc = MQTTASYNC_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strncmp(options->struct_id, "MQTC", 4) != 0 || options->struct_version < 0 || options->struct_version > 8)
|
||||
{
|
||||
rc = MQTTASYNC_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (m->ssl && options->ssl == NULL)
|
||||
{
|
||||
rc = MQTTASYNC_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
if (options->ssl == NULL && options->serverURIcount > 0)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < options->serverURIcount; i++)
|
||||
{
|
||||
char* serverURI = options->serverURIs[i];
|
||||
printf("checking %s\n", serverURI);
|
||||
if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0 ||
|
||||
strncmp(URI_TLS, serverURI, strlen(URI_TLS)) == 0 ||
|
||||
strncmp(URI_MQTTS, serverURI, strlen(URI_MQTTS)) == 0 ||
|
||||
strncmp(URI_WSS, serverURI, strlen(URI_WSS)) == 0)
|
||||
{
|
||||
rc = MQTTASYNC_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (options->will) /* check validity of will options structure */
|
||||
{
|
||||
if (strncmp(options->will->struct_id, "MQTW", 4) != 0 || (options->will->struct_version != 0 && options->will->struct_version != 1))
|
||||
{
|
||||
rc = MQTTASYNC_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
if (options->will->qos < 0 || options->will->qos > 2)
|
||||
{
|
||||
rc = MQTTASYNC_BAD_QOS;
|
||||
goto exit;
|
||||
}
|
||||
if (options->will->topicName == NULL)
|
||||
{
|
||||
rc = MQTTASYNC_NULL_PARAMETER;
|
||||
goto exit;
|
||||
} else if (strlen(options->will->topicName) == 0)
|
||||
{
|
||||
rc = MQTTASYNC_0_LEN_WILL_TOPIC;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */
|
||||
{
|
||||
if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 5)
|
||||
{
|
||||
rc = MQTTASYNC_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (options->MQTTVersion >= MQTTVERSION_5 && m->c->MQTTVersion < MQTTVERSION_5)
|
||||
{
|
||||
rc = MQTTASYNC_WRONG_MQTT_VERSION;
|
||||
goto exit;
|
||||
}
|
||||
if ((options->username && !UTF8_validateString(options->username)) ||
|
||||
(options->password && !UTF8_validateString(options->password)))
|
||||
{
|
||||
rc = MQTTASYNC_BAD_UTF8_STRING;
|
||||
goto exit;
|
||||
}
|
||||
if (options->MQTTVersion >= MQTTVERSION_5 && options->struct_version < 6)
|
||||
{
|
||||
rc = MQTTASYNC_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
if (options->MQTTVersion >= MQTTVERSION_5 && options->cleansession != 0)
|
||||
{
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
goto exit;
|
||||
}
|
||||
if (options->MQTTVersion < MQTTVERSION_5 && options->struct_version >= 6)
|
||||
{
|
||||
if (options->cleanstart != 0 || options->onFailure5 || options->onSuccess5 ||
|
||||
options->connectProperties || options->willProperties)
|
||||
{
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
m->connect.onSuccess = options->onSuccess;
|
||||
m->connect.onFailure = options->onFailure;
|
||||
if (options->struct_version >= 6)
|
||||
{
|
||||
m->connect.onSuccess5 = options->onSuccess5;
|
||||
m->connect.onFailure5 = options->onFailure5;
|
||||
}
|
||||
m->connect.context = options->context;
|
||||
m->connectTimeout = options->connectTimeout;
|
||||
|
||||
/* don't lock async mutex if we are being called from a callback */
|
||||
thread_id = Paho_thread_getid();
|
||||
if (thread_id != sendThread_id && thread_id != receiveThread_id)
|
||||
{
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
locked = 1;
|
||||
}
|
||||
MQTTAsync_tostop = 0;
|
||||
if (sendThread_state != STARTING && sendThread_state != RUNNING)
|
||||
{
|
||||
sendThread_state = STARTING;
|
||||
Paho_thread_start(MQTTAsync_sendThread, NULL);
|
||||
}
|
||||
if (receiveThread_state != STARTING && receiveThread_state != RUNNING)
|
||||
{
|
||||
receiveThread_state = STARTING;
|
||||
Paho_thread_start(MQTTAsync_receiveThread, handle);
|
||||
}
|
||||
if (locked)
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
|
||||
m->c->keepAliveInterval = m->c->savedKeepAliveInterval = options->keepAliveInterval;
|
||||
setRetryLoopInterval(options->keepAliveInterval);
|
||||
m->c->cleansession = options->cleansession;
|
||||
m->c->maxInflightMessages = options->maxInflight;
|
||||
if (options->struct_version >= 3)
|
||||
m->c->MQTTVersion = options->MQTTVersion;
|
||||
else
|
||||
m->c->MQTTVersion = MQTTVERSION_DEFAULT;
|
||||
if (options->struct_version >= 4)
|
||||
{
|
||||
m->automaticReconnect = options->automaticReconnect;
|
||||
m->minRetryInterval = options->minRetryInterval;
|
||||
m->maxRetryInterval = options->maxRetryInterval;
|
||||
}
|
||||
if (options->struct_version >= 7)
|
||||
{
|
||||
m->c->net.httpHeaders = (const MQTTClient_nameValue *) options->httpHeaders;
|
||||
}
|
||||
if (options->struct_version >= 8)
|
||||
{
|
||||
if (options->httpProxy)
|
||||
m->c->httpProxy = MQTTStrdup(options->httpProxy);
|
||||
if (options->httpsProxy)
|
||||
m->c->httpsProxy = MQTTStrdup(options->httpsProxy);
|
||||
}
|
||||
|
||||
if (m->c->will)
|
||||
{
|
||||
free(m->c->will->payload);
|
||||
free(m->c->will->topic);
|
||||
free(m->c->will);
|
||||
m->c->will = NULL;
|
||||
}
|
||||
|
||||
if (options->will && (options->will->struct_version == 0 || options->will->struct_version == 1))
|
||||
{
|
||||
const void* source = NULL;
|
||||
|
||||
if ((m->c->will = malloc(sizeof(willMessages))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (options->will->message || (options->will->struct_version == 1 && options->will->payload.data))
|
||||
{
|
||||
if (options->will->struct_version == 1 && options->will->payload.data)
|
||||
{
|
||||
m->c->will->payloadlen = options->will->payload.len;
|
||||
source = options->will->payload.data;
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->will->payloadlen = (int)strlen(options->will->message);
|
||||
source = (void*)options->will->message;
|
||||
}
|
||||
if ((m->c->will->payload = malloc(m->c->will->payloadlen)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(m->c->will->payload, source, m->c->will->payloadlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->will->payload = NULL;
|
||||
m->c->will->payloadlen = 0;
|
||||
}
|
||||
m->c->will->qos = options->will->qos;
|
||||
m->c->will->retained = options->will->retained;
|
||||
m->c->will->topic = MQTTStrdup(options->will->topicName);
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (m->c->sslopts)
|
||||
{
|
||||
if (m->c->sslopts->trustStore)
|
||||
free((void*)m->c->sslopts->trustStore);
|
||||
if (m->c->sslopts->keyStore)
|
||||
free((void*)m->c->sslopts->keyStore);
|
||||
if (m->c->sslopts->privateKey)
|
||||
free((void*)m->c->sslopts->privateKey);
|
||||
if (m->c->sslopts->privateKeyPassword)
|
||||
free((void*)m->c->sslopts->privateKeyPassword);
|
||||
if (m->c->sslopts->enabledCipherSuites)
|
||||
free((void*)m->c->sslopts->enabledCipherSuites);
|
||||
if (m->c->sslopts->struct_version >= 2)
|
||||
{
|
||||
if (m->c->sslopts->CApath)
|
||||
free((void*)m->c->sslopts->CApath);
|
||||
}
|
||||
free((void*)m->c->sslopts);
|
||||
m->c->sslopts = NULL;
|
||||
}
|
||||
|
||||
if (options->struct_version != 0 && options->ssl)
|
||||
{
|
||||
if ((m->c->sslopts = malloc(sizeof(MQTTClient_SSLOptions))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(m->c->sslopts, '\0', sizeof(MQTTClient_SSLOptions));
|
||||
m->c->sslopts->struct_version = options->ssl->struct_version;
|
||||
if (options->ssl->trustStore)
|
||||
m->c->sslopts->trustStore = MQTTStrdup(options->ssl->trustStore);
|
||||
if (options->ssl->keyStore)
|
||||
m->c->sslopts->keyStore = MQTTStrdup(options->ssl->keyStore);
|
||||
if (options->ssl->privateKey)
|
||||
m->c->sslopts->privateKey = MQTTStrdup(options->ssl->privateKey);
|
||||
if (options->ssl->privateKeyPassword)
|
||||
m->c->sslopts->privateKeyPassword = MQTTStrdup(options->ssl->privateKeyPassword);
|
||||
if (options->ssl->enabledCipherSuites)
|
||||
m->c->sslopts->enabledCipherSuites = MQTTStrdup(options->ssl->enabledCipherSuites);
|
||||
m->c->sslopts->enableServerCertAuth = options->ssl->enableServerCertAuth;
|
||||
if (m->c->sslopts->struct_version >= 1)
|
||||
m->c->sslopts->sslVersion = options->ssl->sslVersion;
|
||||
if (m->c->sslopts->struct_version >= 2)
|
||||
{
|
||||
m->c->sslopts->verify = options->ssl->verify;
|
||||
if (options->ssl->CApath)
|
||||
m->c->sslopts->CApath = MQTTStrdup(options->ssl->CApath);
|
||||
}
|
||||
if (m->c->sslopts->struct_version >= 3)
|
||||
{
|
||||
m->c->sslopts->ssl_error_cb = options->ssl->ssl_error_cb;
|
||||
m->c->sslopts->ssl_error_context = options->ssl->ssl_error_context;
|
||||
}
|
||||
if (m->c->sslopts->struct_version >= 4)
|
||||
{
|
||||
m->c->sslopts->ssl_psk_cb = options->ssl->ssl_psk_cb;
|
||||
m->c->sslopts->ssl_psk_context = options->ssl->ssl_psk_context;
|
||||
m->c->sslopts->disableDefaultTrustStore = options->ssl->disableDefaultTrustStore;
|
||||
}
|
||||
if (m->c->sslopts->struct_version >= 5)
|
||||
{
|
||||
if (options->ssl->protos)
|
||||
m->c->sslopts->protos = (const unsigned char*)MQTTStrdup((const char*)options->ssl->protos);
|
||||
m->c->sslopts->protos_len = options->ssl->protos_len;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (options->struct_version != 0 && options->ssl)
|
||||
{
|
||||
rc = MQTTASYNC_SSL_NOT_SUPPORTED;
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m->c->username)
|
||||
{
|
||||
free((void*)m->c->username);
|
||||
m->c->username = NULL;
|
||||
}
|
||||
if (options->username)
|
||||
m->c->username = MQTTStrdup(options->username);
|
||||
if (m->c->password)
|
||||
{
|
||||
free((void*)m->c->password);
|
||||
m->c->password = NULL;
|
||||
}
|
||||
if (options->password)
|
||||
{
|
||||
m->c->password = MQTTStrdup(options->password);
|
||||
m->c->passwordlen = (int)strlen(options->password);
|
||||
}
|
||||
else if (options->struct_version >= 5 && options->binarypwd.data)
|
||||
{
|
||||
m->c->passwordlen = options->binarypwd.len;
|
||||
if ((m->c->password = malloc(m->c->passwordlen)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memcpy((void*)m->c->password, options->binarypwd.data, m->c->passwordlen);
|
||||
}
|
||||
|
||||
m->c->retryInterval = options->retryInterval;
|
||||
m->shouldBeConnected = 1;
|
||||
|
||||
m->connectTimeout = options->connectTimeout;
|
||||
|
||||
MQTTAsync_freeServerURIs(m);
|
||||
if (options->struct_version >= 2 && options->serverURIcount > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
m->serverURIcount = options->serverURIcount;
|
||||
if ((m->serverURIs = malloc(options->serverURIcount * sizeof(char*))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < options->serverURIcount; ++i)
|
||||
m->serverURIs[i] = MQTTStrdup(options->serverURIs[i]);
|
||||
}
|
||||
|
||||
if (m->connectProps)
|
||||
{
|
||||
MQTTProperties_free(m->connectProps);
|
||||
free(m->connectProps);
|
||||
m->connectProps = NULL;
|
||||
}
|
||||
if (m->willProps)
|
||||
{
|
||||
MQTTProperties_free(m->willProps);
|
||||
free(m->willProps);
|
||||
m->willProps = NULL;
|
||||
}
|
||||
if (options->struct_version >=6)
|
||||
{
|
||||
if (options->connectProperties)
|
||||
{
|
||||
MQTTProperties initialized = MQTTProperties_initializer;
|
||||
|
||||
if ((m->connectProps = malloc(sizeof(MQTTProperties))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
*m->connectProps = initialized;
|
||||
*m->connectProps = MQTTProperties_copy(options->connectProperties);
|
||||
|
||||
if (MQTTProperties_hasProperty(options->connectProperties, MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL))
|
||||
m->c->sessionExpiry = (int)MQTTProperties_getNumericValue(options->connectProperties,
|
||||
MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL);
|
||||
|
||||
}
|
||||
if (options->willProperties)
|
||||
{
|
||||
MQTTProperties initialized = MQTTProperties_initializer;
|
||||
|
||||
if ((m->willProps = malloc(sizeof(MQTTProperties))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
*m->willProps = initialized;
|
||||
*m->willProps = MQTTProperties_copy(options->willProperties);
|
||||
}
|
||||
m->c->cleanstart = options->cleanstart;
|
||||
}
|
||||
|
||||
/* Add connect request to operation queue */
|
||||
if ((conn = malloc(sizeof(MQTTAsync_queuedCommand))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(conn, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
conn->client = m;
|
||||
if (options)
|
||||
{
|
||||
conn->command.onSuccess = options->onSuccess;
|
||||
conn->command.onFailure = options->onFailure;
|
||||
conn->command.onSuccess5 = options->onSuccess5;
|
||||
conn->command.onFailure5 = options->onFailure5;
|
||||
conn->command.context = options->context;
|
||||
}
|
||||
conn->command.type = CONNECT;
|
||||
conn->command.details.conn.currentURI = 0;
|
||||
rc = MQTTAsync_addCommand(conn, sizeof(conn));
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_reconnect(MQTTAsync handle)
|
||||
{
|
||||
int rc = MQTTASYNC_FAILURE;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m->automaticReconnect)
|
||||
{
|
||||
if (m->shouldBeConnected)
|
||||
{
|
||||
m->reconnectNow = 1;
|
||||
m->currentIntervalBase = m->minRetryInterval;
|
||||
m->currentInterval = m->minRetryInterval;
|
||||
m->retrying = 1;
|
||||
rc = MQTTASYNC_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* to reconnect, put the connect command to the head of the command queue */
|
||||
MQTTAsync_queuedCommand* conn = malloc(sizeof(MQTTAsync_queuedCommand));
|
||||
if (!conn)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(conn, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
conn->client = m;
|
||||
conn->command = m->connect;
|
||||
/* make sure that the version attempts are restarted */
|
||||
if (m->c->MQTTVersion == MQTTVERSION_DEFAULT)
|
||||
conn->command.details.conn.MQTTVersion = 0;
|
||||
rc = MQTTAsync_addCommand(conn, sizeof(m->connect));
|
||||
}
|
||||
|
||||
exit:
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_inCallback()
|
||||
{
|
||||
thread_id_type thread_id = Paho_thread_getid();
|
||||
return thread_id == sendThread_id || thread_id == receiveThread_id;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_subscribeMany(MQTTAsync handle, int count, char* const* topic, const int* qos, MQTTAsync_responseOptions* response)
|
||||
{
|
||||
MQTTAsyncs* m = handle;
|
||||
int i = 0, j = 0;
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsync_queuedCommand* sub;
|
||||
int msgid = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (!MQTTAsync_inCallback())
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
if (m == NULL || m->c == NULL)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else if (m->c->connected == 0)
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
else for (i = 0; i < count; i++)
|
||||
{
|
||||
if (!UTF8_validateString(topic[i]))
|
||||
{
|
||||
rc = MQTTASYNC_BAD_UTF8_STRING;
|
||||
break;
|
||||
}
|
||||
if (qos[i] < 0 || qos[i] > 2)
|
||||
{
|
||||
rc = MQTTASYNC_BAD_QOS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
; /* don't overwrite a previous error code */
|
||||
else if ((msgid = MQTTAsync_assignMsgId(m)) == 0)
|
||||
rc = MQTTASYNC_NO_MORE_MSGIDS;
|
||||
else if (m->c->MQTTVersion >= MQTTVERSION_5 && count > 1 && (count != response->subscribeOptionsCount
|
||||
&& response->subscribeOptionsCount != 0))
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
else if (response)
|
||||
{
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (response->struct_version == 0 || response->onFailure || response->onSuccess)
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
}
|
||||
else if (m->c->MQTTVersion < MQTTVERSION_5)
|
||||
{
|
||||
if (response->struct_version >= 1 && (response->onFailure5 || response->onSuccess5))
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
}
|
||||
}
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
goto exit;
|
||||
|
||||
/* Add subscribe request to operation queue */
|
||||
if ((sub = malloc(sizeof(MQTTAsync_queuedCommand))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(sub, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
sub->client = m;
|
||||
sub->command.token = msgid;
|
||||
if (response)
|
||||
{
|
||||
sub->command.onSuccess = response->onSuccess;
|
||||
sub->command.onFailure = response->onFailure;
|
||||
sub->command.onSuccess5 = response->onSuccess5;
|
||||
sub->command.onFailure5 = response->onFailure5;
|
||||
sub->command.context = response->context;
|
||||
response->token = sub->command.token;
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
sub->command.properties = MQTTProperties_copy(&response->properties);
|
||||
sub->command.details.sub.opts = response->subscribeOptions;
|
||||
if (count > 1)
|
||||
{
|
||||
if ((sub->command.details.sub.optlist = malloc(sizeof(MQTTSubscribe_options) * count)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
if(sub)
|
||||
{
|
||||
free(sub);
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
if (response->subscribeOptionsCount == 0)
|
||||
{
|
||||
MQTTSubscribe_options initialized = MQTTSubscribe_options_initializer;
|
||||
for (i = 0; i < count; ++i)
|
||||
sub->command.details.sub.optlist[i] = initialized;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < count; ++i)
|
||||
sub->command.details.sub.optlist[i] = response->subscribeOptionsList[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sub->command.type = SUBSCRIBE;
|
||||
sub->command.details.sub.count = count;
|
||||
sub->command.details.sub.topics = malloc(sizeof(char*) * count);
|
||||
sub->command.details.sub.qoss = malloc(sizeof(int) * count);
|
||||
if (sub->command.details.sub.topics && sub->command.details.sub.qoss)
|
||||
{
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
if ((sub->command.details.sub.topics[i] = MQTTStrdup(topic[i])) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
for(j = 0; j < i; ++j)
|
||||
{
|
||||
if(sub->command.details.sub.topics[j])
|
||||
{
|
||||
free(sub->command.details.sub.topics[j]);
|
||||
}
|
||||
}
|
||||
if(sub->command.details.sub.optlist)
|
||||
{
|
||||
free(sub->command.details.sub.optlist);
|
||||
}
|
||||
free(sub->command.details.sub.topics);
|
||||
free(sub->command.details.sub.qoss);
|
||||
free(sub);
|
||||
|
||||
goto exit;
|
||||
}
|
||||
sub->command.details.sub.qoss[i] = qos[i];
|
||||
}
|
||||
rc = MQTTAsync_addCommand(sub, sizeof(sub));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
if(sub->command.details.sub.optlist)
|
||||
{
|
||||
free(sub->command.details.sub.optlist);
|
||||
}
|
||||
if(sub->command.details.sub.topics)
|
||||
{
|
||||
free(sub->command.details.sub.topics);
|
||||
}
|
||||
if(sub->command.details.sub.qoss)
|
||||
{
|
||||
free(sub->command.details.sub.qoss);
|
||||
}
|
||||
if(sub)
|
||||
{
|
||||
free(sub);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (!MQTTAsync_inCallback())
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_subscribe(MQTTAsync handle, const char* topic, int qos, MQTTAsync_responseOptions* response)
|
||||
{
|
||||
int rc = 0;
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTAsync_subscribeMany(handle, 1, (char * const *)(&topic), &qos, response);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_unsubscribeMany(MQTTAsync handle, int count, char* const* topic, MQTTAsync_responseOptions* response)
|
||||
{
|
||||
MQTTAsyncs* m = handle;
|
||||
int i = 0;
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsync_queuedCommand* unsub;
|
||||
int msgid = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (!MQTTAsync_inCallback())
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
if (m == NULL || m->c == NULL)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else if (m->c->connected == 0)
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
else for (i = 0; i < count; i++)
|
||||
{
|
||||
if (!UTF8_validateString(topic[i]))
|
||||
{
|
||||
rc = MQTTASYNC_BAD_UTF8_STRING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
; /* don't overwrite a previous error code */
|
||||
else if ((msgid = MQTTAsync_assignMsgId(m)) == 0)
|
||||
rc = MQTTASYNC_NO_MORE_MSGIDS;
|
||||
else if (response)
|
||||
{
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (response->struct_version == 0 || response->onFailure || response->onSuccess)
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
}
|
||||
else if (m->c->MQTTVersion < MQTTVERSION_5)
|
||||
{
|
||||
if (response->struct_version >= 1 && (response->onFailure5 || response->onSuccess5))
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
}
|
||||
}
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
goto exit;
|
||||
|
||||
/* Add unsubscribe request to operation queue */
|
||||
if ((unsub = malloc(sizeof(MQTTAsync_queuedCommand))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(unsub, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
unsub->client = m;
|
||||
unsub->command.type = UNSUBSCRIBE;
|
||||
unsub->command.token = msgid;
|
||||
if (response)
|
||||
{
|
||||
unsub->command.onSuccess = response->onSuccess;
|
||||
unsub->command.onFailure = response->onFailure;
|
||||
unsub->command.onSuccess5 = response->onSuccess5;
|
||||
unsub->command.onFailure5 = response->onFailure5;
|
||||
unsub->command.context = response->context;
|
||||
response->token = unsub->command.token;
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
unsub->command.properties = MQTTProperties_copy(&response->properties);
|
||||
}
|
||||
unsub->command.details.unsub.count = count;
|
||||
if ((unsub->command.details.unsub.topics = malloc(sizeof(char*) * count)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < count; ++i)
|
||||
unsub->command.details.unsub.topics[i] = MQTTStrdup(topic[i]);
|
||||
rc = MQTTAsync_addCommand(unsub, sizeof(unsub));
|
||||
|
||||
exit:
|
||||
if (!MQTTAsync_inCallback())
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_unsubscribe(MQTTAsync handle, const char* topic, MQTTAsync_responseOptions* response)
|
||||
{
|
||||
int rc = 0;
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTAsync_unsubscribeMany(handle, 1, (char * const *)(&topic), response);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_send(MQTTAsync handle, const char* destinationName, int payloadlen, const void* payload,
|
||||
int qos, int retained, MQTTAsync_responseOptions* response)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
MQTTAsync_queuedCommand* pub;
|
||||
int msgid = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (!MQTTAsync_inCallback())
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
if (m == NULL || m->c == NULL)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else if (m->c->connected == 0)
|
||||
{
|
||||
if (m->createOptions == NULL)
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
else if (m->createOptions->sendWhileDisconnected == 0)
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
else if (m->shouldBeConnected == 0 && (m->createOptions->struct_version < 2 || m->createOptions->allowDisconnectedSendAtAnyTime == 0))
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
}
|
||||
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
goto exit;
|
||||
|
||||
if (!UTF8_validateString(destinationName))
|
||||
rc = MQTTASYNC_BAD_UTF8_STRING;
|
||||
else if (qos < 0 || qos > 2)
|
||||
rc = MQTTASYNC_BAD_QOS;
|
||||
else if (qos > 0 && (msgid = MQTTAsync_assignMsgId(m)) == 0)
|
||||
rc = MQTTASYNC_NO_MORE_MSGIDS;
|
||||
else if (m->createOptions &&
|
||||
(m->createOptions->struct_version < 2 || m->createOptions->deleteOldestMessages == 0) &&
|
||||
(MQTTAsync_getNoBufferedMessages(m) >= m->createOptions->maxBufferedMessages))
|
||||
rc = MQTTASYNC_MAX_BUFFERED_MESSAGES;
|
||||
else if (response)
|
||||
{
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (response->struct_version == 0 || response->onFailure || response->onSuccess)
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
}
|
||||
else if (m->c->MQTTVersion < MQTTVERSION_5)
|
||||
{
|
||||
if (response->struct_version >= 1 && (response->onFailure5 || response->onSuccess5))
|
||||
rc = MQTTASYNC_BAD_MQTT_OPTION;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
goto exit;
|
||||
|
||||
/* Add publish request to operation queue */
|
||||
if ((pub = malloc(sizeof(MQTTAsync_queuedCommand))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(pub, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
pub->client = m;
|
||||
pub->command.type = PUBLISH;
|
||||
pub->command.token = msgid;
|
||||
if (response)
|
||||
{
|
||||
pub->command.onSuccess = response->onSuccess;
|
||||
pub->command.onFailure = response->onFailure;
|
||||
pub->command.onSuccess5 = response->onSuccess5;
|
||||
pub->command.onFailure5 = response->onFailure5;
|
||||
pub->command.context = response->context;
|
||||
response->token = pub->command.token;
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
pub->command.properties = MQTTProperties_copy(&response->properties);
|
||||
}
|
||||
if ((pub->command.details.pub.destinationName = MQTTStrdup(destinationName)) == NULL)
|
||||
{
|
||||
free(pub);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
pub->command.details.pub.payloadlen = payloadlen;
|
||||
if ((pub->command.details.pub.payload = malloc(payloadlen)) == NULL)
|
||||
{
|
||||
free(pub->command.details.pub.destinationName);
|
||||
free(pub);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(pub->command.details.pub.payload, payload, payloadlen);
|
||||
pub->command.details.pub.qos = qos;
|
||||
pub->command.details.pub.retained = retained;
|
||||
rc = MQTTAsync_addCommand(pub, sizeof(pub));
|
||||
|
||||
exit:
|
||||
if (!MQTTAsync_inCallback())
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_sendMessage(MQTTAsync handle, const char* destinationName, const MQTTAsync_message* message,
|
||||
MQTTAsync_responseOptions* response)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (message == NULL)
|
||||
{
|
||||
rc = MQTTASYNC_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
if (strncmp(message->struct_id, "MQTM", 4) != 0 ||
|
||||
(message->struct_version != 0 && message->struct_version != 1))
|
||||
{
|
||||
rc = MQTTASYNC_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5 && response)
|
||||
response->properties = message->properties;
|
||||
|
||||
rc = MQTTAsync_send(handle, destinationName, message->payloadlen, message->payload,
|
||||
message->qos, message->retained, response);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_disconnect(MQTTAsync handle, const MQTTAsync_disconnectOptions* options)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (!MQTTAsync_inCallback())
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
if (options != NULL && (strncmp(options->struct_id, "MQTD", 4) != 0 || options->struct_version < 0 || options->struct_version > 1))
|
||||
rc = MQTTASYNC_BAD_STRUCTURE;
|
||||
else
|
||||
rc = MQTTAsync_disconnect1(handle, options, 0);
|
||||
if (!MQTTAsync_inCallback())
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_isConnected(MQTTAsync handle)
|
||||
{
|
||||
MQTTAsyncs* m = handle;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
if (m && m->c)
|
||||
rc = m->c->connected;
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_isComplete(MQTTAsync handle, MQTTAsync_token dt)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
ListElement* current = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL)
|
||||
{
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* First check unprocessed commands */
|
||||
current = NULL;
|
||||
while (ListNextElement(MQTTAsync_commands, ¤t))
|
||||
{
|
||||
MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(current->content);
|
||||
|
||||
if (cmd->client == m && cmd->command.token == dt)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Now check the inflight messages */
|
||||
if (m->c && m->c->outboundMsgs->count > 0)
|
||||
{
|
||||
current = NULL;
|
||||
while (ListNextElement(m->c->outboundMsgs, ¤t))
|
||||
{
|
||||
Messages* m2 = (Messages*)(current->content);
|
||||
if (m2->msgid == dt)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
rc = MQTTASYNC_TRUE; /* Can't find it, so it must be complete */
|
||||
|
||||
exit:
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_waitForCompletion(MQTTAsync handle, MQTTAsync_token dt, unsigned long timeout)
|
||||
{
|
||||
int rc = MQTTASYNC_FAILURE;
|
||||
START_TIME_TYPE start = MQTTTime_start_clock();
|
||||
ELAPSED_TIME_TYPE elapsed = 0L;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL || m->c == NULL)
|
||||
{
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
if (m->c->connected == 0)
|
||||
{
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
goto exit;
|
||||
}
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
|
||||
if (MQTTAsync_isComplete(handle, dt) == 1)
|
||||
{
|
||||
rc = MQTTASYNC_SUCCESS; /* well we couldn't find it */
|
||||
goto exit;
|
||||
}
|
||||
|
||||
elapsed = MQTTTime_elapsed(start);
|
||||
while (elapsed < timeout && rc == MQTTASYNC_FAILURE)
|
||||
{
|
||||
MQTTTime_sleep(100);
|
||||
if (MQTTAsync_isComplete(handle, dt) == 1)
|
||||
rc = MQTTASYNC_SUCCESS; /* well we couldn't find it */
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
if (m->c->connected == 0)
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
elapsed = MQTTTime_elapsed(start);
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_getPendingTokens(MQTTAsync handle, MQTTAsync_token **tokens)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
ListElement* current = NULL;
|
||||
int count = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
MQTTAsync_lock_mutex(mqttcommand_mutex);
|
||||
*tokens = NULL;
|
||||
|
||||
if (m == NULL)
|
||||
{
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* calculate the number of pending tokens - commands plus inflight */
|
||||
while (ListNextElement(MQTTAsync_commands, ¤t))
|
||||
{
|
||||
MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(current->content);
|
||||
|
||||
if (cmd->client == m && cmd->command.type == PUBLISH)
|
||||
count++;
|
||||
}
|
||||
if (m->c)
|
||||
count += m->c->outboundMsgs->count;
|
||||
if (count == 0)
|
||||
goto exit; /* no tokens to return */
|
||||
*tokens = malloc(sizeof(MQTTAsync_token) * (count + 1)); /* add space for sentinel at end of list */
|
||||
if (!*tokens)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* First add the unprocessed commands to the pending tokens */
|
||||
current = NULL;
|
||||
count = 0;
|
||||
while (ListNextElement(MQTTAsync_commands, ¤t))
|
||||
{
|
||||
MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(current->content);
|
||||
|
||||
if (cmd->client == m && cmd->command.type == PUBLISH)
|
||||
(*tokens)[count++] = cmd->command.token;
|
||||
}
|
||||
|
||||
/* Now add the inflight messages */
|
||||
if (m->c && m->c->outboundMsgs->count > 0)
|
||||
{
|
||||
current = NULL;
|
||||
while (ListNextElement(m->c->outboundMsgs, ¤t))
|
||||
{
|
||||
Messages* m2 = (Messages*)(current->content);
|
||||
(*tokens)[count++] = m2->msgid;
|
||||
}
|
||||
}
|
||||
(*tokens)[count] = -1; /* indicate end of list */
|
||||
|
||||
exit:
|
||||
MQTTAsync_unlock_mutex(mqttcommand_mutex);
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_setCallbacks(MQTTAsync handle, void* context,
|
||||
MQTTAsync_connectionLost* cl,
|
||||
MQTTAsync_messageArrived* ma,
|
||||
MQTTAsync_deliveryComplete* dc)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL || ma == NULL || m->c == NULL || m->c->connect_state != NOT_IN_PROGRESS)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->clContext = m->maContext = m->dcContext = context;
|
||||
m->cl = cl;
|
||||
m->ma = ma;
|
||||
m->dc = dc;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int MQTTAsync_setConnectionLostCallback(MQTTAsync handle, void* context,
|
||||
MQTTAsync_connectionLost* cl)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL || m->c->connect_state != 0)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->clContext = context;
|
||||
m->cl = cl;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_setMessageArrivedCallback(MQTTAsync handle, void* context,
|
||||
MQTTAsync_messageArrived* ma)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL || ma == NULL || m->c->connect_state != 0)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->maContext = context;
|
||||
m->ma = ma;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int MQTTAsync_setDeliveryCompleteCallback(MQTTAsync handle, void* context,
|
||||
MQTTAsync_deliveryComplete* dc)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL || m->c->connect_state != 0)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->dcContext = context;
|
||||
m->dc = dc;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_setDisconnected(MQTTAsync handle, void* context, MQTTAsync_disconnected* disconnected)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL || m->c->connect_state != NOT_IN_PROGRESS)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->disconnected_context = context;
|
||||
m->disconnected = disconnected;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_setConnected(MQTTAsync handle, void* context, MQTTAsync_connected* connected)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL || m->c->connect_state != NOT_IN_PROGRESS)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->connected_context = context;
|
||||
m->connected = connected;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_setUpdateConnectOptions(MQTTAsync handle, void* context, MQTTAsync_updateConnectOptions* updateOptions)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->updateConnectOptions_context = context;
|
||||
m->updateConnectOptions = updateOptions;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
int MQTTAsync_setBeforePersistenceWrite(MQTTAsync handle, void* context, MQTTPersistence_beforeWrite* co)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->c->beforeWrite = co;
|
||||
m->c->beforeWrite_context = context;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_setAfterPersistenceRead(MQTTAsync handle, void* context, MQTTPersistence_afterRead* co)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
|
||||
if (m == NULL)
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
else
|
||||
{
|
||||
m->c->afterRead = co;
|
||||
m->c->afterRead_context = context;
|
||||
}
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void MQTTAsync_setTraceLevel(enum MQTTASYNC_TRACE_LEVELS level)
|
||||
{
|
||||
Log_setTraceLevel((enum LOG_LEVELS)level);
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_setTraceCallback(MQTTAsync_traceCallback* callback)
|
||||
{
|
||||
Log_setTraceCallback((Log_traceCallback*)callback);
|
||||
}
|
||||
|
||||
|
||||
MQTTAsync_nameValue* MQTTAsync_getVersionInfo(void)
|
||||
{
|
||||
#define MAX_INFO_STRINGS 8
|
||||
static MQTTAsync_nameValue libinfo[MAX_INFO_STRINGS + 1];
|
||||
int i = 0;
|
||||
|
||||
libinfo[i].name = "Product name";
|
||||
libinfo[i++].value = "Eclipse Paho Asynchronous MQTT C Client Library";
|
||||
|
||||
libinfo[i].name = "Version";
|
||||
libinfo[i++].value = CLIENT_VERSION;
|
||||
|
||||
libinfo[i].name = "Build level";
|
||||
libinfo[i++].value = BUILD_TIMESTAMP;
|
||||
#if defined(OPENSSL)
|
||||
libinfo[i].name = "OpenSSL version";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_VERSION);
|
||||
|
||||
libinfo[i].name = "OpenSSL flags";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_CFLAGS);
|
||||
|
||||
libinfo[i].name = "OpenSSL build timestamp";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_BUILT_ON);
|
||||
|
||||
libinfo[i].name = "OpenSSL platform";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_PLATFORM);
|
||||
|
||||
libinfo[i].name = "OpenSSL directory";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_DIR);
|
||||
#endif
|
||||
libinfo[i].name = NULL;
|
||||
libinfo[i].value = NULL;
|
||||
return libinfo;
|
||||
}
|
||||
|
||||
const char* MQTTAsync_strerror(int code)
|
||||
{
|
||||
static char buf[30];
|
||||
int chars = 0;
|
||||
|
||||
switch (code) {
|
||||
case MQTTASYNC_SUCCESS:
|
||||
return "Success";
|
||||
case MQTTASYNC_FAILURE:
|
||||
return "Failure";
|
||||
case MQTTASYNC_PERSISTENCE_ERROR:
|
||||
return "Persistence error";
|
||||
case MQTTASYNC_DISCONNECTED:
|
||||
return "Disconnected";
|
||||
case MQTTASYNC_MAX_MESSAGES_INFLIGHT:
|
||||
return "Maximum in-flight messages amount reached";
|
||||
case MQTTASYNC_BAD_UTF8_STRING:
|
||||
return "Invalid UTF8 string";
|
||||
case MQTTASYNC_NULL_PARAMETER:
|
||||
return "Invalid (NULL) parameter";
|
||||
case MQTTASYNC_TOPICNAME_TRUNCATED:
|
||||
return "Topic containing NULL characters has been truncated";
|
||||
case MQTTASYNC_BAD_STRUCTURE:
|
||||
return "Bad structure";
|
||||
case MQTTASYNC_BAD_QOS:
|
||||
return "Invalid QoS value";
|
||||
case MQTTASYNC_NO_MORE_MSGIDS:
|
||||
return "Too many pending commands";
|
||||
case MQTTASYNC_OPERATION_INCOMPLETE:
|
||||
return "Operation discarded before completion";
|
||||
case MQTTASYNC_MAX_BUFFERED_MESSAGES:
|
||||
return "No more messages can be buffered";
|
||||
case MQTTASYNC_SSL_NOT_SUPPORTED:
|
||||
return "SSL is not supported";
|
||||
case MQTTASYNC_BAD_PROTOCOL:
|
||||
return "Invalid protocol scheme";
|
||||
case MQTTASYNC_BAD_MQTT_OPTION:
|
||||
return "Options for wrong MQTT version";
|
||||
case MQTTASYNC_WRONG_MQTT_VERSION:
|
||||
return "Client created for another version of MQTT";
|
||||
case MQTTASYNC_0_LEN_WILL_TOPIC:
|
||||
return "Zero length will topic on connect";
|
||||
case MQTTASYNC_COMMAND_IGNORED:
|
||||
return "Connect or disconnect command ignored";
|
||||
case MQTTASYNC_MAX_BUFFERED:
|
||||
return "maxBufferedMessages in the connect options must be >= 0";
|
||||
}
|
||||
|
||||
chars = snprintf(buf, sizeof(buf), "Unknown error code %d", code);
|
||||
if (chars >= sizeof(buf))
|
||||
{
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_freeMessage(MQTTAsync_message** message)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
MQTTProperties_free(&(*message)->properties);
|
||||
free((*message)->payload);
|
||||
free(*message);
|
||||
*message = NULL;
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_free(void* memory)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
free(memory);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void* MQTTAsync_malloc(size_t size)
|
||||
{
|
||||
void* val;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
val = malloc(size);
|
||||
rc = (val != NULL);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTAsync_freeServerURIs(MQTTAsyncs* m)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < m->serverURIcount; ++i)
|
||||
free(m->serverURIs[i]);
|
||||
m->serverURIcount = 0;
|
||||
if (m->serverURIs)
|
||||
free(m->serverURIs);
|
||||
m->serverURIs = NULL;
|
||||
}
|
||||
2404
3rd/paho.mqtt.c/src/MQTTAsync.h
Normal file
2404
3rd/paho.mqtt.c/src/MQTTAsync.h
Normal file
@@ -0,0 +1,2404 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2026 IBM Corp., Ian Craggs and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL connections
|
||||
* Ian Craggs - multiple server connection support
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - fix for bug 444103 - success/failure callbacks not invoked
|
||||
* Ian Craggs - automatic reconnect and offline buffering (send while disconnected)
|
||||
* Ian Craggs - binary will message
|
||||
* Ian Craggs - binary password
|
||||
* Ian Craggs - remove const on eyecatchers #168
|
||||
* Ian Craggs - MQTT 5.0
|
||||
*******************************************************************************/
|
||||
|
||||
/********************************************************************/
|
||||
|
||||
/**
|
||||
* @cond MQTTAsync_main
|
||||
* @mainpage Asynchronous MQTT client library for C (MQTTAsync)
|
||||
*
|
||||
* © Copyright 2009, 2026 IBM Corp., Ian Craggs and others.
|
||||
*
|
||||
* @brief An Asynchronous MQTT client library for C.
|
||||
*
|
||||
* Version 1.3.16
|
||||
*
|
||||
* An MQTT client application connects to MQTT-capable servers.
|
||||
* A typical client is responsible for collecting information from a telemetry
|
||||
* device and publishing the information to the server. It can also subscribe
|
||||
* to topics, receive messages, and use this information to control the
|
||||
* telemetry device.
|
||||
*
|
||||
* MQTT clients implement the published MQTT v3 protocol. You can write your own
|
||||
* API to the MQTT protocol using the programming language and platform of your
|
||||
* choice. This can be time-consuming and error-prone.
|
||||
*
|
||||
* To simplify writing MQTT client applications, this library encapsulates
|
||||
* the MQTT v3 protocol for you. Using this library enables a fully functional
|
||||
* MQTT client application to be written in a few lines of code.
|
||||
* The information presented here documents the API provided
|
||||
* by the Asynchronous MQTT Client library for C.
|
||||
*
|
||||
* <b>Using the client</b><br>
|
||||
* Applications that use the client library typically use a similar structure:
|
||||
* <ul>
|
||||
* <li>Create a client object</li>
|
||||
* <li>Set the options to connect to an MQTT server</li>
|
||||
* <li>Set up callback functions</li>
|
||||
* <li>Connect the client to an MQTT server</li>
|
||||
* <li>Subscribe to any topics the client needs to receive</li>
|
||||
* <li>Repeat until finished:</li>
|
||||
* <ul>
|
||||
* <li>Publish any messages the client needs to</li>
|
||||
* <li>Handle any incoming messages</li>
|
||||
* </ul>
|
||||
* <li>Disconnect the client</li>
|
||||
* <li>Free any memory being used by the client</li>
|
||||
* </ul>
|
||||
* Some simple examples are shown here:
|
||||
* <ul>
|
||||
* <li>@ref publish</li>
|
||||
* <li>@ref subscribe</li>
|
||||
* </ul>
|
||||
* Additional information about important concepts is provided here:
|
||||
* <ul>
|
||||
* <li>@ref async</li>
|
||||
* <li>@ref wildcard</li>
|
||||
* <li>@ref qos</li>
|
||||
* <li>@ref tracing</li>
|
||||
* <li>@ref auto_reconnect</li>
|
||||
* <li>@ref offline_publish</li>
|
||||
* <li>@ref HTTP_proxies</li>
|
||||
* </ul>
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/*
|
||||
/// @cond EXCLUDE
|
||||
*/
|
||||
#if !defined(MQTTASYNC_H)
|
||||
#define MQTTASYNC_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
/*
|
||||
/// @endcond
|
||||
*/
|
||||
|
||||
#include "MQTTExportDeclarations.h"
|
||||
|
||||
#include "MQTTProperties.h"
|
||||
#include "MQTTReasonCodes.h"
|
||||
#include "MQTTSubscribeOpts.h"
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
#include "MQTTClientPersistence.h"
|
||||
#else
|
||||
#define MQTTCLIENT_PERSISTENCE_NONE 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return code: No error. Indicates successful completion of an MQTT client
|
||||
* operation.
|
||||
*/
|
||||
#define MQTTASYNC_SUCCESS 0
|
||||
/**
|
||||
* Return code: A generic error code indicating the failure of an MQTT client
|
||||
* operation.
|
||||
*/
|
||||
#define MQTTASYNC_FAILURE -1
|
||||
|
||||
/* error code -2 is MQTTAsync_PERSISTENCE_ERROR */
|
||||
|
||||
#define MQTTASYNC_PERSISTENCE_ERROR -2
|
||||
|
||||
/**
|
||||
* Return code: The client is disconnected.
|
||||
*/
|
||||
#define MQTTASYNC_DISCONNECTED -3
|
||||
/**
|
||||
* Return code: The maximum number of messages allowed to be simultaneously
|
||||
* in-flight has been reached.
|
||||
*/
|
||||
#define MQTTASYNC_MAX_MESSAGES_INFLIGHT -4
|
||||
/**
|
||||
* Return code: An invalid UTF-8 string has been detected.
|
||||
*/
|
||||
#define MQTTASYNC_BAD_UTF8_STRING -5
|
||||
/**
|
||||
* Return code: A NULL parameter has been supplied when this is invalid.
|
||||
*/
|
||||
#define MQTTASYNC_NULL_PARAMETER -6
|
||||
/**
|
||||
* Return code: The topic has been truncated (the topic string includes
|
||||
* embedded NULL characters). String functions will not access the full topic.
|
||||
* Use the topic length value to access the full topic.
|
||||
*/
|
||||
#define MQTTASYNC_TOPICNAME_TRUNCATED -7
|
||||
/**
|
||||
* Return code: A structure parameter does not have the correct eyecatcher
|
||||
* and version number.
|
||||
*/
|
||||
#define MQTTASYNC_BAD_STRUCTURE -8
|
||||
/**
|
||||
* Return code: A qos parameter is not 0, 1 or 2
|
||||
*/
|
||||
#define MQTTASYNC_BAD_QOS -9
|
||||
/**
|
||||
* Return code: All 65535 MQTT msgids are being used
|
||||
*/
|
||||
#define MQTTASYNC_NO_MORE_MSGIDS -10
|
||||
/**
|
||||
* Return code: the request is being discarded when not complete
|
||||
*/
|
||||
#define MQTTASYNC_OPERATION_INCOMPLETE -11
|
||||
/**
|
||||
* Return code: no more messages can be buffered
|
||||
*/
|
||||
#define MQTTASYNC_MAX_BUFFERED_MESSAGES -12
|
||||
/**
|
||||
* Return code: Attempting SSL connection using non-SSL version of library
|
||||
*/
|
||||
#define MQTTASYNC_SSL_NOT_SUPPORTED -13
|
||||
/**
|
||||
* Return code: protocol prefix in serverURI should be:
|
||||
* @li @em tcp:// or @em mqtt:// - Insecure TCP
|
||||
* @li @em ssl:// or @em tls:// or @em mqtts:// - Encrypted SSL/TLS
|
||||
* @li @em ws:// - Insecure websockets
|
||||
* @li @em wss:// - Secure web sockets
|
||||
*
|
||||
* The TLS enabled prefixes (ssl, tls, mqtts, wss) are only valid if the TLS
|
||||
* version of the library is linked with.
|
||||
*/
|
||||
#define MQTTASYNC_BAD_PROTOCOL -14
|
||||
/**
|
||||
* Return code: don't use options for another version of MQTT
|
||||
*/
|
||||
#define MQTTASYNC_BAD_MQTT_OPTION -15
|
||||
/**
|
||||
* Return code: call not applicable to the client's version of MQTT
|
||||
*/
|
||||
#define MQTTASYNC_WRONG_MQTT_VERSION -16
|
||||
/**
|
||||
* Return code: 0 length will topic
|
||||
*/
|
||||
#define MQTTASYNC_0_LEN_WILL_TOPIC -17
|
||||
/*
|
||||
* Return code: connect or disconnect command ignored because there is already a connect or disconnect
|
||||
* command at the head of the list waiting to be processed. Use the onSuccess/onFailure callbacks to wait
|
||||
* for the previous connect or disconnect command to be complete.
|
||||
*/
|
||||
#define MQTTASYNC_COMMAND_IGNORED -18
|
||||
/*
|
||||
* Return code: maxBufferedMessages in the connect options must be >= 0
|
||||
*/
|
||||
#define MQTTASYNC_MAX_BUFFERED -19
|
||||
|
||||
/**
|
||||
* Default MQTT version to connect with. Use 3.1.1 then fall back to 3.1
|
||||
*/
|
||||
#define MQTTVERSION_DEFAULT 0
|
||||
/**
|
||||
* MQTT version to connect with: 3.1
|
||||
*/
|
||||
#define MQTTVERSION_3_1 3
|
||||
/**
|
||||
* MQTT version to connect with: 3.1.1
|
||||
*/
|
||||
#define MQTTVERSION_3_1_1 4
|
||||
/**
|
||||
* MQTT version to connect with: 5
|
||||
*/
|
||||
#define MQTTVERSION_5 5
|
||||
/**
|
||||
* Bad return code from subscribe, as defined in the 3.1.1 specification
|
||||
*/
|
||||
#define MQTT_BAD_SUBSCRIBE 0x80
|
||||
|
||||
|
||||
/**
|
||||
* Initialization options
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. Must be MQTG. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** 1 = we do openssl init, 0 = leave it to the application */
|
||||
int do_openssl_init;
|
||||
} MQTTAsync_init_options;
|
||||
|
||||
#define MQTTAsync_init_options_initializer { {'M', 'Q', 'T', 'G'}, 0, 0 }
|
||||
|
||||
/**
|
||||
* Global init of mqtt library. Call once on program start to set global behaviour.
|
||||
* handle_openssl_init - if mqtt library should handle openssl init (1) or rely on the caller to init it before using mqtt (0)
|
||||
*/
|
||||
LIBMQTT_API void MQTTAsync_global_init(MQTTAsync_init_options* inits);
|
||||
|
||||
/**
|
||||
* A handle representing an MQTT client. A valid client handle is available
|
||||
* following a successful call to MQTTAsync_create().
|
||||
*/
|
||||
typedef void* MQTTAsync;
|
||||
/**
|
||||
* A value representing an MQTT message. A token is returned to the
|
||||
* client application when a message is published. The token can then be used to
|
||||
* check that the message was successfully delivered to its destination (see
|
||||
* MQTTAsync_publish(),
|
||||
* MQTTAsync_publishMessage(),
|
||||
* MQTTAsync_deliveryComplete(), and
|
||||
* MQTTAsync_getPendingTokens()).
|
||||
*/
|
||||
typedef int MQTTAsync_token;
|
||||
|
||||
/**
|
||||
* A structure representing the payload and attributes of an MQTT message. The
|
||||
* message topic is not part of this structure (see MQTTAsync_publishMessage(),
|
||||
* MQTTAsync_publish(), MQTTAsync_receive(), MQTTAsync_freeMessage()
|
||||
* and MQTTAsync_messageArrived()).
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTM. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 or 1.
|
||||
* 0 indicates no message properties */
|
||||
int struct_version;
|
||||
/** The length of the MQTT message payload in bytes. */
|
||||
int payloadlen;
|
||||
/** A pointer to the payload of the MQTT message. */
|
||||
void* payload;
|
||||
/**
|
||||
* The quality of service (QoS) assigned to the message.
|
||||
* There are three levels of QoS:
|
||||
* <DL>
|
||||
* <DT><B>QoS0</B></DT>
|
||||
* <DD>Fire and forget - the message may not be delivered</DD>
|
||||
* <DT><B>QoS1</B></DT>
|
||||
* <DD>At least once - the message will be delivered, but may be
|
||||
* delivered more than once in some circumstances.</DD>
|
||||
* <DT><B>QoS2</B></DT>
|
||||
* <DD>Once and one only - the message will be delivered exactly once.</DD>
|
||||
* </DL>
|
||||
*/
|
||||
int qos;
|
||||
/**
|
||||
* The retained flag serves two purposes depending on whether the message
|
||||
* it is associated with is being published or received.
|
||||
*
|
||||
* <b>retained = true</b><br>
|
||||
* For messages being published, a true setting indicates that the MQTT
|
||||
* server should retain a copy of the message. The message will then be
|
||||
* transmitted to new subscribers to a topic that matches the message topic.
|
||||
* For subscribers registering a new subscription, the flag being true
|
||||
* indicates that the received message is not a new one, but one that has
|
||||
* been retained by the MQTT server.
|
||||
*
|
||||
* <b>retained = false</b> <br>
|
||||
* For publishers, this indicates that this message should not be retained
|
||||
* by the MQTT server. For subscribers, a false setting indicates this is
|
||||
* a normal message, received as a result of it being published to the
|
||||
* server.
|
||||
*/
|
||||
int retained;
|
||||
/**
|
||||
* The dup flag indicates whether or not this message is a duplicate.
|
||||
* It is only meaningful when receiving QoS1 messages. When true, the
|
||||
* client application should take appropriate action to deal with the
|
||||
* duplicate message. This is an output parameter only.
|
||||
*/
|
||||
int dup;
|
||||
/** The message identifier is reserved for internal use by the
|
||||
* MQTT client and server. It is an output parameter only - writing
|
||||
* to it will serve no purpose. It contains the MQTT message id of
|
||||
* an incoming publish message.
|
||||
*/
|
||||
int msgid;
|
||||
/**
|
||||
* The MQTT V5 properties associated with the message.
|
||||
*/
|
||||
MQTTProperties properties;
|
||||
} MQTTAsync_message;
|
||||
|
||||
#define MQTTAsync_message_initializer { {'M', 'Q', 'T', 'M'}, 1, 0, NULL, 0, 0, 0, 0, MQTTProperties_initializer }
|
||||
|
||||
/**
|
||||
* This is a callback function. The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* receipt of messages. The function is registered with the client library by
|
||||
* passing it as an argument to MQTTAsync_setCallbacks(). It is
|
||||
* called by the client library when a new message that matches a client
|
||||
* subscription has been received from the server. This function is executed on
|
||||
* a separate thread to the one on which the client application is running.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTAsync_setCallbacks(), which contains any application-specific context.
|
||||
* @param topicName The topic associated with the received message.
|
||||
* @param topicLen The length of the topic if there are one
|
||||
* more NULL characters embedded in <i>topicName</i>, otherwise <i>topicLen</i>
|
||||
* is 0. If <i>topicLen</i> is 0, the value returned by <i>strlen(topicName)</i>
|
||||
* can be trusted. If <i>topicLen</i> is greater than 0, the full topic name
|
||||
* can be retrieved by accessing <i>topicName</i> as a byte array of length
|
||||
* <i>topicLen</i>.
|
||||
* @param message The MQTTAsync_message structure for the received message.
|
||||
* This structure contains the message payload and attributes.
|
||||
* @return This function must return 0 or 1 indicating whether or not
|
||||
* the message has been safely received by the client application. <br>
|
||||
* Returning 1 indicates that the message has been successfully handled.
|
||||
* To free the message storage, ::MQTTAsync_freeMessage must be called.
|
||||
* To free the topic name storage, ::MQTTAsync_free must be called.<br>
|
||||
* Returning 0 indicates that there was a problem. In this
|
||||
* case, the client library will reinvoke MQTTAsync_messageArrived() to
|
||||
* attempt to deliver the message to the application again.
|
||||
* Do not free the message and topic storage when returning 0, otherwise
|
||||
* the redelivery will fail.
|
||||
*/
|
||||
typedef int MQTTAsync_messageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* message);
|
||||
|
||||
/**
|
||||
* This is a callback function. The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* notification of delivery of messages to the server. The function is
|
||||
* registered with the client library by passing it as an argument to MQTTAsync_setCallbacks().
|
||||
* It is called by the client library after the client application has
|
||||
* published a message to the server. It indicates that the necessary
|
||||
* handshaking and acknowledgements for the requested quality of service (see
|
||||
* MQTTAsync_message.qos) have been completed. This function is executed on a
|
||||
* separate thread to the one on which the client application is running.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTAsync_setCallbacks(), which contains any application-specific context.
|
||||
* @param token The ::MQTTAsync_token associated with
|
||||
* the published message. Applications can check that all messages have been
|
||||
* correctly published by matching the tokens returned from calls to
|
||||
* MQTTAsync_send() and MQTTAsync_sendMessage() with the tokens passed
|
||||
* to this callback.
|
||||
*/
|
||||
typedef void MQTTAsync_deliveryComplete(void* context, MQTTAsync_token token);
|
||||
|
||||
/**
|
||||
* This is a callback function. The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* notification of the loss of connection to the server. The function is
|
||||
* registered with the client library by passing it as an argument to
|
||||
* MQTTAsync_setCallbacks(). It is called by the client library if the client
|
||||
* loses its connection to the server. The client application must take
|
||||
* appropriate action, such as trying to reconnect or reporting the problem.
|
||||
* This function is executed on a separate thread to the one on which the
|
||||
* client application is running.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTAsync_setCallbacks(), which contains any application-specific context.
|
||||
* @param cause The reason for the disconnection.
|
||||
* Currently, <i>cause</i> is always set to NULL.
|
||||
*/
|
||||
typedef void MQTTAsync_connectionLost(void* context, char* cause);
|
||||
|
||||
|
||||
/**
|
||||
* This is a callback function, which will be called when the client
|
||||
* library successfully connects. This is superfluous when the connection
|
||||
* is made in response to a MQTTAsync_connect call, because the onSuccess
|
||||
* callback can be used. It is intended for use when automatic reconnect
|
||||
* is enabled, so that when a reconnection attempt succeeds in the background,
|
||||
* the application is notified and can take any required actions.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTAsync_setCallbacks(), which contains any application-specific context.
|
||||
* @param cause The reason for the disconnection.
|
||||
* Currently, <i>cause</i> is always set to NULL.
|
||||
*/
|
||||
typedef void MQTTAsync_connected(void* context, char* cause);
|
||||
|
||||
/**
|
||||
* This is a callback function, which will be called when the client
|
||||
* library receives a disconnect packet from the server. This applies to MQTT V5 and above only.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTAsync_setCallbacks(), which contains any application-specific context.
|
||||
* @param properties the properties in the disconnect packet.
|
||||
* @param reasonCode the reason code from the disconnect packet
|
||||
*/
|
||||
typedef void MQTTAsync_disconnected(void* context, MQTTProperties* properties,
|
||||
enum MQTTReasonCodes reasonCode);
|
||||
|
||||
/**
|
||||
* Sets the MQTTAsync_disconnected() callback function for a client.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to each of the callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
* @param co A pointer to an MQTTAsync_connected() callback
|
||||
* function. NULL removes the callback setting.
|
||||
* @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTASYNC_FAILURE if an error occurred.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_setDisconnected(MQTTAsync handle, void* context, MQTTAsync_disconnected* co);
|
||||
|
||||
/** The connect options that can be updated before an automatic reconnect. */
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. Will be MQCD. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Will be 0 */
|
||||
int struct_version;
|
||||
/**
|
||||
* MQTT servers that support the MQTT v3.1 protocol provide authentication
|
||||
* and authorisation by user name and password. This is the user name parameter.
|
||||
* Set data to NULL to remove. To change, allocate new
|
||||
* storage with ::MQTTAsync_allocate - this will then be free later by the library.
|
||||
*/
|
||||
const char* username;
|
||||
/**
|
||||
* The password parameter of the MQTT authentication.
|
||||
* Set data to NULL to remove. To change, allocate new
|
||||
* storage with ::MQTTAsync_allocate - this will then be free later by the library.
|
||||
*/
|
||||
struct {
|
||||
int len; /**< binary password length */
|
||||
const void* data; /**< binary password data */
|
||||
} binarypwd;
|
||||
} MQTTAsync_connectData;
|
||||
|
||||
#define MQTTAsync_connectData_initializer {{'M', 'Q', 'C', 'D'}, 0, NULL, {0, NULL}}
|
||||
|
||||
/**
|
||||
* This is a callback function which will allow the client application to update the
|
||||
* connection data.
|
||||
* @param data The connection data which can be modified by the application.
|
||||
* @return Return a non-zero value to update the connect data, zero to keep the same data.
|
||||
*/
|
||||
typedef int MQTTAsync_updateConnectOptions(void* context, MQTTAsync_connectData* data);
|
||||
|
||||
/**
|
||||
* Sets the MQTTAsync_updateConnectOptions() callback function for a client.
|
||||
* @param handle A valid client handle from a successful call to MQTTAsync_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to each of the callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
* @param co A pointer to an MQTTAsync_updateConnectOptions() callback
|
||||
* function. NULL removes the callback setting.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_setUpdateConnectOptions(MQTTAsync handle, void* context, MQTTAsync_updateConnectOptions* co);
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
/**
|
||||
* Sets the MQTTPersistence_beforeWrite() callback function for a client.
|
||||
* @param handle A valid client handle from a successful call to MQTTAsync_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to the callback function to
|
||||
* provide access to the context information in the callback.
|
||||
* @param co A pointer to an MQTTPersistence_beforeWrite() callback
|
||||
* function. NULL removes the callback setting.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_setBeforePersistenceWrite(MQTTAsync handle, void* context, MQTTPersistence_beforeWrite* co);
|
||||
|
||||
|
||||
/**
|
||||
* Sets the MQTTPersistence_afterRead() callback function for a client.
|
||||
* @param handle A valid client handle from a successful call to MQTTAsync_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to the callback function to
|
||||
* provide access to the context information in the callback.
|
||||
* @param co A pointer to an MQTTPersistence_beforeWrite() callback
|
||||
* function. NULL removes the callback setting.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_setAfterPersistenceRead(MQTTAsync handle, void* context, MQTTPersistence_afterRead* co);
|
||||
#endif
|
||||
|
||||
/** The data returned on completion of an unsuccessful API call in the response callback onFailure. */
|
||||
typedef struct
|
||||
{
|
||||
/** A token identifying the failed request. */
|
||||
MQTTAsync_token token;
|
||||
/** A numeric code identifying the error. */
|
||||
int code;
|
||||
/** Optional text explaining the error. Can be NULL. */
|
||||
const char *message;
|
||||
} MQTTAsync_failureData;
|
||||
|
||||
|
||||
/** The data returned on completion of an unsuccessful API call in the response callback onFailure. */
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. Will be MQFD. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Will be 0 */
|
||||
int struct_version;
|
||||
/** A token identifying the failed request. */
|
||||
MQTTAsync_token token;
|
||||
/** The MQTT reason code returned. */
|
||||
enum MQTTReasonCodes reasonCode;
|
||||
/** The MQTT properties on the ack, if any. */
|
||||
MQTTProperties properties;
|
||||
/** A numeric code identifying the MQTT client library error. */
|
||||
int code;
|
||||
/** Optional further text explaining the error. Can be NULL. */
|
||||
const char *message;
|
||||
/** Packet type on which the failure occurred - used for publish QoS 1/2 exchanges*/
|
||||
int packet_type;
|
||||
} MQTTAsync_failureData5;
|
||||
|
||||
#define MQTTAsync_failureData5_initializer {{'M', 'Q', 'F', 'D'}, 0, 0, MQTTREASONCODE_SUCCESS, MQTTProperties_initializer, 0, NULL, 0}
|
||||
|
||||
/** The data returned on completion of a successful API call in the response callback onSuccess. */
|
||||
typedef struct
|
||||
{
|
||||
/** A token identifying the successful request. Can be used to refer to the request later. */
|
||||
MQTTAsync_token token;
|
||||
/** A union of the different values that can be returned for subscribe, unsubscribe and publish. */
|
||||
union
|
||||
{
|
||||
/** For subscribe, the granted QoS of the subscription returned by the server.
|
||||
* Also for subscribeMany, if only 1 subscription was requested. */
|
||||
int qos;
|
||||
/** For subscribeMany, if more than one subscription was requested,
|
||||
* the list of granted QoSs of the subscriptions returned by the server. */
|
||||
int* qosList;
|
||||
/** For publish, the message being sent to the server. */
|
||||
struct
|
||||
{
|
||||
MQTTAsync_message message; /**< the message being sent to the server */
|
||||
char* destinationName; /**< the topic destination for the message */
|
||||
} pub;
|
||||
/* For connect, the server connected to, MQTT version used, and sessionPresent flag */
|
||||
struct
|
||||
{
|
||||
char* serverURI; /**< the connection string of the server */
|
||||
int MQTTVersion; /**< the version of MQTT being used */
|
||||
int sessionPresent; /**< the session present flag returned from the server */
|
||||
} connect;
|
||||
} alt;
|
||||
} MQTTAsync_successData;
|
||||
|
||||
|
||||
/** The data returned on completion of a successful API call in the response callback onSuccess. */
|
||||
typedef struct
|
||||
{
|
||||
char struct_id[4]; /**< The eyecatcher for this structure. Will be MQSD. */
|
||||
int struct_version; /**< The version number of this structure. Will be 0 */
|
||||
/** A token identifying the successful request. Can be used to refer to the request later. */
|
||||
MQTTAsync_token token;
|
||||
enum MQTTReasonCodes reasonCode; /**< MQTT V5 reason code returned */
|
||||
MQTTProperties properties; /**< MQTT V5 properties returned, if any */
|
||||
/** A union of the different values that can be returned for subscribe, unsubscribe and publish. */
|
||||
union
|
||||
{
|
||||
/** For subscribeMany, the list of reasonCodes returned by the server. */
|
||||
struct
|
||||
{
|
||||
int reasonCodeCount; /**< the number of reason codes in the reasonCodes array */
|
||||
enum MQTTReasonCodes* reasonCodes; /**< an array of reasonCodes */
|
||||
} sub;
|
||||
/** For publish, the message being sent to the server. */
|
||||
struct
|
||||
{
|
||||
MQTTAsync_message message; /**< the message being sent to the server */
|
||||
char* destinationName; /**< the topic destination for the message */
|
||||
} pub;
|
||||
/* For connect, the server connected to, MQTT version used, and sessionPresent flag */
|
||||
struct
|
||||
{
|
||||
char* serverURI; /**< the connection string of the server */
|
||||
int MQTTVersion; /**< the version of MQTT being used */
|
||||
int sessionPresent; /**< the session present flag returned from the server */
|
||||
} connect;
|
||||
/** For unsubscribeMany, the list of reasonCodes returned by the server. */
|
||||
struct
|
||||
{
|
||||
int reasonCodeCount; /**< the number of reason codes in the reasonCodes array */
|
||||
enum MQTTReasonCodes* reasonCodes; /**< an array of reasonCodes */
|
||||
} unsub;
|
||||
} alt;
|
||||
} MQTTAsync_successData5;
|
||||
|
||||
#define MQTTAsync_successData5_initializer {{'M', 'Q', 'S', 'D'}, 0, 0, MQTTREASONCODE_SUCCESS, MQTTProperties_initializer, {.sub={0,0}}}
|
||||
|
||||
/**
|
||||
* This is a callback function. The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* notification of the successful completion of an API call. The function is
|
||||
* registered with the client library by passing it as an argument in
|
||||
* ::MQTTAsync_responseOptions.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* ::MQTTAsync_responseOptions, which contains any application-specific context.
|
||||
* @param response Any success data associated with the API completion.
|
||||
*/
|
||||
typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response);
|
||||
|
||||
/**
|
||||
* This is a callback function, the MQTT V5 version of ::MQTTAsync_onSuccess.
|
||||
* The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* notification of the successful completion of an API call. The function is
|
||||
* registered with the client library by passing it as an argument in
|
||||
* ::MQTTAsync_responseOptions.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* ::MQTTAsync_responseOptions, which contains any application-specific context.
|
||||
* @param response Any success data associated with the API completion.
|
||||
*/
|
||||
typedef void MQTTAsync_onSuccess5(void* context, MQTTAsync_successData5* response);
|
||||
|
||||
/**
|
||||
* This is a callback function. The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* notification of the unsuccessful completion of an API call. The function is
|
||||
* registered with the client library by passing it as an argument in
|
||||
* ::MQTTAsync_responseOptions.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* ::MQTTAsync_responseOptions, which contains any application-specific context.
|
||||
* @param response Failure data associated with the API completion.
|
||||
*/
|
||||
typedef void MQTTAsync_onFailure(void* context, MQTTAsync_failureData* response);
|
||||
|
||||
/**
|
||||
* This is a callback function, the MQTT V5 version of ::MQTTAsync_onFailure.
|
||||
* The application must provide an implementation of this function to enable asynchronous
|
||||
* notification of the unsuccessful completion of an API call. The function is
|
||||
* registered with the client library by passing it as an argument in
|
||||
* ::MQTTAsync_responseOptions.
|
||||
*
|
||||
* <b>Note:</b> Neither MQTTAsync_create() nor MQTTAsync_destroy() should be
|
||||
* called within this callback.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* ::MQTTAsync_responseOptions, which contains any application-specific context.
|
||||
* @param response Failure data associated with the API completion.
|
||||
*/
|
||||
typedef void MQTTAsync_onFailure5(void* context, MQTTAsync_failureData5* response);
|
||||
|
||||
/** Structure to define call options. For MQTT 5.0 there is input data as well as that
|
||||
* describing the response method. So there is now also a synonym ::MQTTAsync_callOptions
|
||||
* to better reflect the use. This responseOptions name is kept for backward
|
||||
* compatibility.
|
||||
*/
|
||||
typedef struct MQTTAsync_responseOptions
|
||||
{
|
||||
/** The eyecatcher for this structure. Must be MQTR */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 or 1
|
||||
* if 0, no MQTTV5 options */
|
||||
int struct_version;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the API call successfully
|
||||
* completes. Can be set to NULL, in which case no indication of successful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onSuccess* onSuccess;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the API call fails.
|
||||
* Can be set to NULL, in which case no indication of unsuccessful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onFailure* onFailure;
|
||||
/**
|
||||
* A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to success or failure callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
*/
|
||||
void* context;
|
||||
/**
|
||||
* A token is returned from the call. It can be used to track
|
||||
* the state of this request, both in the callbacks and in future calls
|
||||
* such as ::MQTTAsync_waitForCompletion. This is output only - any
|
||||
* change by the application will be ignored.
|
||||
*/
|
||||
MQTTAsync_token token;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the API call successfully
|
||||
* completes. Can be set to NULL, in which case no indication of successful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onSuccess5* onSuccess5;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the API call successfully
|
||||
* completes. Can be set to NULL, in which case no indication of successful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onFailure5* onFailure5;
|
||||
/**
|
||||
* MQTT V5 input properties
|
||||
*/
|
||||
MQTTProperties properties;
|
||||
/*
|
||||
* MQTT V5 subscribe options, when used with subscribe only.
|
||||
*/
|
||||
MQTTSubscribe_options subscribeOptions;
|
||||
/*
|
||||
* MQTT V5 subscribe option count, when used with subscribeMany only.
|
||||
* The number of entries in the subscribe_options_list array.
|
||||
*/
|
||||
int subscribeOptionsCount;
|
||||
/*
|
||||
* MQTT V5 subscribe option array, when used with subscribeMany only.
|
||||
*/
|
||||
MQTTSubscribe_options* subscribeOptionsList;
|
||||
} MQTTAsync_responseOptions;
|
||||
|
||||
#define MQTTAsync_responseOptions_initializer { {'M', 'Q', 'T', 'R'}, 1, NULL, NULL, 0, 0, NULL, NULL, MQTTProperties_initializer, MQTTSubscribe_options_initializer, 0, NULL}
|
||||
|
||||
/** A synonym for responseOptions to better reflect its usage since MQTT 5.0 */
|
||||
typedef struct MQTTAsync_responseOptions MQTTAsync_callOptions;
|
||||
#define MQTTAsync_callOptions_initializer MQTTAsync_responseOptions_initializer
|
||||
|
||||
/**
|
||||
* This function sets the global callback functions for a specific client.
|
||||
* If your client application doesn't use a particular callback, set the
|
||||
* relevant parameter to NULL (except for message arrived, which must be given).
|
||||
* Any necessary message acknowledgements and status communications are handled
|
||||
* in the background without any intervention
|
||||
* from the client application.
|
||||
*
|
||||
* <b>Note:</b> The MQTT client must be disconnected when this function is
|
||||
* called.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to each of the callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
* @param cl A pointer to an MQTTAsync_connectionLost() callback
|
||||
* function. You can set this to NULL if your application doesn't handle
|
||||
* disconnections.
|
||||
* @param ma A pointer to an MQTTAsync_messageArrived() callback
|
||||
* function. If this callback is not set, an error will be returned.
|
||||
* You must set this callback because otherwise there would be
|
||||
* no way to deliver any incoming messages.
|
||||
* @param dc A pointer to an MQTTAsync_deliveryComplete() callback
|
||||
* function. You can set this to NULL if you do not want to check
|
||||
* for successful delivery.
|
||||
* @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTASYNC_FAILURE if an error occurred.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_setCallbacks(MQTTAsync handle, void* context, MQTTAsync_connectionLost* cl,
|
||||
MQTTAsync_messageArrived* ma, MQTTAsync_deliveryComplete* dc);
|
||||
|
||||
/**
|
||||
* This function sets the callback function for a connection lost event for
|
||||
* a specific client. Any necessary message acknowledgements and status
|
||||
* communications are handled in the background without any intervention
|
||||
* from the client application.
|
||||
*
|
||||
* <b>Note:</b> The MQTT client must be disconnected when this function is
|
||||
* called.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed the callback functions to provide
|
||||
* access to the context information in the callback.
|
||||
* @param cl A pointer to an MQTTAsync_connectionLost() callback
|
||||
* function. You can set this to NULL if your application doesn't handle
|
||||
* disconnections.
|
||||
* @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTASYNC_FAILURE if an error occurred.
|
||||
*/
|
||||
|
||||
LIBMQTT_API int MQTTAsync_setConnectionLostCallback(MQTTAsync handle, void* context,
|
||||
MQTTAsync_connectionLost* cl);
|
||||
|
||||
/**
|
||||
* This function sets the callback function for a message arrived event for
|
||||
* a specific client. Any necessary message acknowledgements and status
|
||||
* communications are handled in the background without any intervention
|
||||
* from the client application. If you do not set a messageArrived callback
|
||||
* function, you will not be notified of the receipt of any messages as a
|
||||
* result of a subscription.
|
||||
*
|
||||
* <b>Note:</b> The MQTT client must be disconnected when this function is
|
||||
* called.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to the callback functions to provide
|
||||
* access to the context information in the callback.
|
||||
* @param ma A pointer to an MQTTAsync_messageArrived() callback
|
||||
* function. You can set this to NULL if your application doesn't handle
|
||||
* receipt of messages.
|
||||
* @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTASYNC_FAILURE if an error occurred.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_setMessageArrivedCallback(MQTTAsync handle, void* context,
|
||||
MQTTAsync_messageArrived* ma);
|
||||
|
||||
/**
|
||||
* This function sets the callback function for a delivery complete event
|
||||
* for a specific client. Any necessary message acknowledgements and status
|
||||
* communications are handled in the background without any intervention
|
||||
* from the client application.
|
||||
*
|
||||
* <b>Note:</b> The MQTT client must be disconnected when this function is
|
||||
* called.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to the callback functions to provide
|
||||
* access to the context information in the callback.
|
||||
* @param dc A pointer to an MQTTAsync_deliveryComplete() callback
|
||||
* function. You can set this to NULL if you do not want to check
|
||||
* for successful delivery.
|
||||
* @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTASYNC_FAILURE if an error occurred.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_setDeliveryCompleteCallback(MQTTAsync handle, void* context,
|
||||
MQTTAsync_deliveryComplete* dc);
|
||||
|
||||
/**
|
||||
* Sets the MQTTAsync_connected() callback function for a client.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to each of the callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
* @param co A pointer to an MQTTAsync_connected() callback
|
||||
* function. NULL removes the callback setting.
|
||||
* @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTASYNC_FAILURE if an error occurred.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_setConnected(MQTTAsync handle, void* context, MQTTAsync_connected* co);
|
||||
|
||||
|
||||
/**
|
||||
* Reconnects a client with the previously used connect options. Connect
|
||||
* must have previously been called for this to work.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTASYNC_FAILURE if an error occurred.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_reconnect(MQTTAsync handle);
|
||||
|
||||
|
||||
/**
|
||||
* This function creates an MQTT client ready for connection to the
|
||||
* specified server and using the specified persistent storage (see
|
||||
* MQTTAsync_persistence). See also MQTTAsync_destroy().
|
||||
* @param handle A pointer to an ::MQTTAsync handle. The handle is
|
||||
* populated with a valid client reference following a successful return from
|
||||
* this function.
|
||||
* @param serverURI A null-terminated string specifying the server to
|
||||
* which the client will connect. It takes the form
|
||||
* <i>protocol://host:port</i> where <i>protocol</i> must be:
|
||||
* <br>
|
||||
* @em tcp:// or @em mqtt:// - Insecure TCP
|
||||
* <br>
|
||||
* @em ssl:// or @em tls:// or @em mqtts:// - Encrypted SSL/TLS
|
||||
* <br>
|
||||
* @em ws:// - Insecure websockets
|
||||
* <br>
|
||||
* @em wss:// - Secure web sockets
|
||||
* <br>
|
||||
* The TLS enabled prefixes (ssl, tls, mqtts, wss) are only valid if a TLS
|
||||
* version of the library is linked with.
|
||||
* For <i>host</i>, you can specify either an IP address or a host name. For
|
||||
* instance, to connect to a server running on the local machines with the
|
||||
* default MQTT port, specify <i>tcp://localhost:1883</i>.
|
||||
* @param clientId The client identifier passed to the server when the
|
||||
* client connects to it. It is a null-terminated UTF-8 encoded string.
|
||||
* @param persistence_type The type of persistence to be used by the client:
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_NONE: Use in-memory persistence. If the device or
|
||||
* system on which the client is running fails or is switched off, the current
|
||||
* state of any in-flight messages is lost and some messages may not be
|
||||
* delivered even at QoS1 and QoS2.
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_DEFAULT: Use the default (file system-based)
|
||||
* persistence mechanism. Status about in-flight messages is held in persistent
|
||||
* storage and provides some protection against message loss in the case of
|
||||
* unexpected failure.
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_USER: Use an application-specific persistence
|
||||
* implementation. Using this type of persistence gives control of the
|
||||
* persistence mechanism to the application. The application has to implement
|
||||
* the MQTTClient_persistence interface.
|
||||
* @param persistence_context If the application uses
|
||||
* ::MQTTCLIENT_PERSISTENCE_NONE persistence, this argument is unused and should
|
||||
* be set to NULL. For ::MQTTCLIENT_PERSISTENCE_DEFAULT persistence, it
|
||||
* should be set to the location of the persistence directory (if set
|
||||
* to NULL, the persistence directory used is the working directory).
|
||||
* Applications that use ::MQTTCLIENT_PERSISTENCE_USER persistence set this
|
||||
* argument to point to a valid MQTTClient_persistence structure.
|
||||
* @return ::MQTTASYNC_SUCCESS if the client is successfully created, otherwise
|
||||
* an error code is returned.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_create(MQTTAsync* handle, const char* serverURI, const char* clientId,
|
||||
int persistence_type, void* persistence_context);
|
||||
|
||||
/** Options for the ::MQTTAsync_createWithOptions call */
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQCO. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0, 1, 2 or 3
|
||||
* 0 means no MQTTVersion
|
||||
* 1 means no allowDisconnectedSendAtAnyTime, deleteOldestMessages, restoreMessages
|
||||
* 2 means no persistQoS0
|
||||
*/
|
||||
int struct_version;
|
||||
/** Whether to allow messages to be sent when the client library is not connected. */
|
||||
int sendWhileDisconnected;
|
||||
/** The maximum number of messages allowed to be buffered. This is intended to be used to
|
||||
* limit the number of messages queued while the client is not connected. It also applies
|
||||
* when the client is connected, however, so has to be greater than 0. */
|
||||
int maxBufferedMessages;
|
||||
/** Whether the MQTT version is 3.1, 3.1.1, or 5. To use V5, this must be set.
|
||||
* MQTT V5 has to be chosen here, because during the create call the message persistence
|
||||
* is initialized, and we want to know whether the format of any persisted messages
|
||||
* is appropriate for the MQTT version we are going to connect with. Selecting 3.1 or
|
||||
* 3.1.1 and attempting to read 5.0 persisted messages will result in an error on create. */
|
||||
int MQTTVersion;
|
||||
/**
|
||||
* Allow sending of messages while disconnected before a first successful connect.
|
||||
*/
|
||||
int allowDisconnectedSendAtAnyTime;
|
||||
/*
|
||||
* When the maximum number of buffered messages is reached, delete the oldest rather than the newest.
|
||||
*/
|
||||
int deleteOldestMessages;
|
||||
/*
|
||||
* Restore messages from persistence on create - or clear it.
|
||||
*/
|
||||
int restoreMessages;
|
||||
/*
|
||||
* Persist QoS0 publish commands - an option to not persist them.
|
||||
*/
|
||||
int persistQoS0;
|
||||
} MQTTAsync_createOptions;
|
||||
|
||||
#define MQTTAsync_createOptions_initializer { {'M', 'Q', 'C', 'O'}, 2, 0, 100, MQTTVERSION_DEFAULT, 0, 0, 1, 1}
|
||||
|
||||
#define MQTTAsync_createOptions_initializer5 { {'M', 'Q', 'C', 'O'}, 2, 0, 100, MQTTVERSION_5, 0, 0, 1, 1}
|
||||
|
||||
|
||||
LIBMQTT_API int MQTTAsync_createWithOptions(MQTTAsync* handle, const char* serverURI, const char* clientId,
|
||||
int persistence_type, void* persistence_context, MQTTAsync_createOptions* options);
|
||||
|
||||
/**
|
||||
* MQTTAsync_willOptions defines the MQTT "Last Will and Testament" (LWT) settings for
|
||||
* the client. In the event that a client unexpectedly loses its connection to
|
||||
* the server, the server publishes the LWT message to the LWT topic on
|
||||
* behalf of the client. This allows other clients (subscribed to the LWT topic)
|
||||
* to be made aware that the client has disconnected. To enable the LWT
|
||||
* function for a specific client, a valid pointer to an MQTTAsync_willOptions
|
||||
* structure is passed in the MQTTAsync_connectOptions structure used in the
|
||||
* MQTTAsync_connect() call that connects the client to the server. The pointer
|
||||
* to MQTTAsync_willOptions can be set to NULL if the LWT function is not
|
||||
* required.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTW. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 or 1
|
||||
0 indicates no binary will message support
|
||||
*/
|
||||
int struct_version;
|
||||
/** The LWT topic to which the LWT message will be published. */
|
||||
const char* topicName;
|
||||
/** The LWT payload. */
|
||||
const char* message;
|
||||
/**
|
||||
* The retained flag for the LWT message (see MQTTAsync_message.retained).
|
||||
*/
|
||||
int retained;
|
||||
/**
|
||||
* The quality of service setting for the LWT message (see
|
||||
* MQTTAsync_message.qos and @ref qos).
|
||||
*/
|
||||
int qos;
|
||||
/** The LWT payload in binary form. This is only checked and used if the message option is NULL */
|
||||
struct
|
||||
{
|
||||
int len; /**< binary payload length */
|
||||
const void* data; /**< binary payload data */
|
||||
} payload;
|
||||
} MQTTAsync_willOptions;
|
||||
|
||||
#define MQTTAsync_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 1, NULL, NULL, 0, 0, { 0, NULL } }
|
||||
|
||||
#define MQTT_SSL_VERSION_DEFAULT 0
|
||||
#define MQTT_SSL_VERSION_TLS_1_0 1
|
||||
#define MQTT_SSL_VERSION_TLS_1_1 2
|
||||
#define MQTT_SSL_VERSION_TLS_1_2 3
|
||||
|
||||
/**
|
||||
* MQTTAsync_sslProperties defines the settings to establish an SSL/TLS connection using the
|
||||
* OpenSSL library. It covers the following scenarios:
|
||||
* - Server authentication: The client needs the digital certificate of the server. It is included
|
||||
* in a store containting trusted material (also known as "trust store").
|
||||
* - Mutual authentication: Both client and server are authenticated during the SSL handshake. In
|
||||
* addition to the digital certificate of the server in a trust store, the client will need its own
|
||||
* digital certificate and the private key used to sign its digital certificate stored in a "key store".
|
||||
* - Anonymous connection: Both client and server do not get authenticated and no credentials are needed
|
||||
* to establish an SSL connection. Note that this scenario is not fully secure since it is subject to
|
||||
* man-in-the-middle attacks.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. Must be MQTS */
|
||||
char struct_id[4];
|
||||
|
||||
/** The version number of this structure. Must be 0, 1, 2, 3, 4 or 5.
|
||||
* 0 means no sslVersion
|
||||
* 1 means no verify, CApath
|
||||
* 2 means no ssl_error_context, ssl_error_cb
|
||||
* 3 means no ssl_psk_cb, ssl_psk_context, disableDefaultTrustStore
|
||||
* 4 means no protos, protos_len
|
||||
*/
|
||||
int struct_version;
|
||||
|
||||
/** The file in PEM format containing the public digital certificates trusted by the client. */
|
||||
const char* trustStore;
|
||||
|
||||
/** The file in PEM format containing the public certificate chain of the client. It may also include
|
||||
* the client's private key.
|
||||
*/
|
||||
const char* keyStore;
|
||||
|
||||
/** If not included in the sslKeyStore, this setting points to the file in PEM format containing
|
||||
* the client's private key.
|
||||
*/
|
||||
const char* privateKey;
|
||||
|
||||
/** The password to load the client's privateKey if encrypted. */
|
||||
const char* privateKeyPassword;
|
||||
|
||||
/**
|
||||
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
|
||||
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
|
||||
* http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
|
||||
* If this setting is ommitted, its default value will be "ALL", that is, all the cipher suites -excluding
|
||||
* those offering no encryption- will be considered.
|
||||
* This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance).
|
||||
*/
|
||||
const char* enabledCipherSuites;
|
||||
|
||||
/** True/False option to enable verification of the server certificate **/
|
||||
int enableServerCertAuth;
|
||||
|
||||
/** The SSL/TLS version to use. Specify one of MQTT_SSL_VERSION_DEFAULT (0),
|
||||
* MQTT_SSL_VERSION_TLS_1_0 (1), MQTT_SSL_VERSION_TLS_1_1 (2) or MQTT_SSL_VERSION_TLS_1_2 (3).
|
||||
* Only used if struct_version is >= 1.
|
||||
*/
|
||||
int sslVersion;
|
||||
|
||||
/**
|
||||
* Whether to carry out post-connect checks, including that a certificate
|
||||
* matches the given host name.
|
||||
* Exists only if struct_version >= 2
|
||||
*/
|
||||
int verify;
|
||||
|
||||
/**
|
||||
* From the OpenSSL documentation:
|
||||
* If CApath is not NULL, it points to a directory containing CA certificates in PEM format.
|
||||
* Exists only if struct_version >= 2
|
||||
*/
|
||||
const char* CApath;
|
||||
|
||||
/**
|
||||
* Callback function for OpenSSL error handler ERR_print_errors_cb
|
||||
* Exists only if struct_version >= 3
|
||||
*/
|
||||
int (*ssl_error_cb) (const char *str, size_t len, void *u);
|
||||
|
||||
/**
|
||||
* Application-specific contex for OpenSSL error handler ERR_print_errors_cb
|
||||
* Exists only if struct_version >= 3
|
||||
*/
|
||||
void* ssl_error_context;
|
||||
|
||||
/**
|
||||
* Callback function for setting TLS-PSK options. Parameters correspond to that of
|
||||
* SSL_CTX_set_psk_client_callback, except for u which is the pointer ssl_psk_context.
|
||||
* Exists only if struct_version >= 4
|
||||
*/
|
||||
unsigned int (*ssl_psk_cb) (const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len, void *u);
|
||||
|
||||
/**
|
||||
* Application-specific contex for ssl_psk_cb
|
||||
* Exists only if struct_version >= 4
|
||||
*/
|
||||
void* ssl_psk_context;
|
||||
|
||||
/**
|
||||
* Don't load default SSL CA. Should be used together with PSK to make sure
|
||||
* regular servers with certificate in place is not accepted.
|
||||
* Exists only if struct_version >= 4
|
||||
*/
|
||||
int disableDefaultTrustStore;
|
||||
|
||||
/**
|
||||
* The protocol-lists must be in wire-format, which is defined as a vector of non-empty, 8-bit length-prefixed, byte strings.
|
||||
* The length-prefix byte is not included in the length. Each string is limited to 255 bytes. A byte-string length of 0 is invalid.
|
||||
* A truncated byte-string is invalid.
|
||||
* Check documentation for SSL_CTX_set_alpn_protos
|
||||
* Exists only if struct_version >= 5
|
||||
*/
|
||||
const unsigned char *protos;
|
||||
|
||||
/**
|
||||
* The length of the vector protos vector
|
||||
* Exists only if struct_version >= 5
|
||||
*/
|
||||
unsigned int protos_len;
|
||||
} MQTTAsync_SSLOptions;
|
||||
|
||||
#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }
|
||||
|
||||
/** Utility structure where name/value pairs are needed */
|
||||
typedef struct
|
||||
{
|
||||
const char* name; /**< name string */
|
||||
const char* value; /**< value string */
|
||||
} MQTTAsync_nameValue;
|
||||
|
||||
/**
|
||||
* MQTTAsync_connectOptions defines several settings that control the way the
|
||||
* client connects to an MQTT server.
|
||||
*
|
||||
* Suitable default values are set in the following initializers:
|
||||
* - MQTTAsync_connectOptions_initializer: for MQTT 3.1.1 non-WebSockets
|
||||
* - MQTTAsync_connectOptions_initializer5: for MQTT 5.0 non-WebSockets
|
||||
* - MQTTAsync_connectOptions_initializer_ws: for MQTT 3.1.1 WebSockets
|
||||
* - MQTTAsync_connectOptions_initializer5_ws: for MQTT 5.0 WebSockets
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTC. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0, 1, 2, 3 4 5 6, 7 or 8.
|
||||
* 0 signifies no SSL options and no serverURIs
|
||||
* 1 signifies no serverURIs
|
||||
* 2 signifies no MQTTVersion
|
||||
* 3 signifies no automatic reconnect options
|
||||
* 4 signifies no binary password option (just string)
|
||||
* 5 signifies no MQTTV5 properties
|
||||
* 6 signifies no HTTP headers option
|
||||
* 7 signifies no HTTP proxy and HTTPS proxy options
|
||||
*/
|
||||
int struct_version;
|
||||
/** The "keep alive" interval, measured in seconds, defines the maximum time
|
||||
* that should pass without communication between the client and the server
|
||||
* The client will ensure that at least one message travels across the
|
||||
* network within each keep alive period. In the absence of a data-related
|
||||
* message during the time period, the client sends a very small MQTT
|
||||
* "ping" message, which the server will acknowledge. The keep alive
|
||||
* interval enables the client to detect when the server is no longer
|
||||
* available without having to wait for the long TCP/IP timeout.
|
||||
* Set to 0 if you do not want any keep alive processing.
|
||||
*/
|
||||
int keepAliveInterval;
|
||||
/**
|
||||
* This is a boolean value. The cleansession setting controls the behaviour
|
||||
* of both the client and the server at connection and disconnection time.
|
||||
* The client and server both maintain session state information. This
|
||||
* information is used to ensure "at least once" and "exactly once"
|
||||
* delivery, and "exactly once" receipt of messages. Session state also
|
||||
* includes subscriptions created by an MQTT client. You can choose to
|
||||
* maintain or discard state information between sessions.
|
||||
*
|
||||
* When cleansession is true, the state information is discarded at
|
||||
* connect and disconnect. Setting cleansession to false keeps the state
|
||||
* information. When you connect an MQTT client application with
|
||||
* MQTTAsync_connect(), the client identifies the connection using the
|
||||
* client identifier and the address of the server. The server checks
|
||||
* whether session information for this client
|
||||
* has been saved from a previous connection to the server. If a previous
|
||||
* session still exists, and cleansession=true, then the previous session
|
||||
* information at the client and server is cleared. If cleansession=false,
|
||||
* the previous session is resumed. If no previous session exists, a new
|
||||
* session is started.
|
||||
*/
|
||||
int cleansession;
|
||||
/**
|
||||
* This controls how many messages can be in-flight simultaneously.
|
||||
*/
|
||||
int maxInflight;
|
||||
/**
|
||||
* This is a pointer to an MQTTAsync_willOptions structure. If your
|
||||
* application does not make use of the Last Will and Testament feature,
|
||||
* set this pointer to NULL.
|
||||
*/
|
||||
MQTTAsync_willOptions* will;
|
||||
/**
|
||||
* MQTT servers that support the MQTT v3.1 protocol provide authentication
|
||||
* and authorisation by user name and password. This is the user name
|
||||
* parameter.
|
||||
*/
|
||||
const char* username;
|
||||
/**
|
||||
* MQTT servers that support the MQTT v3.1 protocol provide authentication
|
||||
* and authorisation by user name and password. This is the password
|
||||
* parameter.
|
||||
*/
|
||||
const char* password;
|
||||
/**
|
||||
* The time interval in seconds to allow a connect to complete.
|
||||
*/
|
||||
int connectTimeout;
|
||||
/**
|
||||
* The time interval in seconds after which unacknowledged publish requests are
|
||||
* retried during a TCP session. With MQTT 3.1.1 and later, retries are
|
||||
* not required except on reconnect. 0 turns off in-session retries, and is the
|
||||
* recommended setting. Adding retries to an already overloaded network only
|
||||
* exacerbates the problem.
|
||||
*/
|
||||
int retryInterval;
|
||||
/**
|
||||
* This is a pointer to an MQTTAsync_SSLOptions structure. If your
|
||||
* application does not make use of SSL, set this pointer to NULL.
|
||||
*/
|
||||
MQTTAsync_SSLOptions* ssl;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the connect successfully
|
||||
* completes. Can be set to NULL, in which case no indication of successful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onSuccess* onSuccess;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the connect fails.
|
||||
* Can be set to NULL, in which case no indication of unsuccessful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onFailure* onFailure;
|
||||
/**
|
||||
* A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to success or failure callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
*/
|
||||
void* context;
|
||||
/**
|
||||
* The number of entries in the serverURIs array.
|
||||
*/
|
||||
int serverURIcount;
|
||||
/**
|
||||
* An array of null-terminated strings specifying the servers to
|
||||
* which the client will connect. Each string takes the form <i>protocol://host:port</i>.
|
||||
* <i>protocol</i> must be <i>tcp</i>, <i>ssl</i>, <i>tls</i>, <i>ws</i> or <i>wss</i>.
|
||||
* The TLS enabled prefixes (ssl, tls, wss) are only valid if a TLS version of the library
|
||||
* is linked with.
|
||||
* For <i>host</i>, you can
|
||||
* specify either an IP address or a domain name. For instance, to connect to
|
||||
* a server running on the local machines with the default MQTT port, specify
|
||||
* <i>tcp://localhost:1883</i>.
|
||||
*/
|
||||
char* const* serverURIs;
|
||||
/**
|
||||
* Sets the version of MQTT to be used on the connect.
|
||||
* MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if that fails, fall back to 3.1
|
||||
* MQTTVERSION_3_1 (3) = only try version 3.1
|
||||
* MQTTVERSION_3_1_1 (4) = only try version 3.1.1
|
||||
*/
|
||||
int MQTTVersion;
|
||||
/**
|
||||
* Reconnect automatically in the case of a connection being lost. 0=false, 1=true
|
||||
*/
|
||||
int automaticReconnect;
|
||||
/**
|
||||
* The minimum automatic reconnect retry interval in seconds. Doubled on each failed retry.
|
||||
*/
|
||||
int minRetryInterval;
|
||||
/**
|
||||
* The maximum automatic reconnect retry interval in seconds. The doubling stops here on failed retries.
|
||||
*/
|
||||
int maxRetryInterval;
|
||||
/**
|
||||
* Optional binary password. Only checked and used if the password option is NULL
|
||||
*/
|
||||
struct {
|
||||
int len; /**< binary password length */
|
||||
const void* data; /**< binary password data */
|
||||
} binarypwd;
|
||||
/*
|
||||
* MQTT V5 clean start flag. Only clears state at the beginning of the session.
|
||||
*/
|
||||
int cleanstart;
|
||||
/**
|
||||
* MQTT V5 properties for connect
|
||||
*/
|
||||
MQTTProperties *connectProperties;
|
||||
/**
|
||||
* MQTT V5 properties for the will message in the connect
|
||||
*/
|
||||
MQTTProperties *willProperties;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the connect successfully
|
||||
* completes. Can be set to NULL, in which case no indication of successful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onSuccess5* onSuccess5;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the connect fails.
|
||||
* Can be set to NULL, in which case no indication of unsuccessful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onFailure5* onFailure5;
|
||||
/**
|
||||
* HTTP headers for websockets
|
||||
*/
|
||||
const MQTTAsync_nameValue* httpHeaders;
|
||||
/**
|
||||
* The string value of the HTTP proxy. Examples:
|
||||
* - http://your.proxy.server:8080/
|
||||
* - http://user:pass@my.proxy.server:8080/
|
||||
*/
|
||||
const char* httpProxy;
|
||||
/**
|
||||
* HTTPS proxy setting. See ::MQTTAsync_connectOptions.httpProxy and the section @ref HTTP_proxies.
|
||||
*/
|
||||
const char* httpsProxy;
|
||||
} MQTTAsync_connectOptions;
|
||||
|
||||
/** Initializer for connect options for MQTT 3.1.1 non-WebSocket connections */
|
||||
#define MQTTAsync_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 8, 60, 1, 65535, NULL, NULL, NULL, 30, 0,\
|
||||
NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_DEFAULT, 0, 1, 60, {0, NULL}, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
|
||||
|
||||
/** Initializer for connect options for MQTT 5.0 non-WebSocket connections */
|
||||
#define MQTTAsync_connectOptions_initializer5 { {'M', 'Q', 'T', 'C'}, 8, 60, 0, 65535, NULL, NULL, NULL, 30, 0,\
|
||||
NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_5, 0, 1, 60, {0, NULL}, 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
|
||||
|
||||
/** Initializer for connect options for MQTT 3.1.1 WebSockets connections.
|
||||
* The keepalive interval is set to 45 seconds to avoid webserver 60 second inactivity timeouts.
|
||||
*/
|
||||
#define MQTTAsync_connectOptions_initializer_ws { {'M', 'Q', 'T', 'C'}, 8, 45, 1, 65535, NULL, NULL, NULL, 30, 0,\
|
||||
NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_DEFAULT, 0, 1, 60, {0, NULL}, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
|
||||
|
||||
/** Initializer for connect options for MQTT 5.0 WebSockets connections.
|
||||
* The keepalive interval is set to 45 seconds to avoid webserver 60 second inactivity timeouts.
|
||||
*/
|
||||
#define MQTTAsync_connectOptions_initializer5_ws { {'M', 'Q', 'T', 'C'}, 8, 45, 0, 65535, NULL, NULL, NULL, 30, 0,\
|
||||
NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_5, 0, 1, 60, {0, NULL}, 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
|
||||
|
||||
|
||||
/**
|
||||
* This function attempts to connect a previously-created client (see
|
||||
* MQTTAsync_create()) to an MQTT server using the specified options. If you
|
||||
* want to enable asynchronous message and status notifications, you must call
|
||||
* MQTTAsync_setCallbacks() prior to MQTTAsync_connect().
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param options A pointer to a valid MQTTAsync_connectOptions
|
||||
* structure.
|
||||
* @return ::MQTTASYNC_SUCCESS if the client connect request was accepted.
|
||||
* If the client was unable to connect to the server, an error code is
|
||||
* returned via the onFailure callback, if set.
|
||||
* Error codes greater than 0 are returned by the MQTT protocol:<br><br>
|
||||
* <b>1</b>: Connection refused: Unacceptable protocol version<br>
|
||||
* <b>2</b>: Connection refused: Identifier rejected<br>
|
||||
* <b>3</b>: Connection refused: Server unavailable<br>
|
||||
* <b>4</b>: Connection refused: Bad user name or password<br>
|
||||
* <b>5</b>: Connection refused: Not authorized<br>
|
||||
* <b>6-255</b>: Reserved for future use<br>
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options);
|
||||
|
||||
/** Options for the ::MQTTAsync_disconnect call */
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. Must be MQTD. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 or 1. 0 signifies no V5 properties */
|
||||
int struct_version;
|
||||
/**
|
||||
* The client delays disconnection for up to this time (in
|
||||
* milliseconds) in order to allow in-flight message transfers to complete.
|
||||
*/
|
||||
int timeout;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the disconnect successfully
|
||||
* completes. Can be set to NULL, in which case no indication of successful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onSuccess* onSuccess;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the disconnect fails.
|
||||
* Can be set to NULL, in which case no indication of unsuccessful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onFailure* onFailure;
|
||||
/**
|
||||
* A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to success or failure callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
*/
|
||||
void* context;
|
||||
/**
|
||||
* MQTT V5 input properties
|
||||
*/
|
||||
MQTTProperties properties;
|
||||
/**
|
||||
* Reason code for MQTTV5 disconnect
|
||||
*/
|
||||
enum MQTTReasonCodes reasonCode;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the disconnect successfully
|
||||
* completes. Can be set to NULL, in which case no indication of successful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onSuccess5* onSuccess5;
|
||||
/**
|
||||
* A pointer to a callback function to be called if the disconnect fails.
|
||||
* Can be set to NULL, in which case no indication of unsuccessful
|
||||
* completion will be received.
|
||||
*/
|
||||
MQTTAsync_onFailure5* onFailure5;
|
||||
} MQTTAsync_disconnectOptions;
|
||||
|
||||
#define MQTTAsync_disconnectOptions_initializer { {'M', 'Q', 'T', 'D'}, 0, 0, NULL, NULL, NULL,\
|
||||
MQTTProperties_initializer, MQTTREASONCODE_SUCCESS, NULL, NULL }
|
||||
|
||||
#define MQTTAsync_disconnectOptions_initializer5 { {'M', 'Q', 'T', 'D'}, 1, 0, NULL, NULL, NULL,\
|
||||
MQTTProperties_initializer, MQTTREASONCODE_SUCCESS, NULL, NULL }
|
||||
|
||||
/**
|
||||
* This function attempts to disconnect the client from the MQTT
|
||||
* server. In order to allow the client time to complete handling of messages
|
||||
* that are in-flight when this function is called, a timeout period is
|
||||
* specified. When the timeout period has expired, the client disconnects even
|
||||
* if there are still outstanding message acknowledgements.
|
||||
* The next time the client connects to the same server, any QoS 1 or 2
|
||||
* messages which have not completed will be retried depending on the
|
||||
* cleansession settings for both the previous and the new connection (see
|
||||
* MQTTAsync_connectOptions.cleansession and MQTTAsync_connect()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param options The client delays disconnection for up to this time (in
|
||||
* milliseconds) in order to allow in-flight message transfers to complete.
|
||||
* @return ::MQTTASYNC_SUCCESS if the client successfully disconnects from
|
||||
* the server. An error code is returned if the client was unable to disconnect
|
||||
* from the server
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_disconnect(MQTTAsync handle, const MQTTAsync_disconnectOptions* options);
|
||||
|
||||
|
||||
/**
|
||||
* This function allows the client application to test whether or not a
|
||||
* client is currently connected to the MQTT server.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @return Boolean true if the client is connected, otherwise false.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_isConnected(MQTTAsync handle);
|
||||
|
||||
|
||||
/**
|
||||
* This function attempts to subscribe a client to a single topic, which may
|
||||
* contain wildcards (see @ref wildcard). This call also specifies the
|
||||
* @ref qos requested for the subscription
|
||||
* (see also MQTTAsync_subscribeMany()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param topic The subscription topic, which may include wildcards.
|
||||
* @param qos The requested quality of service for the subscription.
|
||||
* @param response A pointer to a response options structure. Used to set callback functions.
|
||||
* @return ::MQTTASYNC_SUCCESS if the subscription request is successful.
|
||||
* An error code is returned if there was a problem registering the
|
||||
* subscription.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_subscribe(MQTTAsync handle, const char* topic, int qos, MQTTAsync_responseOptions* response);
|
||||
|
||||
|
||||
/**
|
||||
* This function attempts to subscribe a client to a list of topics, which may
|
||||
* contain wildcards (see @ref wildcard). This call also specifies the
|
||||
* @ref qos requested for each topic (see also MQTTAsync_subscribe()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param count The number of topics for which the client is requesting
|
||||
* subscriptions.
|
||||
* @param topic An array (of length <i>count</i>) of pointers to
|
||||
* topics, each of which may include wildcards.
|
||||
* @param qos An array (of length <i>count</i>) of @ref qos
|
||||
* values. qos[n] is the requested QoS for topic[n].
|
||||
* @param response A pointer to a response options structure. Used to set callback functions.
|
||||
* @return ::MQTTASYNC_SUCCESS if the subscription request is successful.
|
||||
* An error code is returned if there was a problem registering the
|
||||
* subscriptions.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_subscribeMany(MQTTAsync handle, int count, char* const* topic, const int* qos, MQTTAsync_responseOptions* response);
|
||||
|
||||
/**
|
||||
* This function attempts to remove an existing subscription made by the
|
||||
* specified client.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param topic The topic for the subscription to be removed, which may
|
||||
* include wildcards (see @ref wildcard).
|
||||
* @param response A pointer to a response options structure. Used to set callback functions.
|
||||
* @return ::MQTTASYNC_SUCCESS if the subscription is removed.
|
||||
* An error code is returned if there was a problem removing the
|
||||
* subscription.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_unsubscribe(MQTTAsync handle, const char* topic, MQTTAsync_responseOptions* response);
|
||||
|
||||
/**
|
||||
* This function attempts to remove existing subscriptions to a list of topics
|
||||
* made by the specified client.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param count The number subscriptions to be removed.
|
||||
* @param topic An array (of length <i>count</i>) of pointers to the topics of
|
||||
* the subscriptions to be removed, each of which may include wildcards.
|
||||
* @param response A pointer to a response options structure. Used to set callback functions.
|
||||
* @return ::MQTTASYNC_SUCCESS if the subscriptions are removed.
|
||||
* An error code is returned if there was a problem removing the subscriptions.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_unsubscribeMany(MQTTAsync handle, int count, char* const* topic, MQTTAsync_responseOptions* response);
|
||||
|
||||
|
||||
/**
|
||||
* This function attempts to publish a message to a given topic (see also
|
||||
* ::MQTTAsync_sendMessage()). An ::MQTTAsync_token is issued when
|
||||
* this function returns successfully if the QoS is greater than 0.
|
||||
* If the client application needs to
|
||||
* test for successful delivery of messages, a callback should be set
|
||||
* (see ::MQTTAsync_onSuccess() and ::MQTTAsync_deliveryComplete()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param destinationName The topic associated with this message.
|
||||
* @param payloadlen The length of the payload in bytes.
|
||||
* @param payload A pointer to the byte array payload of the message.
|
||||
* @param qos The @ref qos of the message.
|
||||
* @param retained The retained flag for the message.
|
||||
* @param response A pointer to an ::MQTTAsync_responseOptions structure. Used to set callback functions.
|
||||
* This is optional and can be set to NULL.
|
||||
* @return ::MQTTASYNC_SUCCESS if the message is accepted for publication.
|
||||
* An error code is returned if there was a problem accepting the message.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_send(MQTTAsync handle, const char* destinationName, int payloadlen, const void* payload, int qos,
|
||||
int retained, MQTTAsync_responseOptions* response);
|
||||
|
||||
/**
|
||||
* This function attempts to publish a message to a given topic (see also
|
||||
* MQTTAsync_publish()). An ::MQTTAsync_token is issued when
|
||||
* this function returns successfully if the QoS is greater than 0.
|
||||
* If the client application needs to
|
||||
* test for successful delivery of messages, a callback should be set
|
||||
* (see ::MQTTAsync_onSuccess() and ::MQTTAsync_deliveryComplete()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param destinationName The topic associated with this message.
|
||||
* @param msg A pointer to a valid MQTTAsync_message structure containing
|
||||
* the payload and attributes of the message to be published.
|
||||
* @param response A pointer to an ::MQTTAsync_responseOptions structure. Used to set callback functions.
|
||||
* @return ::MQTTASYNC_SUCCESS if the message is accepted for publication.
|
||||
* An error code is returned if there was a problem accepting the message.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_sendMessage(MQTTAsync handle, const char* destinationName, const MQTTAsync_message* msg, MQTTAsync_responseOptions* response);
|
||||
|
||||
|
||||
/**
|
||||
* This function sets a pointer to an array of tokens for
|
||||
* messages that are currently in-flight (pending completion).
|
||||
*
|
||||
* <b>Important note:</b> The memory used to hold the array of tokens is
|
||||
* malloc()'d in this function. The client application is responsible for
|
||||
* freeing this memory when it is no longer required.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param tokens The address of a pointer to an ::MQTTAsync_token.
|
||||
* When the function returns successfully, the pointer is set to point to an
|
||||
* array of tokens representing messages pending completion. The last member of
|
||||
* the array is set to -1 to indicate there are no more tokens. If no tokens
|
||||
* are pending, the pointer is set to NULL.
|
||||
* @return ::MQTTASYNC_SUCCESS if the function returns successfully.
|
||||
* An error code is returned if there was a problem obtaining the list of
|
||||
* pending tokens.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_getPendingTokens(MQTTAsync handle, MQTTAsync_token **tokens);
|
||||
|
||||
/**
|
||||
* Tests whether a request corresponding to a token is complete.
|
||||
*
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param token An ::MQTTAsync_token associated with a request.
|
||||
* @return 1 if the request has been completed, 0 if not.
|
||||
*/
|
||||
#define MQTTASYNC_TRUE 1
|
||||
LIBMQTT_API int MQTTAsync_isComplete(MQTTAsync handle, MQTTAsync_token token);
|
||||
|
||||
|
||||
/**
|
||||
* Waits for a request corresponding to a token to complete. This only works for
|
||||
* messages with QoS greater than 0. A QoS 0 message has no MQTT token.
|
||||
* This function will always return ::MQTTASYNC_SUCCESS for a QoS 0 message.
|
||||
*
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTAsync_create().
|
||||
* @param token An ::MQTTAsync_token associated with a request.
|
||||
* @param timeout the maximum time to wait for completion, in milliseconds
|
||||
* @return ::MQTTASYNC_SUCCESS if the request has been completed in the time allocated,
|
||||
* ::MQTTASYNC_FAILURE or ::MQTTASYNC_DISCONNECTED if not.
|
||||
*/
|
||||
LIBMQTT_API int MQTTAsync_waitForCompletion(MQTTAsync handle, MQTTAsync_token token, unsigned long timeout);
|
||||
|
||||
|
||||
/**
|
||||
* This function frees memory allocated to an MQTT message, including the
|
||||
* additional memory allocated to the message payload. The client application
|
||||
* calls this function when the message has been fully processed. <b>Important
|
||||
* note:</b> This function does not free the memory allocated to a message
|
||||
* topic string. It is the responsibility of the client application to free
|
||||
* this memory using the MQTTAsync_free() library function.
|
||||
* @param msg The address of a pointer to the ::MQTTAsync_message structure
|
||||
* to be freed.
|
||||
*/
|
||||
LIBMQTT_API void MQTTAsync_freeMessage(MQTTAsync_message** msg);
|
||||
|
||||
/**
|
||||
* This function frees memory allocated by the MQTT C client library, especially the
|
||||
* topic name. This is needed on Windows when the client library and application
|
||||
* program have been compiled with different versions of the C compiler. It is
|
||||
* thus good policy to always use this function when freeing any MQTT C client-
|
||||
* allocated memory.
|
||||
* @param ptr The pointer to the client library storage to be freed.
|
||||
*/
|
||||
LIBMQTT_API void MQTTAsync_free(void* ptr);
|
||||
|
||||
/**
|
||||
* This function is used to allocate memory to be used or freed by the MQTT C client library,
|
||||
* especially the data in the ::MQTTPersistence_afterRead and ::MQTTPersistence_beforeWrite
|
||||
* callbacks. This is needed on Windows when the client library and application
|
||||
* program have been compiled with different versions of the C compiler.
|
||||
* @param size The size of the memory to be allocated.
|
||||
*/
|
||||
LIBMQTT_API void* MQTTAsync_malloc(size_t size);
|
||||
|
||||
/**
|
||||
* This function frees the memory allocated to an MQTT client (see
|
||||
* MQTTAsync_create()). It should be called when the client is no longer
|
||||
* required.
|
||||
* @param handle A pointer to the handle referring to the ::MQTTAsync
|
||||
* structure to be freed.
|
||||
*/
|
||||
LIBMQTT_API void MQTTAsync_destroy(MQTTAsync* handle);
|
||||
|
||||
|
||||
|
||||
enum MQTTASYNC_TRACE_LEVELS
|
||||
{
|
||||
MQTTASYNC_TRACE_MAXIMUM = 1,
|
||||
MQTTASYNC_TRACE_MEDIUM,
|
||||
MQTTASYNC_TRACE_MINIMUM,
|
||||
MQTTASYNC_TRACE_PROTOCOL,
|
||||
MQTTASYNC_TRACE_ERROR,
|
||||
MQTTASYNC_TRACE_SEVERE,
|
||||
MQTTASYNC_TRACE_FATAL,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function sets the level of trace information which will be
|
||||
* returned in the trace callback.
|
||||
* @param level the trace level required
|
||||
*/
|
||||
LIBMQTT_API void MQTTAsync_setTraceLevel(enum MQTTASYNC_TRACE_LEVELS level);
|
||||
|
||||
|
||||
/**
|
||||
* This is a callback function prototype which must be implemented if you want
|
||||
* to receive trace information. Do not invoke any other Paho API calls in this
|
||||
* callback function - unpredictable behavior may result.
|
||||
* @param level the trace level of the message returned
|
||||
* @param message the trace message. This is a pointer to a static buffer which
|
||||
* will be overwritten on each call. You must copy the data if you want to keep
|
||||
* it for later.
|
||||
*/
|
||||
typedef void MQTTAsync_traceCallback(enum MQTTASYNC_TRACE_LEVELS level, char* message);
|
||||
|
||||
/**
|
||||
* This function sets the trace callback if needed. If set to NULL,
|
||||
* no trace information will be returned. The default trace level is
|
||||
* MQTTASYNC_TRACE_MINIMUM.
|
||||
* @param callback a pointer to the function which will handle the trace information
|
||||
*/
|
||||
LIBMQTT_API void MQTTAsync_setTraceCallback(MQTTAsync_traceCallback* callback);
|
||||
|
||||
/**
|
||||
* This function returns version information about the library.
|
||||
* no trace information will be returned. The default trace level is
|
||||
* MQTTASYNC_TRACE_MINIMUM
|
||||
* @return an array of strings describing the library. The last entry is a NULL pointer.
|
||||
*/
|
||||
LIBMQTT_API MQTTAsync_nameValue* MQTTAsync_getVersionInfo(void);
|
||||
|
||||
/**
|
||||
* Returns a pointer to a string representation of the error code, or NULL.
|
||||
* Do not free after use. Returns NULL if the error code is unknown.
|
||||
* @param code the MQTTASYNC_ return code.
|
||||
* @return a static string representation of the error code.
|
||||
*/
|
||||
LIBMQTT_API const char* MQTTAsync_strerror(int code);
|
||||
|
||||
|
||||
/*!
|
||||
* @cond MQTTAsync_main
|
||||
* @page async Threading
|
||||
* The client application runs on several threads.
|
||||
* Processing of handshaking and maintaining
|
||||
* the network connection is performed in the background.
|
||||
* This API is thread safe: functions may be called by multiple application
|
||||
* threads.
|
||||
* Notifications of status and message reception are provided to the client
|
||||
* application using callbacks registered with the library by the call to
|
||||
* MQTTAsync_setCallbacks() (see MQTTAsync_messageArrived(),
|
||||
* MQTTAsync_connectionLost() and MQTTAsync_deliveryComplete()).
|
||||
* In addition, some functions allow success and failure callbacks to be set
|
||||
* for individual requests, in the ::MQTTAsync_responseOptions structure. Applications
|
||||
* can be written as a chain of callback functions.
|
||||
*
|
||||
* @page callbacks Callbacks
|
||||
* Any function from this API may be used within a callback. It is not advisable to
|
||||
* use ::MQTTAsync_waitForCompletion within a callback, however, as it is the only
|
||||
* API call that may take some time to complete, which may cause unpredictable
|
||||
* behaviour. All the other API calls are intended to complete quickly, starting
|
||||
* a request in the background, with success or failure notified by other callbacks.
|
||||
*
|
||||
* If no callbacks are assigned, this will include the message arrived callback.
|
||||
* This could be done if the application is a pure publisher, and does
|
||||
* not subscribe to any topics. If however messages are received, and no message
|
||||
* arrived callback is set, then those messages will accumulate
|
||||
* and take up memory, as there is no place for them to be delivered.
|
||||
* A log message will be written to highlight the issue, but it is up
|
||||
* to the application to protect against this situation.
|
||||
*
|
||||
* @page auto_reconnect Automatic Reconnect
|
||||
* The ability for the client library to reconnect automatically in the event
|
||||
* of a connection failure was added in 1.1. The connection lost callback
|
||||
* allows a flexible response to the loss of a connection, so almost any
|
||||
* behaviour can be implemented in that way. Automatic reconnect does have the
|
||||
* advantage of being a little simpler to use.
|
||||
*
|
||||
* To switch on automatic reconnect, the connect options field
|
||||
* automaticReconnect should be set to non-zero. The minimum and maximum times
|
||||
* before the next connection attempt can also be set, the defaults being 1 and
|
||||
* 60 seconds. At each failure to reconnect, the retry interval is doubled until
|
||||
* the maximum value is reached, and there it stays until the connection is
|
||||
* successfully re-established whereupon it is reset.
|
||||
*
|
||||
* When a reconnection attempt is successful, the ::MQTTAsync_connected callback
|
||||
* function is invoked, if set by calling ::MQTTAsync_setConnected. This allows
|
||||
* the application to take any actions needed, such as amending subscriptions.
|
||||
*
|
||||
* @page offline_publish Publish While Disconnected
|
||||
* This feature was not originally available because with persistence enabled,
|
||||
* messages could be stored locally without ever knowing if they could be sent.
|
||||
* The client application could have created the client with an erroneous broker
|
||||
* address or port for instance.
|
||||
*
|
||||
* To enable messages to be published when the application is disconnected
|
||||
* ::MQTTAsync_createWithOptions must be used instead of ::MQTTAsync_create to
|
||||
* create the client object. The ::MQTTAsync_createOptions field sendWhileDisconnected
|
||||
* must be set to non-zero, and the maxBufferedMessages field set as required -
|
||||
* the default being 100.
|
||||
*
|
||||
* ::MQTTAsync_getPendingTokens can be called to return the ids of the messages
|
||||
* waiting to be sent, or for which the sending process has not completed.
|
||||
*
|
||||
* @page wildcard Subscription wildcards
|
||||
* Every MQTT message includes a topic that classifies it. MQTT servers use
|
||||
* topics to determine which subscribers should receive messages published to
|
||||
* the server.
|
||||
*
|
||||
* Consider the server receiving messages from several environmental sensors.
|
||||
* Each sensor publishes its measurement data as a message with an associated
|
||||
* topic. Subscribing applications need to know which sensor originally
|
||||
* published each received message. A unique topic is thus used to identify
|
||||
* each sensor and measurement type. Topics such as SENSOR1TEMP,
|
||||
* SENSOR1HUMIDITY, SENSOR2TEMP and so on achieve this but are not very
|
||||
* flexible. If additional sensors are added to the system at a later date,
|
||||
* subscribing applications must be modified to receive them.
|
||||
*
|
||||
* To provide more flexibility, MQTT supports a hierarchical topic namespace.
|
||||
* This allows application designers to organize topics to simplify their
|
||||
* management. Levels in the hierarchy are delimited by the '/' character,
|
||||
* such as SENSOR/1/HUMIDITY. Publishers and subscribers use these
|
||||
* hierarchical topics as already described.
|
||||
*
|
||||
* For subscriptions, two wildcard characters are supported:
|
||||
* <ul>
|
||||
* <li>A '#' character represents a complete sub-tree of the hierarchy and
|
||||
* thus must be the last character in a subscription topic string, such as
|
||||
* SENSOR/#. This will match any topic starting with SENSOR/, such as
|
||||
* SENSOR/1/TEMP and SENSOR/2/HUMIDITY.</li>
|
||||
* <li> A '+' character represents a single level of the hierarchy and is
|
||||
* used between delimiters. For example, SENSOR/+/TEMP will match
|
||||
* SENSOR/1/TEMP and SENSOR/2/TEMP.</li>
|
||||
* </ul>
|
||||
* Publishers are not allowed to use the wildcard characters in their topic
|
||||
* names.
|
||||
*
|
||||
* Deciding on your topic hierarchy is an important step in your system design.
|
||||
*
|
||||
* @page qos Quality of service
|
||||
* The MQTT protocol provides three qualities of service for delivering
|
||||
* messages between clients and servers: "at most once", "at least once" and
|
||||
* "exactly once".
|
||||
*
|
||||
* Quality of service (QoS) is an attribute of an individual message being
|
||||
* published. An application sets the QoS for a specific message by setting the
|
||||
* MQTTAsync_message.qos field to the required value.
|
||||
*
|
||||
* A subscribing client can set the maximum quality of service a server uses
|
||||
* to send messages that match the client subscriptions. The
|
||||
* MQTTAsync_subscribe() and MQTTAsync_subscribeMany() functions set this
|
||||
* maximum. The QoS of a message forwarded to a subscriber thus might be
|
||||
* different to the QoS given to the message by the original publisher.
|
||||
* The lower of the two values is used to forward a message.
|
||||
*
|
||||
* The three levels are:
|
||||
*
|
||||
* <b>QoS0, At most once:</b> The message is delivered at most once, or it
|
||||
* may not be delivered at all. Its delivery across the network is not
|
||||
* acknowledged. The message is not stored. The message could be lost if the
|
||||
* client is disconnected, or if the server fails. QoS0 is the fastest mode of
|
||||
* transfer. It is sometimes called "fire and forget".
|
||||
*
|
||||
* The MQTT protocol does not require servers to forward publications at QoS0
|
||||
* to a client. If the client is disconnected at the time the server receives
|
||||
* the publication, the publication might be discarded, depending on the
|
||||
* server implementation.
|
||||
*
|
||||
* <b>QoS1, At least once:</b> The message is always delivered at least once.
|
||||
* It might be delivered multiple times if there is a failure before an
|
||||
* acknowledgment is received by the sender. The message must be stored
|
||||
* locally at the sender, until the sender receives confirmation that the
|
||||
* message has been published by the receiver. The message is stored in case
|
||||
* the message must be sent again.
|
||||
*
|
||||
* <b>QoS2, Exactly once:</b> The message is always delivered exactly once.
|
||||
* The message must be stored locally at the sender, until the sender receives
|
||||
* confirmation that the message has been published by the receiver. The
|
||||
* message is stored in case the message must be sent again. QoS2 is the
|
||||
* safest, but slowest mode of transfer. A more sophisticated handshaking
|
||||
* and acknowledgement sequence is used than for QoS1 to ensure no duplication
|
||||
* of messages occurs.
|
||||
* @page publish Publication example
|
||||
@code
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "MQTTAsync.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WRS_KERNEL)
|
||||
#include <OsWrapper.h>
|
||||
#endif
|
||||
|
||||
#define ADDRESS "tcp://test.mosquitto.org:1883"
|
||||
#define CLIENTID "ExampleClientPub"
|
||||
#define TOPIC "MQTT Examples"
|
||||
#define PAYLOAD "Hello World!"
|
||||
#define QOS 1
|
||||
#define TIMEOUT 10000L
|
||||
|
||||
int finished = 0;
|
||||
|
||||
void connlost(void *context, char *cause)
|
||||
{
|
||||
MQTTAsync client = (MQTTAsync)context;
|
||||
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
|
||||
int rc;
|
||||
|
||||
printf("\nConnection lost\n");
|
||||
printf(" cause: %s\n", cause);
|
||||
|
||||
printf("Reconnecting\n");
|
||||
conn_opts.keepAliveInterval = 20;
|
||||
conn_opts.cleansession = 1;
|
||||
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start connect, return code %d\n", rc);
|
||||
finished = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void onDisconnectFailure(void* context, MQTTAsync_failureData* response)
|
||||
{
|
||||
printf("Disconnect failed\n");
|
||||
finished = 1;
|
||||
}
|
||||
|
||||
void onDisconnect(void* context, MQTTAsync_successData* response)
|
||||
{
|
||||
printf("Successful disconnection\n");
|
||||
finished = 1;
|
||||
}
|
||||
|
||||
void onSendFailure(void* context, MQTTAsync_failureData* response)
|
||||
{
|
||||
MQTTAsync client = (MQTTAsync)context;
|
||||
MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
|
||||
int rc;
|
||||
|
||||
printf("Message send failed token %d error code %d\n", response->token, response->code);
|
||||
opts.onSuccess = onDisconnect;
|
||||
opts.onFailure = onDisconnectFailure;
|
||||
opts.context = client;
|
||||
if ((rc = MQTTAsync_disconnect(client, &opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start disconnect, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void onSend(void* context, MQTTAsync_successData* response)
|
||||
{
|
||||
MQTTAsync client = (MQTTAsync)context;
|
||||
MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
|
||||
int rc;
|
||||
|
||||
printf("Message with token value %d delivery confirmed\n", response->token);
|
||||
opts.onSuccess = onDisconnect;
|
||||
opts.onFailure = onDisconnectFailure;
|
||||
opts.context = client;
|
||||
if ((rc = MQTTAsync_disconnect(client, &opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start disconnect, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void onConnectFailure(void* context, MQTTAsync_failureData* response)
|
||||
{
|
||||
printf("Connect failed, rc %d\n", response ? response->code : 0);
|
||||
finished = 1;
|
||||
}
|
||||
|
||||
|
||||
void onConnect(void* context, MQTTAsync_successData* response)
|
||||
{
|
||||
MQTTAsync client = (MQTTAsync)context;
|
||||
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
|
||||
MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
|
||||
int rc;
|
||||
|
||||
printf("Successful connection\n");
|
||||
opts.onSuccess = onSend;
|
||||
opts.onFailure = onSendFailure;
|
||||
opts.context = client;
|
||||
pubmsg.payload = PAYLOAD;
|
||||
pubmsg.payloadlen = (int)strlen(PAYLOAD);
|
||||
pubmsg.qos = QOS;
|
||||
pubmsg.retained = 0;
|
||||
if ((rc = MQTTAsync_sendMessage(client, TOPIC, &pubmsg, &opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start sendMessage, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int messageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* m)
|
||||
{
|
||||
// not expecting any messages
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
MQTTAsync client;
|
||||
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
|
||||
int rc;
|
||||
|
||||
if ((rc = MQTTAsync_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to create client object, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if ((rc = MQTTAsync_setCallbacks(client, NULL, connlost, messageArrived, NULL)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to set callback, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
conn_opts.keepAliveInterval = 20;
|
||||
conn_opts.cleansession = 1;
|
||||
conn_opts.onSuccess = onConnect;
|
||||
conn_opts.onFailure = onConnectFailure;
|
||||
conn_opts.context = client;
|
||||
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start connect, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Waiting for publication of %s\n"
|
||||
"on topic %s for client with ClientID: %s\n",
|
||||
PAYLOAD, TOPIC, CLIENTID);
|
||||
while (!finished)
|
||||
#if defined(_WIN32)
|
||||
Sleep(100);
|
||||
#else
|
||||
usleep(10000L);
|
||||
#endif
|
||||
|
||||
MQTTAsync_destroy(&client);
|
||||
return rc;
|
||||
}
|
||||
|
||||
* @endcode
|
||||
* @page subscribe Subscription example
|
||||
@code
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "MQTTAsync.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WRS_KERNEL)
|
||||
#include <OsWrapper.h>
|
||||
#endif
|
||||
|
||||
#define ADDRESS "tcp://test.mosquitto.org:1883"
|
||||
#define CLIENTID "ExampleClientSub"
|
||||
#define TOPIC "MQTT Examples"
|
||||
#define PAYLOAD "Hello World!"
|
||||
#define QOS 1
|
||||
#define TIMEOUT 10000L
|
||||
|
||||
int disc_finished = 0;
|
||||
int subscribed = 0;
|
||||
int finished = 0;
|
||||
|
||||
void connlost(void *context, char *cause)
|
||||
{
|
||||
MQTTAsync client = (MQTTAsync)context;
|
||||
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
|
||||
int rc;
|
||||
|
||||
printf("\nConnection lost\n");
|
||||
if (cause)
|
||||
printf(" cause: %s\n", cause);
|
||||
|
||||
printf("Reconnecting\n");
|
||||
conn_opts.keepAliveInterval = 20;
|
||||
conn_opts.cleansession = 1;
|
||||
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start connect, return code %d\n", rc);
|
||||
finished = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int msgarrvd(void *context, char *topicName, int topicLen, MQTTAsync_message *message)
|
||||
{
|
||||
printf("Message arrived\n");
|
||||
printf(" topic: %s\n", topicName);
|
||||
printf(" message: %.*s\n", message->payloadlen, (char*)message->payload);
|
||||
MQTTAsync_freeMessage(&message);
|
||||
MQTTAsync_free(topicName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void onDisconnectFailure(void* context, MQTTAsync_failureData* response)
|
||||
{
|
||||
printf("Disconnect failed, rc %d\n", response->code);
|
||||
disc_finished = 1;
|
||||
}
|
||||
|
||||
void onDisconnect(void* context, MQTTAsync_successData* response)
|
||||
{
|
||||
printf("Successful disconnection\n");
|
||||
disc_finished = 1;
|
||||
}
|
||||
|
||||
void onSubscribe(void* context, MQTTAsync_successData* response)
|
||||
{
|
||||
printf("Subscribe succeeded\n");
|
||||
subscribed = 1;
|
||||
}
|
||||
|
||||
void onSubscribeFailure(void* context, MQTTAsync_failureData* response)
|
||||
{
|
||||
printf("Subscribe failed, rc %d\n", response->code);
|
||||
finished = 1;
|
||||
}
|
||||
|
||||
|
||||
void onConnectFailure(void* context, MQTTAsync_failureData* response)
|
||||
{
|
||||
printf("Connect failed, rc %d\n", response->code);
|
||||
finished = 1;
|
||||
}
|
||||
|
||||
|
||||
void onConnect(void* context, MQTTAsync_successData* response)
|
||||
{
|
||||
MQTTAsync client = (MQTTAsync)context;
|
||||
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
|
||||
int rc;
|
||||
|
||||
printf("Successful connection\n");
|
||||
|
||||
printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
|
||||
"Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
|
||||
opts.onSuccess = onSubscribe;
|
||||
opts.onFailure = onSubscribeFailure;
|
||||
opts.context = client;
|
||||
if ((rc = MQTTAsync_subscribe(client, TOPIC, QOS, &opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start subscribe, return code %d\n", rc);
|
||||
finished = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
MQTTAsync client;
|
||||
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
|
||||
MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer;
|
||||
int rc;
|
||||
int ch;
|
||||
|
||||
if ((rc = MQTTAsync_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL))
|
||||
!= MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to create client, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((rc = MQTTAsync_setCallbacks(client, client, connlost, msgarrvd, NULL)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to set callbacks, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto destroy_exit;
|
||||
}
|
||||
|
||||
conn_opts.keepAliveInterval = 20;
|
||||
conn_opts.cleansession = 1;
|
||||
conn_opts.onSuccess = onConnect;
|
||||
conn_opts.onFailure = onConnectFailure;
|
||||
conn_opts.context = client;
|
||||
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start connect, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto destroy_exit;
|
||||
}
|
||||
|
||||
while (!subscribed && !finished)
|
||||
#if defined(_WIN32)
|
||||
Sleep(100);
|
||||
#else
|
||||
usleep(10000L);
|
||||
#endif
|
||||
|
||||
if (finished)
|
||||
goto exit;
|
||||
|
||||
do
|
||||
{
|
||||
ch = getchar();
|
||||
} while (ch!='Q' && ch != 'q');
|
||||
|
||||
disc_opts.onSuccess = onDisconnect;
|
||||
disc_opts.onFailure = onDisconnectFailure;
|
||||
if ((rc = MQTTAsync_disconnect(client, &disc_opts)) != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
printf("Failed to start disconnect, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto destroy_exit;
|
||||
}
|
||||
while (!disc_finished)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
Sleep(100);
|
||||
#else
|
||||
usleep(10000L);
|
||||
#endif
|
||||
}
|
||||
|
||||
destroy_exit:
|
||||
MQTTAsync_destroy(&client);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
* @endcode
|
||||
* @page tracing Tracing
|
||||
*
|
||||
* Runtime tracing can be controlled by environment variables or API calls.
|
||||
*
|
||||
* #### Environment variables
|
||||
*
|
||||
* Tracing is switched on by setting the MQTT_C_CLIENT_TRACE environment variable.
|
||||
* A value of ON, or stdout, prints to stdout, any other value is interpreted as a file name to use.
|
||||
*
|
||||
* The amount of trace detail is controlled with the MQTT_C_CLIENT_TRACE_LEVEL environment
|
||||
* variable - valid values are ERROR, PROTOCOL, MINIMUM, MEDIUM and MAXIMUM
|
||||
* (from least to most verbose).
|
||||
*
|
||||
* The variable MQTT_C_CLIENT_TRACE_MAX_LINES limits the number of lines of trace that are output
|
||||
* to a file. Two files are used at most, when they are full, the last one is overwritten with the
|
||||
* new trace entries. The default size is 1000 lines.
|
||||
*
|
||||
* #### Trace API calls
|
||||
*
|
||||
* MQTTAsync_traceCallback() is used to set a callback function which is called whenever trace
|
||||
* information is available. This will be the same information as that printed if the
|
||||
* environment variables were used to control the trace.
|
||||
*
|
||||
* The MQTTAsync_setTraceLevel() calls is used to set the maximum level of trace entries that will be
|
||||
* passed to the callback function. The levels are:
|
||||
* 1. ::MQTTASYNC_TRACE_MAXIMUM
|
||||
* 2. ::MQTTASYNC_TRACE_MEDIUM
|
||||
* 3. ::MQTTASYNC_TRACE_MINIMUM
|
||||
* 4. ::MQTTASYNC_TRACE_PROTOCOL
|
||||
* 5. ::MQTTASYNC_TRACE_ERROR
|
||||
* 6. ::MQTTASYNC_TRACE_SEVERE
|
||||
* 7. ::MQTTASYNC_TRACE_FATAL
|
||||
*
|
||||
* Selecting ::MQTTASYNC_TRACE_MAXIMUM will cause all trace entries at all levels to be returned.
|
||||
* Choosing ::MQTTASYNC_TRACE_ERROR will cause ERROR, SEVERE and FATAL trace entries to be returned
|
||||
* to the callback function.
|
||||
*
|
||||
* ### MQTT Packet Tracing
|
||||
*
|
||||
* A feature that can be very useful is printing the MQTT packets that are sent and received. To
|
||||
* achieve this, use the following environment variable settings:
|
||||
* @code
|
||||
MQTT_C_CLIENT_TRACE=ON
|
||||
MQTT_C_CLIENT_TRACE_LEVEL=PROTOCOL
|
||||
* @endcode
|
||||
* The output you should see looks like this:
|
||||
* @code
|
||||
20130528 155936.813 3 stdout-subscriber -> CONNECT cleansession: 1 (0)
|
||||
20130528 155936.813 3 stdout-subscriber <- CONNACK rc: 0
|
||||
20130528 155936.813 3 stdout-subscriber -> SUBSCRIBE msgid: 1 (0)
|
||||
20130528 155936.813 3 stdout-subscriber <- SUBACK msgid: 1
|
||||
20130528 155941.818 3 stdout-subscriber -> DISCONNECT (0)
|
||||
* @endcode
|
||||
* where the fields are:
|
||||
* 1. date
|
||||
* 2. time
|
||||
* 3. socket number
|
||||
* 4. client id
|
||||
* 5. direction (-> from client to server, <- from server to client)
|
||||
* 6. packet details
|
||||
*
|
||||
* ### Default Level Tracing
|
||||
*
|
||||
* This is an extract of a default level trace of a call to connect:
|
||||
* @code
|
||||
19700101 010000.000 (1152206656) (0)> MQTTClient_connect:893
|
||||
19700101 010000.000 (1152206656) (1)> MQTTClient_connectURI:716
|
||||
20130528 160447.479 Connecting to serverURI localhost:1883
|
||||
20130528 160447.479 (1152206656) (2)> MQTTProtocol_connect:98
|
||||
20130528 160447.479 (1152206656) (3)> MQTTProtocol_addressPort:48
|
||||
20130528 160447.479 (1152206656) (3)< MQTTProtocol_addressPort:73
|
||||
20130528 160447.479 (1152206656) (3)> Socket_new:599
|
||||
20130528 160447.479 New socket 4 for localhost, port 1883
|
||||
20130528 160447.479 (1152206656) (4)> Socket_addSocket:163
|
||||
20130528 160447.479 (1152206656) (5)> Socket_setnonblocking:73
|
||||
20130528 160447.479 (1152206656) (5)< Socket_setnonblocking:78 (0)
|
||||
20130528 160447.479 (1152206656) (4)< Socket_addSocket:176 (0)
|
||||
20130528 160447.479 (1152206656) (4)> Socket_error:95
|
||||
20130528 160447.479 (1152206656) (4)< Socket_error:104 (115)
|
||||
20130528 160447.479 Connect pending
|
||||
20130528 160447.479 (1152206656) (3)< Socket_new:683 (115)
|
||||
20130528 160447.479 (1152206656) (2)< MQTTProtocol_connect:131 (115)
|
||||
* @endcode
|
||||
* where the fields are:
|
||||
* 1. date
|
||||
* 2. time
|
||||
* 3. thread id
|
||||
* 4. function nesting level
|
||||
* 5. function entry (>) or exit (<)
|
||||
* 6. function name : line of source code file
|
||||
* 7. return value (if there is one)
|
||||
*
|
||||
* ### Memory Allocation Tracing
|
||||
*
|
||||
* Setting the trace level to maximum causes memory allocations and frees to be traced along with
|
||||
* the default trace entries, with messages like the following:
|
||||
* @code
|
||||
20130528 161819.657 Allocating 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 177 ptr 0x179f930
|
||||
|
||||
20130528 161819.657 Freeing 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 201, heap use now 896 bytes
|
||||
* @endcode
|
||||
* When the last MQTT client object is destroyed, if the trace is being recorded
|
||||
* and all memory allocated by the client library has not been freed, an error message will be
|
||||
* written to the trace. This can help with fixing memory leaks. The message will look like this:
|
||||
* @code
|
||||
20130528 163909.208 Some memory not freed at shutdown, possible memory leak
|
||||
20130528 163909.208 Heap scan start, total 880 bytes
|
||||
20130528 163909.208 Heap element size 32, line 354, file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c, ptr 0x260cb00
|
||||
20130528 163909.208 Content
|
||||
20130528 163909.209 Heap scan end
|
||||
* @endcode
|
||||
* @endcond
|
||||
*
|
||||
√* @page HTTP_proxies HTTP Proxies
|
||||
* The use of HTTP proxies can be controlled by environment variables or API calls.
|
||||
*
|
||||
* The ::MQTTAsync_connectOptions.httpProxy and ::MQTTAsync_connectOptions.httpsProxy fields
|
||||
* of the ::MQTTAsync_connectOptions structure override any settings in the environment.
|
||||
*
|
||||
* If the environment variable PAHO_C_CLIENT_USE_HTTP_PROXY is set to TRUE, then the
|
||||
* http_proxy or https_proxy (lower case only) environment variables are used, for plain
|
||||
* TCP and TLS-secured connections respectively.
|
||||
*
|
||||
* The no_proxy environment variable can be used to exclude certain hosts from using an
|
||||
* environment variable chosen proxy. This does not apply to a proxy selected through the API.
|
||||
* The no_proxy environment variable is lower case only, and is a list of comma-separated
|
||||
* hostname:port values. Suffixes are matched (e.g. example.com will match test.example.com).
|
||||
*/
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
3255
3rd/paho.mqtt.c/src/MQTTAsyncUtils.c
Normal file
3255
3rd/paho.mqtt.c/src/MQTTAsyncUtils.c
Normal file
@@ -0,0 +1,3255 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2026 IBM Corp., Ian Craggs and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial implementation and documentation
|
||||
* Sven Gambel - add generic proxy support
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
#include "MQTTPersistence.h"
|
||||
#endif
|
||||
#include "MQTTAsync.h"
|
||||
#include "MQTTAsyncUtils.h"
|
||||
#include "utf-8.h"
|
||||
#include "MQTTProtocol.h"
|
||||
#include "MQTTProtocolOut.h"
|
||||
#include "Thread.h"
|
||||
#include "SocketBuffer.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Heap.h"
|
||||
#include "OsWrapper.h"
|
||||
#include "WebSocket.h"
|
||||
#include "Proxy.h"
|
||||
|
||||
#if defined(OPENSSL) && defined(LIBRESSL_VERSION_NUMBER)
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
|
||||
static int clientSockCompare(void* a, void* b);
|
||||
static int MQTTAsync_checkConn(MQTTAsync_command* command, MQTTAsyncs* client, int was_connected);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
static int MQTTAsync_unpersistCommand(MQTTAsync_queuedCommand* qcmd);
|
||||
static int MQTTAsync_persistCommand(MQTTAsync_queuedCommand* qcmd);
|
||||
static MQTTAsync_queuedCommand* MQTTAsync_restoreCommand(char* buffer, int buflen, int MQTTVersion, MQTTAsync_queuedCommand*);
|
||||
#endif
|
||||
static void MQTTAsync_startConnectRetry(MQTTAsyncs* m);
|
||||
static void MQTTAsync_checkDisconnect(MQTTAsync handle, MQTTAsync_command* command);
|
||||
static void MQTTProtocol_checkPendingWrites(void);
|
||||
static void MQTTAsync_freeCommand1(MQTTAsync_queuedCommand *command);
|
||||
static void MQTTAsync_freeCommand(MQTTAsync_queuedCommand *command);
|
||||
static int MQTTAsync_processCommand(void);
|
||||
static void MQTTAsync_checkTimeouts(void);
|
||||
static int MQTTAsync_completeConnection(MQTTAsyncs* m, Connack* connack);
|
||||
static void MQTTAsync_stop(void);
|
||||
static void MQTTAsync_closeOnly(Clients* client, enum MQTTReasonCodes reasonCode, MQTTProperties* props);
|
||||
static int clientStructCompare(void* a, void* b);
|
||||
static int MQTTAsync_cleanSession(Clients* client);
|
||||
static int MQTTAsync_deliverMessage(MQTTAsyncs* m, char* topicName, size_t topicLen, MQTTAsync_message* mm);
|
||||
static int MQTTAsync_disconnect_internal(MQTTAsync handle, int timeout);
|
||||
static int cmdMessageIDCompare(void* a, void* b);
|
||||
static void MQTTAsync_retry(void);
|
||||
static MQTTPacket* MQTTAsync_cycle(SOCKET* sock, unsigned long timeout, int* rc);
|
||||
static int MQTTAsync_connecting(MQTTAsyncs* m);
|
||||
|
||||
extern MQTTProtocol state; /* defined in MQTTAsync.c */
|
||||
extern ClientStates* bstate; /* defined in MQTTAsync.c */
|
||||
|
||||
extern enum MQTTAsync_threadStates sendThread_state;
|
||||
extern enum MQTTAsync_threadStates receiveThread_state;
|
||||
extern thread_id_type sendThread_id,
|
||||
receiveThread_id;
|
||||
|
||||
extern volatile int global_initialized;
|
||||
extern List* MQTTAsync_handles;
|
||||
extern List* MQTTAsync_commands;
|
||||
extern int MQTTAsync_tostop;
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
extern mutex_type mqttasync_mutex;
|
||||
extern mutex_type socket_mutex;
|
||||
extern mutex_type mqttcommand_mutex;
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
extern mutex_type stack_mutex;
|
||||
extern mutex_type heap_mutex;
|
||||
#endif
|
||||
extern mutex_type log_mutex;
|
||||
#else
|
||||
extern mutex_type mqttasync_mutex;
|
||||
extern mutex_type socket_mutex;
|
||||
extern mutex_type mqttcommand_mutex;
|
||||
#endif
|
||||
|
||||
extern evt_type send_evt;
|
||||
|
||||
#if !defined(min)
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
void MQTTAsync_sleep(long milliseconds)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
#if defined(_WIN32)
|
||||
Sleep(milliseconds);
|
||||
#else
|
||||
usleep(milliseconds*1000);
|
||||
#endif
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/* Add random amount of jitter for exponential backoff on retry
|
||||
Jitter value will be +/- 20% of "base" interval, including max interval
|
||||
https://www.awsarchitectureblog.com/2015/03/backoff.html
|
||||
http://ee.lbl.gov/papers/sync_94.pdf */
|
||||
int MQTTAsync_randomJitter(int currentIntervalBase, int minInterval, int maxInterval)
|
||||
{
|
||||
const int max_sleep = (int)(min(maxInterval, currentIntervalBase) * 1.2); // (e.g. 72 if base > 60)
|
||||
const int min_sleep = (int)(max(minInterval, currentIntervalBase) / 1.2); // (e.g. 48 if base > 60)
|
||||
|
||||
if (min_sleep >= max_sleep) // shouldn't happen, but just in case
|
||||
{
|
||||
return min_sleep;
|
||||
}
|
||||
|
||||
{
|
||||
/* random_between(min_sleep, max_sleep)
|
||||
http://stackoverflow.com/questions/2509679/how-to-generate-a-random-number-from-within-a-range */
|
||||
int r;
|
||||
int range = max_sleep - min_sleep + 1;
|
||||
const int buckets = RAND_MAX / range;
|
||||
const int limit = buckets * range;
|
||||
|
||||
/* Create equal size buckets all in a row, then fire randomly towards
|
||||
* the buckets until you land in one of them. All buckets are equally
|
||||
* likely. If you land off the end of the line of buckets, try again. */
|
||||
do
|
||||
{
|
||||
r = rand();
|
||||
} while (r >= limit);
|
||||
|
||||
{
|
||||
const int randResult = r / buckets;
|
||||
return min_sleep + randResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing clients by socket
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
static int clientSockCompare(void* a, void* b)
|
||||
{
|
||||
MQTTAsyncs* m = (MQTTAsyncs*)a;
|
||||
return m->c->net.socket == *(int*)b;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_lock_mutex(mutex_type amutex)
|
||||
{
|
||||
int rc = Paho_thread_lock_mutex(amutex);
|
||||
if (rc != 0)
|
||||
Log(LOG_ERROR, 0, "Error %s locking mutex", strerror(rc));
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_unlock_mutex(mutex_type amutex)
|
||||
{
|
||||
int rc = Paho_thread_unlock_mutex(amutex);
|
||||
if (rc != 0)
|
||||
Log(LOG_ERROR, 0, "Error %s unlocking mutex", strerror(rc));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check whether there are any more connect options. If not then we are finished
|
||||
with connect attempts.
|
||||
return 1 if more connect options left
|
||||
*/
|
||||
static int MQTTAsync_checkConn(MQTTAsync_command* command, MQTTAsyncs* client, int was_connected)
|
||||
{
|
||||
int rc;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = command->details.conn.currentURI + 1 < client->serverURIcount ||
|
||||
(was_connected == 0 && command->details.conn.MQTTVersion == MQTTVERSION_3_1 && client->c->MQTTVersion == MQTTVERSION_DEFAULT);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_terminate(void)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_stop();
|
||||
|
||||
/* don't destroy global data if a new client was created while waiting for background threads to terminate */
|
||||
if (global_initialized && bstate->clients->count == 0)
|
||||
{
|
||||
ListElement* elem = NULL;
|
||||
ListFree(bstate->clients);
|
||||
ListFree(MQTTAsync_handles);
|
||||
while (ListNextElement(MQTTAsync_commands, &elem))
|
||||
MQTTAsync_freeCommand1((MQTTAsync_queuedCommand*)(elem->content));
|
||||
ListFree(MQTTAsync_commands);
|
||||
MQTTAsync_handles = NULL;
|
||||
WebSocket_terminate();
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
Heap_terminate();
|
||||
#endif
|
||||
Log_terminate();
|
||||
global_initialized = 0;
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
static int MQTTAsync_unpersistCommand(MQTTAsync_queuedCommand* qcmd)
|
||||
{
|
||||
int rc = 0;
|
||||
char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
|
||||
int chars = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (qcmd->client->c->MQTTVersion >= MQTTVERSION_5)
|
||||
chars = snprintf(key, sizeof(key), "%s%u", PERSISTENCE_V5_COMMAND_KEY, qcmd->seqno);
|
||||
else
|
||||
chars = snprintf(key, sizeof(key), "%s%u", PERSISTENCE_COMMAND_KEY, qcmd->seqno);
|
||||
if (chars >= sizeof(key))
|
||||
{
|
||||
rc = MQTTASYNC_PERSISTENCE_ERROR;
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
}
|
||||
else if ((rc = qcmd->client->c->persistence->premove(qcmd->client->c->phandle, key)) != 0)
|
||||
Log(LOG_ERROR, 0, "Error %d removing command from persistence", rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTAsync_persistCommand(MQTTAsync_queuedCommand* qcmd)
|
||||
{
|
||||
int rc = 0;
|
||||
MQTTAsyncs* aclient = qcmd->client;
|
||||
MQTTAsync_command* command = &qcmd->command;
|
||||
int* lens = NULL;
|
||||
void** bufs = NULL;
|
||||
int bufindex = 0, i, nbufs = 0;
|
||||
char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
|
||||
int chars = 0; /* number of chars from snprintf */
|
||||
int props_allocated = 0;
|
||||
int process = 1;
|
||||
int multiplier = 2; /* default value 2 for MQTTVERSION < 5 */
|
||||
|
||||
FUNC_ENTRY;
|
||||
switch (command->type)
|
||||
{
|
||||
case SUBSCRIBE:
|
||||
multiplier = (aclient->c->MQTTVersion >= MQTTVERSION_5) ? 3 : 2;
|
||||
nbufs = ((aclient->c->MQTTVersion >= MQTTVERSION_5) ? 4 : 3) +
|
||||
(command->details.sub.count * multiplier);
|
||||
|
||||
if (((lens = (int*)malloc(nbufs * sizeof(int))) == NULL) ||
|
||||
((bufs = malloc(nbufs * sizeof(char *))) == NULL))
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
bufs[bufindex] = &command->type;
|
||||
lens[bufindex++] = sizeof(command->type);
|
||||
|
||||
bufs[bufindex] = &command->token;
|
||||
lens[bufindex++] = sizeof(command->token);
|
||||
|
||||
bufs[bufindex] = &command->details.sub.count;
|
||||
lens[bufindex++] = sizeof(command->details.sub.count);
|
||||
|
||||
for (i = 0; i < command->details.sub.count; ++i)
|
||||
{
|
||||
bufs[bufindex] = command->details.sub.topics[i];
|
||||
lens[bufindex++] = (int)strlen(command->details.sub.topics[i]) + 1;
|
||||
|
||||
bufs[bufindex] = &command->details.sub.qoss[i];
|
||||
lens[bufindex++] = sizeof(command->details.sub.qoss[i]);
|
||||
if (aclient->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (command->details.sub.count == 1)
|
||||
{
|
||||
bufs[bufindex] = &command->details.sub.opts;
|
||||
lens[bufindex++] = sizeof(command->details.sub.opts);
|
||||
}
|
||||
else
|
||||
{
|
||||
bufs[bufindex] = &command->details.sub.optlist[i];
|
||||
lens[bufindex++] = sizeof(command->details.sub.optlist[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UNSUBSCRIBE:
|
||||
nbufs = ((aclient->c->MQTTVersion >= MQTTVERSION_5) ? 4 : 3) +
|
||||
command->details.unsub.count;
|
||||
|
||||
if (((lens = (int*)malloc(nbufs * sizeof(int))) == NULL) ||
|
||||
((bufs = malloc(nbufs * sizeof(char *))) == NULL))
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bufs[bufindex] = &command->type;
|
||||
lens[bufindex++] = sizeof(command->type);
|
||||
|
||||
bufs[bufindex] = &command->token;
|
||||
lens[bufindex++] = sizeof(command->token);
|
||||
|
||||
bufs[bufindex] = &command->details.unsub.count;
|
||||
lens[bufindex++] = sizeof(command->details.unsub.count);
|
||||
|
||||
for (i = 0; i < command->details.unsub.count; ++i)
|
||||
{
|
||||
bufs[bufindex] = command->details.unsub.topics[i];
|
||||
lens[bufindex++] = (int)strlen(command->details.unsub.topics[i]) + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case PUBLISH:
|
||||
nbufs = (aclient->c->MQTTVersion >= MQTTVERSION_5) ? 8 : 7;
|
||||
|
||||
if (((lens = (int*)malloc(nbufs * sizeof(int))) == NULL) ||
|
||||
((bufs = malloc(nbufs * sizeof(char *))) == NULL))
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bufs[bufindex] = &command->type;
|
||||
lens[bufindex++] = sizeof(command->type);
|
||||
|
||||
bufs[bufindex] = &command->token;
|
||||
lens[bufindex++] = sizeof(command->token);
|
||||
|
||||
bufs[bufindex] = command->details.pub.destinationName;
|
||||
lens[bufindex++] = (int)strlen(command->details.pub.destinationName) + 1;
|
||||
|
||||
bufs[bufindex] = &command->details.pub.payloadlen;
|
||||
lens[bufindex++] = sizeof(command->details.pub.payloadlen);
|
||||
|
||||
bufs[bufindex] = command->details.pub.payload;
|
||||
lens[bufindex++] = command->details.pub.payloadlen;
|
||||
|
||||
bufs[bufindex] = &command->details.pub.qos;
|
||||
lens[bufindex++] = sizeof(command->details.pub.qos);
|
||||
|
||||
bufs[bufindex] = &command->details.pub.retained;
|
||||
lens[bufindex++] = sizeof(command->details.pub.retained);
|
||||
break;
|
||||
|
||||
default:
|
||||
process = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment the command sequence number. Don't exceed the maximum value allowed
|
||||
* by the value PERSISTENCE_MAX_KEY_LENGTH minus the max prefix string length
|
||||
*/
|
||||
if (++aclient->command_seqno == PERSISTENCE_SEQNO_LIMIT)
|
||||
aclient->command_seqno = 0;
|
||||
|
||||
if (aclient->c->MQTTVersion >= MQTTVERSION_5 && process) /* persist properties */
|
||||
{
|
||||
int temp_len = 0;
|
||||
char* ptr = NULL;
|
||||
|
||||
temp_len = MQTTProperties_len(&command->properties);
|
||||
if ((ptr = bufs[bufindex] = malloc(temp_len)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
props_allocated = bufindex;
|
||||
rc = MQTTProperties_write(&ptr, &command->properties);
|
||||
lens[bufindex++] = temp_len;
|
||||
chars = snprintf(key, sizeof(key), "%s%u", PERSISTENCE_V5_COMMAND_KEY, aclient->command_seqno);
|
||||
}
|
||||
else
|
||||
chars = snprintf(key, sizeof(key), "%s%u", PERSISTENCE_COMMAND_KEY, aclient->command_seqno);
|
||||
if (chars >= sizeof(key))
|
||||
{
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (nbufs > 0)
|
||||
{
|
||||
if (aclient->c->beforeWrite)
|
||||
rc = aclient->c->beforeWrite(aclient->c->beforeWrite_context, nbufs, (char**)bufs, lens);
|
||||
|
||||
if ((rc = aclient->c->persistence->pput(aclient->c->phandle, key, nbufs, (char**)bufs, lens)) != 0)
|
||||
Log(LOG_ERROR, 0, "Error persisting command, rc %d", rc);
|
||||
qcmd->seqno = aclient->command_seqno;
|
||||
}
|
||||
exit:
|
||||
if (props_allocated > 0)
|
||||
free(bufs[props_allocated]);
|
||||
if (lens)
|
||||
free(lens);
|
||||
if (bufs)
|
||||
free(bufs);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static MQTTAsync_queuedCommand* MQTTAsync_restoreCommand(char* buffer, int buflen, int MQTTVersion, MQTTAsync_queuedCommand* qcommand)
|
||||
{
|
||||
MQTTAsync_command* command = NULL;
|
||||
char* ptr = buffer;
|
||||
int i;
|
||||
size_t data_size;
|
||||
char* endpos = &buffer[buflen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen == 0)
|
||||
{
|
||||
qcommand = NULL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (qcommand == NULL)
|
||||
{
|
||||
if ((qcommand = malloc(sizeof(MQTTAsync_queuedCommand))) == NULL)
|
||||
goto exit;
|
||||
memset(qcommand, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
qcommand->not_restored = 1; /* don't restore all the command on the first call */
|
||||
}
|
||||
else
|
||||
qcommand->not_restored = 0;
|
||||
|
||||
command = &qcommand->command;
|
||||
|
||||
if (&ptr[sizeof(int) + sizeof(MQTTAsync_token)] > endpos)
|
||||
goto error_exit;
|
||||
memcpy(&command->type, ptr, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
memcpy(&command->token, ptr, sizeof(MQTTAsync_token));
|
||||
ptr += sizeof(MQTTAsync_token);
|
||||
|
||||
switch (command->type)
|
||||
{
|
||||
case SUBSCRIBE:
|
||||
if (qcommand->not_restored == 1)
|
||||
break;
|
||||
if (&ptr[sizeof(int)] > endpos)
|
||||
goto error_exit;
|
||||
memcpy(&command->details.sub.count, ptr, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
|
||||
if (command->details.sub.count > 0)
|
||||
{
|
||||
if ((command->details.sub.topics = (char **)malloc(sizeof(char *) * command->details.sub.count)) == NULL)
|
||||
goto error_exit;
|
||||
if ((command->details.sub.qoss = (int *)malloc(sizeof(int) * command->details.sub.count)) == NULL)
|
||||
goto error_exit;
|
||||
|
||||
if ((MQTTVersion >= MQTTVERSION_5))
|
||||
{
|
||||
if (command->details.sub.count > 1)
|
||||
{
|
||||
command->details.sub.optlist = (MQTTSubscribe_options*)malloc(sizeof(MQTTSubscribe_options) * command->details.sub.count);
|
||||
if (command->details.sub.optlist == NULL)
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < command->details.sub.count; ++i)
|
||||
{
|
||||
data_size = strnlen(ptr, endpos - ptr) + 1;
|
||||
if (data_size == endpos - ptr)
|
||||
goto error_exit; /* no null found */
|
||||
|
||||
if ((command->details.sub.topics[i] = malloc(data_size)) == NULL)
|
||||
goto error_exit;
|
||||
strcpy(command->details.sub.topics[i], ptr);
|
||||
ptr += data_size;
|
||||
|
||||
if (&ptr[sizeof(int)] > endpos)
|
||||
goto error_exit;
|
||||
memcpy(&command->details.sub.qoss[i], ptr, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (&ptr[sizeof(MQTTSubscribe_options)] > endpos)
|
||||
goto error_exit;
|
||||
if (command->details.sub.count == 1)
|
||||
{
|
||||
memcpy(&command->details.sub.opts, ptr, sizeof(MQTTSubscribe_options));
|
||||
ptr += sizeof(MQTTSubscribe_options);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&command->details.sub.optlist[i], ptr, sizeof(MQTTSubscribe_options));
|
||||
ptr += sizeof(MQTTSubscribe_options);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UNSUBSCRIBE:
|
||||
if (qcommand->not_restored == 1)
|
||||
break;
|
||||
|
||||
if (&ptr[sizeof(int)] > endpos)
|
||||
goto error_exit;
|
||||
memcpy(&command->details.unsub.count, ptr, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
|
||||
if (command->details.unsub.count > 0)
|
||||
{
|
||||
command->details.unsub.topics = (char **)malloc(sizeof(char *) * command->details.unsub.count);
|
||||
if (command->details.unsub.topics == NULL)
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < command->details.unsub.count; ++i)
|
||||
{
|
||||
data_size = strnlen(ptr, endpos - ptr) + 1;
|
||||
if (data_size == endpos - ptr)
|
||||
goto error_exit; /* no null found */
|
||||
|
||||
if ((command->details.unsub.topics[i] = malloc(data_size)) == NULL)
|
||||
goto error_exit;
|
||||
strcpy(command->details.unsub.topics[i], ptr);
|
||||
ptr += data_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case PUBLISH:
|
||||
data_size = strnlen(ptr, endpos - ptr) + 1;
|
||||
if (data_size == endpos - ptr)
|
||||
goto error_exit; /* no null found */
|
||||
|
||||
if (qcommand->not_restored == 0)
|
||||
{
|
||||
if ((command->details.pub.destinationName = malloc(data_size)) == NULL)
|
||||
goto error_exit;
|
||||
strcpy(command->details.pub.destinationName, ptr);
|
||||
}
|
||||
ptr += data_size;
|
||||
|
||||
if (&ptr[sizeof(int)] > endpos)
|
||||
goto error_exit;
|
||||
memcpy(&command->details.pub.payloadlen, ptr, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
|
||||
data_size = command->details.pub.payloadlen;
|
||||
if (&ptr[data_size] > endpos)
|
||||
goto error_exit;
|
||||
if (qcommand->not_restored == 0)
|
||||
{
|
||||
if ((command->details.pub.payload = malloc(data_size)) == NULL)
|
||||
goto error_exit;
|
||||
memcpy(command->details.pub.payload, ptr, data_size);
|
||||
}
|
||||
ptr += data_size;
|
||||
|
||||
if (&ptr[sizeof(int)*2] > endpos)
|
||||
goto error_exit;
|
||||
memcpy(&command->details.pub.qos, ptr, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
|
||||
memcpy(&command->details.pub.retained, ptr, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error_exit;
|
||||
|
||||
}
|
||||
if (qcommand != NULL && qcommand->not_restored == 0 && MQTTVersion >= MQTTVERSION_5 &&
|
||||
MQTTProperties_read(&command->properties, &ptr, buffer + buflen) != 1)
|
||||
{
|
||||
Log(LOG_ERROR, -1, "Error restoring properties from persistence");
|
||||
free(qcommand);
|
||||
qcommand = NULL;
|
||||
}
|
||||
goto exit;
|
||||
error_exit:
|
||||
free(qcommand);
|
||||
qcommand = NULL;
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return qcommand;
|
||||
}
|
||||
|
||||
|
||||
static int cmpkeys(const void *p1, const void *p2)
|
||||
{
|
||||
int key1 = atoi(strchr(*(char * const *)p1, '-') + 1);
|
||||
int key2 = atoi(strchr(*(char * const *)p2, '-') + 1);
|
||||
|
||||
return (key1 == key2) ? 0 : ((key1 < key2) ? -1 : 1);
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_restoreCommands(MQTTAsyncs* client)
|
||||
{
|
||||
int rc = 0;
|
||||
char **msgkeys;
|
||||
int nkeys;
|
||||
int i = 0;
|
||||
Clients* c = client->c;
|
||||
int commands_restored = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0 && nkeys > 0)
|
||||
{
|
||||
/* let's have the sequence number array sorted */
|
||||
qsort(msgkeys, (size_t)nkeys, sizeof(char*), cmpkeys);
|
||||
|
||||
while (rc == 0 && i < nkeys)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
int buflen;
|
||||
|
||||
if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) != 0 &&
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_COMMAND_KEY, strlen(PERSISTENCE_V5_COMMAND_KEY)) != 0)
|
||||
{
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
MQTTAsync_queuedCommand* cmd = NULL;
|
||||
if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0 &&
|
||||
(c->afterRead == NULL || (rc = c->afterRead(c->afterRead_context, &buffer, &buflen)) == 0))
|
||||
{
|
||||
int MQTTVersion = (strncmp(msgkeys[i], PERSISTENCE_V5_COMMAND_KEY, strlen(PERSISTENCE_V5_COMMAND_KEY)) == 0)
|
||||
? MQTTVERSION_5 : MQTTVERSION_3_1_1;
|
||||
cmd = MQTTAsync_restoreCommand(buffer, buflen, MQTTVersion, NULL);
|
||||
}
|
||||
|
||||
if (cmd)
|
||||
{
|
||||
/* As the entire command is not restored on the first read to save memory, we temporarily store
|
||||
* the filename of the persisted command to be used when restoreCommand is called the second time.
|
||||
*/
|
||||
cmd->key = malloc(strlen(msgkeys[i])+1);
|
||||
strcpy(cmd->key, msgkeys[i]);
|
||||
|
||||
cmd->client = client;
|
||||
cmd->seqno = atoi(strchr(msgkeys[i], '-')+1); /* key format is tag'-'seqno */
|
||||
/* we can just append the commands to the list as they've already been sorted */
|
||||
ListAppend(MQTTAsync_commands, cmd, sizeof(MQTTAsync_queuedCommand));
|
||||
client->command_seqno = max(client->command_seqno, cmd->seqno);
|
||||
commands_restored++;
|
||||
if (cmd->command.type == PUBLISH)
|
||||
client->noBufferedMessages++;
|
||||
}
|
||||
}
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
if (msgkeys[i])
|
||||
free(msgkeys[i]);
|
||||
i++;
|
||||
}
|
||||
if (msgkeys != NULL)
|
||||
free(msgkeys);
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d commands restored for client %s", commands_restored, c->clientID);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_unpersistCommandsAndMessages(Clients* c)
|
||||
{
|
||||
int rc = 0;
|
||||
char **msgkeys;
|
||||
int nkeys;
|
||||
int i = 0;
|
||||
int messages_deleted = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
|
||||
{
|
||||
while (rc == 0 && i < nkeys)
|
||||
{
|
||||
if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_COMMAND_KEY, strlen(PERSISTENCE_V5_COMMAND_KEY)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) == 0)
|
||||
{
|
||||
if ((rc = c->persistence->premove(c->phandle, msgkeys[i])) == 0)
|
||||
messages_deleted++;
|
||||
else
|
||||
Log(LOG_ERROR, 0, "Error %d removing queued message from persistence", rc);
|
||||
}
|
||||
if (msgkeys[i])
|
||||
free(msgkeys[i]);
|
||||
i++;
|
||||
}
|
||||
if (msgkeys != NULL)
|
||||
free(msgkeys);
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d queued messages deleted for client %s", messages_deleted, c->clientID);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTAsync_unpersistInflightMessages(Clients* c)
|
||||
{
|
||||
int rc = 0;
|
||||
char **msgkeys;
|
||||
int nkeys;
|
||||
int i = 0;
|
||||
int messages_deleted = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
|
||||
{
|
||||
while (rc == 0 && i < nkeys)
|
||||
{
|
||||
if (strncmp(msgkeys[i], PERSISTENCE_PUBLISH_SENT, strlen(PERSISTENCE_PUBLISH_SENT)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_PUBLISH_SENT, strlen(PERSISTENCE_V5_PUBLISH_SENT)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_PUBREL, strlen(PERSISTENCE_PUBREL)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_PUBREL, strlen(PERSISTENCE_V5_PUBREL)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_PUBLISH_RECEIVED, strlen(PERSISTENCE_PUBLISH_RECEIVED)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_PUBLISH_RECEIVED, strlen(PERSISTENCE_V5_PUBLISH_RECEIVED)) == 0)
|
||||
{
|
||||
if ((rc = c->persistence->premove(c->phandle, msgkeys[i])) == 0)
|
||||
messages_deleted++;
|
||||
else
|
||||
Log(LOG_ERROR, 0, "Error %d removing inflight message from persistence", rc);
|
||||
}
|
||||
if (msgkeys[i])
|
||||
free(msgkeys[i]);
|
||||
i++;
|
||||
}
|
||||
if (msgkeys != NULL)
|
||||
free(msgkeys);
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d inflight messages deleted for client %s", messages_deleted, c->clientID);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* List callback function for comparing client handles and command types being CONNECT or DISCONNECT
|
||||
* @param a first MQTTAsync_queuedCommand pointer
|
||||
* @param b second MQTTAsync_queuedCommand pointer
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
static int clientCompareConnectCommand(void* a, void* b)
|
||||
{
|
||||
MQTTAsync_queuedCommand* cmd1 = (MQTTAsync_queuedCommand*)a;
|
||||
MQTTAsync_queuedCommand* cmd2 = (MQTTAsync_queuedCommand*)b;
|
||||
if (cmd1->client == cmd2->client)
|
||||
{
|
||||
if (cmd1->command.type == cmd2->command.type)
|
||||
{
|
||||
if (cmd1->command.type == CONNECT || cmd1->command.type == DISCONNECT)
|
||||
{
|
||||
return 1; //Item found in the list
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0; //Item NOT found in the list
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int MQTTAsync_addCommand(MQTTAsync_queuedCommand* command, int command_size)
|
||||
{
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
int rc1 = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttcommand_mutex);
|
||||
/* Don't set start time if the connect command is already in process #218 */
|
||||
if ((command->command.type != CONNECT) || (command->client->c->connect_state == NOT_IN_PROGRESS))
|
||||
command->command.start_time = MQTTTime_start_clock();
|
||||
|
||||
if (command->command.type == CONNECT ||
|
||||
(command->command.type == DISCONNECT && command->command.details.dis.internal))
|
||||
{
|
||||
MQTTAsync_queuedCommand* head = NULL;
|
||||
ListElement* current = MQTTAsync_commands->first;
|
||||
|
||||
/* Look for any connect or disconnect command belonging to this client. All the connects/disconnects
|
||||
* are at the head of the list, so we don't search any further if we meet anything other than a
|
||||
* connect or disconnect for any client.
|
||||
*/
|
||||
while (current)
|
||||
{
|
||||
MQTTAsync_queuedCommand* cur_cmd = (MQTTAsync_queuedCommand*)(current->content);
|
||||
if (cur_cmd->command.type != CONNECT && cur_cmd->command.type != DISCONNECT)
|
||||
break; /* end search if we meet anything other than connect or disconnect */
|
||||
if (cur_cmd->client == command->client)
|
||||
{
|
||||
head = cur_cmd;
|
||||
break;
|
||||
}
|
||||
current = current->prev;
|
||||
}
|
||||
|
||||
if (head)
|
||||
{
|
||||
MQTTAsync_freeCommand(command); /* ignore duplicate connect or disconnect command */
|
||||
rc = MQTTASYNC_COMMAND_IGNORED;
|
||||
}
|
||||
else
|
||||
{
|
||||
ListElement* result = ListInsert(MQTTAsync_commands, command, command_size, MQTTAsync_commands->first); /* add to the head of the list */
|
||||
if (result == NULL)
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ListAppend(MQTTAsync_commands, command, command_size) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (command->client->c->persistence)
|
||||
{
|
||||
if (command->command.type == PUBLISH &&
|
||||
command->client->createOptions && command->client->createOptions->struct_version >= 2 &&
|
||||
command->client->createOptions->persistQoS0 == 0 && command->command.details.pub.qos == 0)
|
||||
; /* don't persist QoS0 if that create option is set to 0 */
|
||||
else
|
||||
{
|
||||
rc = MQTTAsync_persistCommand(command);
|
||||
if (command->command.type == PUBLISH && rc == 0)
|
||||
{
|
||||
char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
|
||||
int chars = 0;
|
||||
|
||||
command->not_restored = 1;
|
||||
if (command->client->c->MQTTVersion >= MQTTVERSION_5)
|
||||
chars = snprintf(key, sizeof(key), "%s%u", PERSISTENCE_V5_COMMAND_KEY, command->seqno);
|
||||
else
|
||||
chars = snprintf(key, sizeof(key), "%s%u", PERSISTENCE_COMMAND_KEY, command->seqno);
|
||||
if (chars >= sizeof(key))
|
||||
{
|
||||
rc = MQTTASYNC_PERSISTENCE_ERROR;
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
goto exit;
|
||||
}
|
||||
command->key = malloc(strlen(key)+1);
|
||||
strcpy(command->key, key);
|
||||
|
||||
free(command->command.details.pub.payload);
|
||||
command->command.details.pub.payload = NULL;
|
||||
free(command->command.details.pub.destinationName);
|
||||
command->command.details.pub.destinationName = NULL;
|
||||
MQTTProperties_free(&command->command.properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (command->command.type == PUBLISH)
|
||||
{
|
||||
/* delete oldest message if buffer is full. We wouldn't be here if delete newest was in operation */
|
||||
if (command->client->createOptions && (command->client->noBufferedMessages >= command->client->createOptions->maxBufferedMessages))
|
||||
{
|
||||
MQTTAsync_queuedCommand* first_publish = NULL;
|
||||
ListElement* current = NULL;
|
||||
|
||||
/* Find first publish command for this client and detach it */
|
||||
while (ListNextElement(MQTTAsync_commands, ¤t))
|
||||
{
|
||||
MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(current->content);
|
||||
|
||||
if (cmd->client == command->client && cmd->command.type == PUBLISH)
|
||||
{
|
||||
first_publish = cmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (first_publish)
|
||||
{
|
||||
ListDetach(MQTTAsync_commands, first_publish);
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (command->client->c->persistence)
|
||||
MQTTAsync_unpersistCommand(first_publish);
|
||||
#endif
|
||||
if (first_publish->command.onFailure)
|
||||
{
|
||||
MQTTAsync_failureData data;
|
||||
|
||||
data.token = first_publish->command.token;
|
||||
data.code = MQTTASYNC_MAX_BUFFERED_MESSAGES;
|
||||
data.message = NULL;
|
||||
Log(TRACE_MIN, -1, "Calling connect failure for client %s, rc %d", command->client->c->clientID, data.code);
|
||||
(*(first_publish->command.onFailure))(first_publish->command.context, &data);
|
||||
} else if (first_publish->command.onFailure5)
|
||||
{
|
||||
MQTTAsync_failureData5 data;
|
||||
|
||||
data.token = first_publish->command.token;
|
||||
data.code = MQTTASYNC_MAX_BUFFERED_MESSAGES;
|
||||
data.message = NULL;
|
||||
data.packet_type = PUBLISH;
|
||||
Log(TRACE_MIN, -1, "Calling connect failure for client %s, rc %d", command->client->c->clientID, data.code);
|
||||
(*(first_publish->command.onFailure5))(first_publish->command.context, &data);
|
||||
}
|
||||
MQTTAsync_freeCommand(first_publish);
|
||||
}
|
||||
}
|
||||
else
|
||||
command->client->noBufferedMessages++;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
MQTTAsync_unlock_mutex(mqttcommand_mutex);
|
||||
if ((rc1 = Thread_signal_evt(send_evt)) != 0)
|
||||
Log(LOG_ERROR, 0, "Error %d from signal event", rc1);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_startConnectRetry(MQTTAsyncs* m)
|
||||
{
|
||||
if (m->automaticReconnect && m->shouldBeConnected)
|
||||
{
|
||||
m->lastConnectionFailedTime = MQTTTime_start_clock();
|
||||
if (m->retrying)
|
||||
{
|
||||
m->currentIntervalBase = min(m->currentIntervalBase * 2, m->maxRetryInterval);
|
||||
}
|
||||
else
|
||||
{
|
||||
m->currentIntervalBase = m->minRetryInterval;
|
||||
m->retrying = 1;
|
||||
}
|
||||
m->currentInterval = MQTTAsync_randomJitter(m->currentIntervalBase, m->minRetryInterval, m->maxRetryInterval);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_checkDisconnect(MQTTAsync handle, MQTTAsync_command* command)
|
||||
{
|
||||
MQTTAsyncs* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* wait for all inflight message flows to finish, up to timeout */;
|
||||
if (m->c->outboundMsgs->count == 0 || MQTTTime_elapsed(command->start_time) >= (ELAPSED_TIME_TYPE)command->details.dis.timeout)
|
||||
{
|
||||
int was_connected = m->c->connected;
|
||||
MQTTAsync_closeSession(m->c, command->details.dis.reasonCode, &command->properties);
|
||||
if (command->details.dis.internal)
|
||||
{
|
||||
if (m->cl && was_connected)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling connectionLost for client %s", m->c->clientID);
|
||||
(*(m->cl))(m->clContext, NULL);
|
||||
}
|
||||
MQTTAsync_startConnectRetry(m);
|
||||
}
|
||||
else if (command->onSuccess)
|
||||
{
|
||||
MQTTAsync_successData data;
|
||||
|
||||
memset(&data, '\0', sizeof(data));
|
||||
Log(TRACE_MIN, -1, "Calling disconnect complete for client %s", m->c->clientID);
|
||||
(*(command->onSuccess))(command->context, &data);
|
||||
}
|
||||
else if (command->onSuccess5)
|
||||
{
|
||||
MQTTAsync_successData5 data = MQTTAsync_successData5_initializer;
|
||||
|
||||
data.reasonCode = MQTTASYNC_SUCCESS;
|
||||
Log(TRACE_MIN, -1, "Calling disconnect complete for client %s", m->c->clientID);
|
||||
(*(command->onSuccess5))(command->context, &data);
|
||||
}
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call Socket_noPendingWrites(int socket) with protection by socket_mutex, see https://github.com/eclipse/paho.mqtt.c/issues/385
|
||||
*/
|
||||
static int MQTTAsync_Socket_noPendingWrites(SOCKET socket)
|
||||
{
|
||||
int rc;
|
||||
MQTTAsync_lock_mutex(socket_mutex);
|
||||
rc = Socket_noPendingWrites(socket);
|
||||
MQTTAsync_unlock_mutex(socket_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if any pending writes have been completed, and cleanup if so.
|
||||
* Cleaning up means removing any publication data that was stored because the write did
|
||||
* not originally complete.
|
||||
*/
|
||||
static void MQTTProtocol_checkPendingWrites(void)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (state.pending_writes.count > 0)
|
||||
{
|
||||
ListElement* le = state.pending_writes.first;
|
||||
while (le)
|
||||
{
|
||||
if (MQTTAsync_Socket_noPendingWrites(((pending_write*)(le->content))->socket))
|
||||
{
|
||||
MQTTProtocol_removePublication(((pending_write*)(le->content))->p);
|
||||
state.pending_writes.current = le;
|
||||
ListRemove(&(state.pending_writes), le->content); /* does NextElement itself */
|
||||
le = state.pending_writes.current;
|
||||
}
|
||||
else
|
||||
ListNextElement(&(state.pending_writes), &le);
|
||||
}
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTAsync_freeCommand1(MQTTAsync_queuedCommand *command)
|
||||
{
|
||||
if (command->command.type == SUBSCRIBE)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < command->command.details.sub.count; i++)
|
||||
free(command->command.details.sub.topics[i]);
|
||||
|
||||
free(command->command.details.sub.topics);
|
||||
command->command.details.sub.topics = NULL;
|
||||
free(command->command.details.sub.qoss);
|
||||
command->command.details.sub.qoss = NULL;
|
||||
}
|
||||
else if (command->command.type == UNSUBSCRIBE)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < command->command.details.unsub.count; i++)
|
||||
free(command->command.details.unsub.topics[i]);
|
||||
|
||||
free(command->command.details.unsub.topics);
|
||||
command->command.details.unsub.topics = NULL;
|
||||
}
|
||||
else if (command->command.type == PUBLISH)
|
||||
{
|
||||
/* qos 1 and 2 topics are freed in the protocol code when the flows are completed */
|
||||
if (command->command.details.pub.destinationName)
|
||||
free(command->command.details.pub.destinationName);
|
||||
command->command.details.pub.destinationName = NULL;
|
||||
if (command->command.details.pub.payload)
|
||||
free(command->command.details.pub.payload);
|
||||
command->command.details.pub.payload = NULL;
|
||||
}
|
||||
MQTTProperties_free(&command->command.properties);
|
||||
if (command->not_restored && command->key)
|
||||
free(command->key);
|
||||
}
|
||||
|
||||
|
||||
static void MQTTAsync_freeCommand(MQTTAsync_queuedCommand *command)
|
||||
{
|
||||
MQTTAsync_freeCommand1(command);
|
||||
free(command);
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_writeContinue(SOCKET socket)
|
||||
{
|
||||
ListElement* found = NULL;
|
||||
|
||||
if ((found = ListFindItem(MQTTAsync_handles, &socket, clientSockCompare)) != NULL)
|
||||
{
|
||||
MQTTAsyncs* m = (MQTTAsyncs*)(found->content);
|
||||
|
||||
m->c->net.lastSent = MQTTTime_now();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_writeComplete(SOCKET socket, int rc)
|
||||
{
|
||||
ListElement* found = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
||||
/* a partial write is now complete for a socket - this will be on a publish*/
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
MQTTProtocol_checkPendingWrites();
|
||||
|
||||
/* find the client using this socket */
|
||||
if ((found = ListFindItem(MQTTAsync_handles, &socket, clientSockCompare)) != NULL)
|
||||
{
|
||||
MQTTAsyncs* m = (MQTTAsyncs*)(found->content);
|
||||
|
||||
m->c->net.lastSent = MQTTTime_now();
|
||||
|
||||
/* see if there is a pending write flagged */
|
||||
if (m->pending_write)
|
||||
{
|
||||
ListElement* cur_response = NULL;
|
||||
MQTTAsync_command* command = m->pending_write;
|
||||
MQTTAsync_queuedCommand* com = NULL;
|
||||
|
||||
cur_response = NULL;
|
||||
while (ListNextElement(m->responses, &cur_response))
|
||||
{
|
||||
com = (MQTTAsync_queuedCommand*)(cur_response->content);
|
||||
if (&com->command == m->pending_write)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur_response) /* we found a response */
|
||||
{
|
||||
if (command->type == PUBLISH)
|
||||
{
|
||||
if (rc == 1 && command->details.pub.qos == 0)
|
||||
{
|
||||
if (command->onSuccess)
|
||||
{
|
||||
MQTTAsync_successData data;
|
||||
|
||||
data.token = command->token;
|
||||
data.alt.pub.destinationName = command->details.pub.destinationName;
|
||||
data.alt.pub.message.payload = command->details.pub.payload;
|
||||
data.alt.pub.message.payloadlen = command->details.pub.payloadlen;
|
||||
data.alt.pub.message.qos = command->details.pub.qos;
|
||||
data.alt.pub.message.retained = command->details.pub.retained;
|
||||
Log(TRACE_MIN, -1, "Calling publish success for client %s", m->c->clientID);
|
||||
(*(command->onSuccess))(command->context, &data);
|
||||
}
|
||||
else if (command->onSuccess5)
|
||||
{
|
||||
MQTTAsync_successData5 data = MQTTAsync_successData5_initializer;
|
||||
|
||||
data.token = command->token;
|
||||
data.alt.pub.destinationName = command->details.pub.destinationName;
|
||||
data.alt.pub.message.payload = command->details.pub.payload;
|
||||
data.alt.pub.message.payloadlen = command->details.pub.payloadlen;
|
||||
data.alt.pub.message.qos = command->details.pub.qos;
|
||||
data.alt.pub.message.retained = command->details.pub.retained;
|
||||
data.properties = command->properties;
|
||||
Log(TRACE_MIN, -1, "Calling publish success for client %s", m->c->clientID);
|
||||
(*(command->onSuccess5))(command->context, &data);
|
||||
}
|
||||
}
|
||||
else if (rc == -1)
|
||||
{
|
||||
if (command->onFailure)
|
||||
{
|
||||
MQTTAsync_failureData data;
|
||||
|
||||
data.token = command->token;
|
||||
data.code = rc;
|
||||
data.message = NULL;
|
||||
Log(TRACE_MIN, -1, "Calling publish failure for client %s", m->c->clientID);
|
||||
(*(command->onFailure))(command->context, &data);
|
||||
}
|
||||
else if (command->onFailure5)
|
||||
{
|
||||
MQTTAsync_failureData5 data;
|
||||
|
||||
data.token = command->token;
|
||||
data.code = rc;
|
||||
data.message = NULL;
|
||||
data.packet_type = PUBLISH;
|
||||
Log(TRACE_MIN, -1, "Calling publish failure for client %s", m->c->clientID);
|
||||
(*(command->onFailure5))(command->context, &data);
|
||||
}
|
||||
}
|
||||
else
|
||||
com = NULL; /* Don't delete response we haven't acknowledged */
|
||||
/* QoS 0 payloads are freed elsewhere after a write complete,
|
||||
* so we should indicate that.
|
||||
*/
|
||||
if (command->details.pub.qos == 0)
|
||||
command->details.pub.payload = NULL;
|
||||
}
|
||||
if (com)
|
||||
{
|
||||
Log(TRACE_PROTOCOL, -1, "writeComplete: Removing response for msgid %d", com->command.token);
|
||||
ListDetach(m->responses, com);
|
||||
MQTTAsync_freeCommand(com);
|
||||
}
|
||||
} /* if cur_response */
|
||||
m->pending_write = NULL;
|
||||
} /* if pending_write */
|
||||
}
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTAsync_processCommand(void)
|
||||
{
|
||||
int rc = 0;
|
||||
MQTTAsync_queuedCommand* command = NULL;
|
||||
ListElement* cur_command = NULL;
|
||||
List* ignored_clients = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
MQTTAsync_lock_mutex(mqttcommand_mutex);
|
||||
|
||||
/* only the first command in the list must be processed for any particular client, so if we skip
|
||||
a command for a client, we must skip all following commands for that client. Use a list of
|
||||
ignored clients to keep track
|
||||
*/
|
||||
ignored_clients = ListInitialize();
|
||||
|
||||
/* don't try a command until there isn't a pending write for that client, and we are not connecting */
|
||||
while (ListNextElement(MQTTAsync_commands, &cur_command))
|
||||
{
|
||||
MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(cur_command->content);
|
||||
|
||||
if (ListFind(ignored_clients, cmd->client))
|
||||
continue;
|
||||
|
||||
if (cmd->command.type == CONNECT || cmd->command.type == DISCONNECT || (cmd->client->c->connected &&
|
||||
cmd->client->c->connect_state == NOT_IN_PROGRESS && MQTTAsync_Socket_noPendingWrites(cmd->client->c->net.socket)))
|
||||
{
|
||||
if ((cmd->command.type == PUBLISH || cmd->command.type == SUBSCRIBE || cmd->command.type == UNSUBSCRIBE) &&
|
||||
cmd->client->c->outboundMsgs->count >= MAX_MSG_ID - 1)
|
||||
{
|
||||
; /* no more message ids available */
|
||||
}
|
||||
else if (((cmd->command.type == PUBLISH && cmd->command.details.pub.qos > 0) ||
|
||||
cmd->command.type == SUBSCRIBE || cmd->command.type == UNSUBSCRIBE) &&
|
||||
(cmd->client->c->outboundMsgs->count >= cmd->client->c->maxInflightMessages))
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Blocking on server receive maximum for client %s",
|
||||
cmd->client->c->clientID); /* flow control */
|
||||
}
|
||||
else
|
||||
{
|
||||
command = cmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ListAppend(ignored_clients, cmd->client, sizeof(cmd->client));
|
||||
}
|
||||
ListFreeNoContent(ignored_clients);
|
||||
if (command)
|
||||
{
|
||||
if (command->command.type == PUBLISH)
|
||||
command->client->noBufferedMessages--;
|
||||
ListDetach(MQTTAsync_commands, command);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
/*printf("outboundmsgs count %d max inflight %d qos %d %d %d\n", command->client->c->outboundMsgs->count, command->client->c->maxInflightMessages,
|
||||
command->command.details.pub.qos, command->client->c->MQTTVersion, command->command.type);*/
|
||||
if (command->client->c->persistence)
|
||||
{
|
||||
if (command->not_restored)
|
||||
{
|
||||
char* buffer = NULL;
|
||||
int buflen = 0;
|
||||
|
||||
if ((rc = command->client->c->persistence->pget(command->client->c->phandle, command->key, &buffer, &buflen)) == 0
|
||||
&& (command->client->c->afterRead == NULL ||
|
||||
(rc = command->client->c->afterRead(command->client->c->afterRead_context, &buffer, &buflen)) == 0))
|
||||
{
|
||||
int MQTTVersion = (strncmp(command->key, PERSISTENCE_V5_COMMAND_KEY, strlen(PERSISTENCE_V5_COMMAND_KEY)) == 0)
|
||||
? MQTTVERSION_5 : MQTTVERSION_3_1_1;
|
||||
free(command->key);
|
||||
command->key = NULL;
|
||||
command = MQTTAsync_restoreCommand(buffer, buflen, MQTTVersion, command);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(LOG_ERROR, -1, "Error restoring command: rc %d from pget\n", rc);
|
||||
command = NULL;
|
||||
}
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
}
|
||||
if (command)
|
||||
MQTTAsync_unpersistCommand(command);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
MQTTAsync_unlock_mutex(mqttcommand_mutex);
|
||||
|
||||
if (!command)
|
||||
goto exit; /* nothing to do */
|
||||
|
||||
if (command->command.type == CONNECT)
|
||||
{
|
||||
if (command->client->c->connect_state != NOT_IN_PROGRESS || command->client->c->connected)
|
||||
rc = 0;
|
||||
else
|
||||
{
|
||||
char* serverURI = command->client->serverURI;
|
||||
|
||||
if (command->client->serverURIcount > 0)
|
||||
{
|
||||
if (command->command.details.conn.currentURI < command->client->serverURIcount)
|
||||
{
|
||||
serverURI = command->client->serverURIs[command->command.details.conn.currentURI];
|
||||
|
||||
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
|
||||
serverURI += strlen(URI_TCP);
|
||||
else if (strncmp(URI_MQTT, serverURI, strlen(URI_MQTT)) == 0)
|
||||
serverURI += strlen(URI_MQTT);
|
||||
#if defined(UNIXSOCK)
|
||||
else if (strncmp(URI_UNIX, serverURI, strlen(URI_UNIX)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_UNIX);
|
||||
command->client->unixsock = 1;
|
||||
}
|
||||
#endif
|
||||
else if (strncmp(URI_WS, serverURI, strlen(URI_WS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WS);
|
||||
command->client->websocket = 1;
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_SSL);
|
||||
command->client->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_TLS, serverURI, strlen(URI_TLS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_TLS);
|
||||
command->client->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_MQTTS, serverURI, strlen(URI_MQTTS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_MQTTS);
|
||||
command->client->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_WSS, serverURI, strlen(URI_WSS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WSS);
|
||||
command->client->ssl = 1;
|
||||
command->client->websocket = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (command->client->c->MQTTVersion == MQTTVERSION_DEFAULT)
|
||||
{
|
||||
if (command->command.details.conn.MQTTVersion == MQTTVERSION_DEFAULT)
|
||||
command->command.details.conn.MQTTVersion = MQTTVERSION_3_1_1;
|
||||
else if (command->command.details.conn.MQTTVersion == MQTTVERSION_3_1_1)
|
||||
command->command.details.conn.MQTTVersion = MQTTVERSION_3_1;
|
||||
}
|
||||
else
|
||||
command->command.details.conn.MQTTVersion = command->client->c->MQTTVersion;
|
||||
|
||||
Log(TRACE_PROTOCOL, -1, "Connecting to serverURI %s with MQTT version %d", serverURI, command->command.details.conn.MQTTVersion);
|
||||
#if defined(OPENSSL)
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
rc = MQTTProtocol_connect(serverURI, command->client->c, command->client->unixsock, command->client->ssl, command->client->websocket,
|
||||
command->command.details.conn.MQTTVersion, command->client->connectProps, command->client->willProps, 100);
|
||||
#else
|
||||
rc = MQTTProtocol_connect(serverURI, command->client->c, command->client->unixsock, command->client->ssl, command->client->websocket,
|
||||
command->command.details.conn.MQTTVersion, command->client->connectProps, command->client->willProps);
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
rc = MQTTProtocol_connect(serverURI, command->client->c, command->client->unixsock, command->client->websocket,
|
||||
command->command.details.conn.MQTTVersion, command->client->connectProps, command->client->willProps, 100);
|
||||
#else
|
||||
rc = MQTTProtocol_connect(serverURI, command->client->c, command->client->unixsock, command->client->websocket,
|
||||
command->command.details.conn.MQTTVersion, command->client->connectProps, command->client->willProps);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (command->client->c->connect_state == NOT_IN_PROGRESS)
|
||||
rc = SOCKET_ERROR;
|
||||
|
||||
/* if the TCP connect is pending, then we must call select to determine when the connect has completed,
|
||||
which is indicated by the socket being ready *either* for reading *or* writing. The next couple of lines
|
||||
make sure we check for writeability as well as readability, otherwise we wait around longer than we need to
|
||||
in Socket_getReadySocket() */
|
||||
if (rc == EINPROGRESS)
|
||||
Socket_addPendingWrite(command->client->c->net.socket);
|
||||
}
|
||||
}
|
||||
else if (command->command.type == SUBSCRIBE)
|
||||
{
|
||||
List* topics = ListInitialize();
|
||||
List* qoss = ListInitialize();
|
||||
MQTTProperties* props = NULL;
|
||||
MQTTSubscribe_options* subopts = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < command->command.details.sub.count; i++)
|
||||
{
|
||||
ListAppend(topics, command->command.details.sub.topics[i], strlen(command->command.details.sub.topics[i]));
|
||||
ListAppend(qoss, &command->command.details.sub.qoss[i], sizeof(int));
|
||||
}
|
||||
if (command->client->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
props = &command->command.properties;
|
||||
if (command->command.details.sub.count > 1)
|
||||
subopts = command->command.details.sub.optlist;
|
||||
else
|
||||
subopts = &command->command.details.sub.opts;
|
||||
}
|
||||
rc = MQTTProtocol_subscribe(command->client->c, topics, qoss, command->command.token, subopts, props);
|
||||
ListFreeNoContent(topics);
|
||||
ListFreeNoContent(qoss);
|
||||
if (command->client->c->MQTTVersion >= MQTTVERSION_5 && command->command.details.sub.count > 1)
|
||||
free(command->command.details.sub.optlist);
|
||||
}
|
||||
else if (command->command.type == UNSUBSCRIBE)
|
||||
{
|
||||
List* topics = ListInitialize();
|
||||
MQTTProperties* props = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < command->command.details.unsub.count; i++)
|
||||
ListAppend(topics, command->command.details.unsub.topics[i], strlen(command->command.details.unsub.topics[i]));
|
||||
|
||||
if (command->client->c->MQTTVersion >= MQTTVERSION_5)
|
||||
props = &command->command.properties;
|
||||
|
||||
rc = MQTTProtocol_unsubscribe(command->client->c, topics, command->command.token, props);
|
||||
ListFreeNoContent(topics);
|
||||
}
|
||||
else if (command->command.type == PUBLISH)
|
||||
{
|
||||
Messages* msg = NULL;
|
||||
Publish* p = NULL;
|
||||
MQTTProperties initialized = MQTTProperties_initializer;
|
||||
|
||||
if ((p = malloc(sizeof(Publish))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Initialize the mask */
|
||||
memset(p->mask, 0, sizeof(p->mask));
|
||||
|
||||
p->payload = command->command.details.pub.payload;
|
||||
p->payloadlen = command->command.details.pub.payloadlen;
|
||||
p->topic = command->command.details.pub.destinationName;
|
||||
p->msgId = command->command.token;
|
||||
p->MQTTVersion = command->client->c->MQTTVersion;
|
||||
p->properties = initialized;
|
||||
if (p->MQTTVersion >= MQTTVERSION_5)
|
||||
p->properties = command->command.properties;
|
||||
|
||||
rc = MQTTProtocol_startPublish(command->client->c, p, command->command.details.pub.qos, command->command.details.pub.retained, &msg);
|
||||
|
||||
if (command->command.details.pub.qos == 0)
|
||||
{
|
||||
if (rc == TCPSOCKET_COMPLETE)
|
||||
{
|
||||
if (command->command.onSuccess)
|
||||
{
|
||||
MQTTAsync_successData data;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.alt.pub.destinationName = command->command.details.pub.destinationName;
|
||||
data.alt.pub.message.payload = command->command.details.pub.payload;
|
||||
data.alt.pub.message.payloadlen = command->command.details.pub.payloadlen;
|
||||
data.alt.pub.message.qos = command->command.details.pub.qos;
|
||||
data.alt.pub.message.retained = command->command.details.pub.retained;
|
||||
Log(TRACE_MIN, -1, "Calling publish success for client %s", command->client->c->clientID);
|
||||
(*(command->command.onSuccess))(command->command.context, &data);
|
||||
}
|
||||
else if (command->command.onSuccess5)
|
||||
{
|
||||
MQTTAsync_successData5 data = MQTTAsync_successData5_initializer;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.alt.pub.destinationName = command->command.details.pub.destinationName;
|
||||
data.alt.pub.message.payload = command->command.details.pub.payload;
|
||||
data.alt.pub.message.payloadlen = command->command.details.pub.payloadlen;
|
||||
data.alt.pub.message.qos = command->command.details.pub.qos;
|
||||
data.alt.pub.message.retained = command->command.details.pub.retained;
|
||||
data.properties = command->command.properties;
|
||||
Log(TRACE_MIN, -1, "Calling publish success for client %s", command->client->c->clientID);
|
||||
(*(command->command.onSuccess5))(command->command.context, &data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rc != SOCKET_ERROR)
|
||||
{
|
||||
command->command.details.pub.payload = NULL; /* this will be freed by the protocol code */
|
||||
command->command.details.pub.destinationName = NULL; /* this will be freed by the protocol code */
|
||||
}
|
||||
command->client->pending_write = &command->command;
|
||||
}
|
||||
}
|
||||
free(p); /* should this be done if the write isn't complete? */
|
||||
}
|
||||
else if (command->command.type == DISCONNECT)
|
||||
{
|
||||
if (command->client->c->connect_state != NOT_IN_PROGRESS || command->client->c->connected != 0)
|
||||
{
|
||||
if (command->client->c->connect_state != NOT_IN_PROGRESS)
|
||||
{
|
||||
if (command->client->connect.onFailure)
|
||||
{
|
||||
MQTTAsync_failureData data;
|
||||
|
||||
data.token = 0;
|
||||
data.code = MQTTASYNC_OPERATION_INCOMPLETE;
|
||||
data.message = NULL;
|
||||
Log(TRACE_MIN, -1, "Calling connect failure for client %s", command->client->c->clientID);
|
||||
(*(command->client->connect.onFailure))(command->client->connect.context, &data);
|
||||
/* Null out callback pointers so they aren't accidentally called again */
|
||||
command->client->connect.onFailure = NULL;
|
||||
command->client->connect.onSuccess = NULL;
|
||||
}
|
||||
else if (command->client->connect.onFailure5)
|
||||
{
|
||||
MQTTAsync_failureData5 data;
|
||||
|
||||
data.token = 0;
|
||||
data.code = MQTTASYNC_OPERATION_INCOMPLETE;
|
||||
data.message = NULL;
|
||||
Log(TRACE_MIN, -1, "Calling connect failure for client %s", command->client->c->clientID);
|
||||
(*(command->client->connect.onFailure5))(command->client->connect.context, &data);
|
||||
/* Null out callback pointers so they aren't accidentally called again */
|
||||
command->client->connect.onFailure5 = NULL;
|
||||
command->client->connect.onSuccess5 = NULL;
|
||||
}
|
||||
}
|
||||
command->client->c->connect_state = DISCONNECTING;
|
||||
MQTTAsync_checkDisconnect(command->client, &command->command);
|
||||
}
|
||||
}
|
||||
|
||||
if (command->command.type == CONNECT && rc != SOCKET_ERROR && rc != MQTTASYNC_PERSISTENCE_ERROR)
|
||||
{
|
||||
command->client->connect = command->command;
|
||||
MQTTAsync_freeCommand(command);
|
||||
}
|
||||
else if (command->command.type == DISCONNECT)
|
||||
{
|
||||
command->client->disconnect = command->command;
|
||||
MQTTAsync_freeCommand(command);
|
||||
}
|
||||
else if (command->command.type == PUBLISH && command->command.details.pub.qos == 0 &&
|
||||
rc != SOCKET_ERROR && rc != MQTTASYNC_PERSISTENCE_ERROR)
|
||||
{
|
||||
if (rc == TCPSOCKET_INTERRUPTED)
|
||||
ListAppend(command->client->responses, command, sizeof(command));
|
||||
else
|
||||
MQTTAsync_freeCommand(command);
|
||||
}
|
||||
else if (rc == SOCKET_ERROR || rc == MQTTASYNC_PERSISTENCE_ERROR)
|
||||
{
|
||||
if (command->command.type == CONNECT)
|
||||
{
|
||||
MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
|
||||
MQTTAsync_disconnect(command->client, &opts); /* not "internal" because we don't want to call connection lost */
|
||||
command->client->shouldBeConnected = 1; /* as above call is not "internal" we need to reset this */
|
||||
}
|
||||
else
|
||||
MQTTAsync_disconnect_internal(command->client, 0);
|
||||
|
||||
if (command->command.type == CONNECT
|
||||
&& MQTTAsync_checkConn(&command->command, command->client, 0))
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Connect failed, more to try");
|
||||
|
||||
if (command->client->c->MQTTVersion == MQTTVERSION_DEFAULT)
|
||||
{
|
||||
if (command->command.details.conn.MQTTVersion == MQTTVERSION_3_1)
|
||||
{
|
||||
command->command.details.conn.currentURI++;
|
||||
command->command.details.conn.MQTTVersion = MQTTVERSION_DEFAULT;
|
||||
}
|
||||
} else
|
||||
command->command.details.conn.currentURI++; /* Here currentURI becomes larger than command->client->serverURIcount. This needs to be handled to avoid segmentation faults! */
|
||||
|
||||
/* put the connect command back to the head of the command queue, using the next serverURI */
|
||||
rc = MQTTAsync_addCommand(command,
|
||||
sizeof(command->command.details.conn));
|
||||
} else
|
||||
{
|
||||
if (command->command.onFailure)
|
||||
{
|
||||
MQTTAsync_failureData data;
|
||||
|
||||
data.token = 0;
|
||||
data.code = rc;
|
||||
data.message = NULL;
|
||||
Log(TRACE_MIN, -1, "Calling command failure for client %s", command->client->c->clientID);
|
||||
(*(command->command.onFailure))(command->command.context, &data);
|
||||
}
|
||||
else if (command->command.onFailure5)
|
||||
{
|
||||
MQTTAsync_failureData5 data = MQTTAsync_failureData5_initializer;
|
||||
|
||||
data.code = rc;
|
||||
Log(TRACE_MIN, -1, "Calling command failure for client %s", command->client->c->clientID);
|
||||
(*(command->command.onFailure5))(command->command.context, &data);
|
||||
}
|
||||
if (command->command.type == CONNECT)
|
||||
{
|
||||
command->client->connect = command->command;
|
||||
MQTTAsync_startConnectRetry(command->client);
|
||||
}
|
||||
MQTTAsync_freeCommand(command); /* free up the command if necessary */
|
||||
}
|
||||
}
|
||||
else /* put the command into a waiting for response queue for each client, indexed by msgid */
|
||||
ListAppend(command->client->responses, command, sizeof(command));
|
||||
|
||||
exit:
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
rc = (command != NULL);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void nextOrClose(MQTTAsyncs* m, int rc, char* message)
|
||||
{
|
||||
int was_connected = m->c->connected;
|
||||
int more_to_try = 0;
|
||||
int connectionLost_called = 0;
|
||||
FUNC_ENTRY;
|
||||
|
||||
more_to_try = MQTTAsync_checkConn(&m->connect, m, was_connected);
|
||||
if (more_to_try)
|
||||
{
|
||||
MQTTAsync_queuedCommand* conn;
|
||||
|
||||
MQTTAsync_closeOnly(m->c, MQTTREASONCODE_SUCCESS, NULL);
|
||||
if (m->cl && was_connected)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling connectionLost for client %s", m->c->clientID);
|
||||
(*(m->cl))(m->clContext, NULL);
|
||||
connectionLost_called = 1;
|
||||
}
|
||||
/* put the connect command back to the head of the command queue, using the next serverURI */
|
||||
if ((conn = malloc(sizeof(MQTTAsync_queuedCommand))) == NULL)
|
||||
goto exit;
|
||||
memset(conn, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
conn->client = m;
|
||||
conn->command = m->connect;
|
||||
Log(TRACE_MIN, -1, "Connect failed, more to try");
|
||||
|
||||
if (conn->client->c->MQTTVersion == MQTTVERSION_DEFAULT)
|
||||
{
|
||||
/* if last attempt successfully connected and we are using the DEFAULT option, don't fallback to MQTT 3.1 */
|
||||
if (was_connected == 0 || conn->command.details.conn.MQTTVersion == MQTTVERSION_3_1)
|
||||
{
|
||||
conn->command.details.conn.currentURI++;
|
||||
conn->command.details.conn.MQTTVersion = MQTTVERSION_DEFAULT;
|
||||
}
|
||||
}
|
||||
else
|
||||
conn->command.details.conn.currentURI++;
|
||||
|
||||
if (MQTTAsync_addCommand(conn, sizeof(m->connect)) != MQTTASYNC_SUCCESS)
|
||||
more_to_try = 0; /* go into retry mode if CONNECT command add fails */
|
||||
}
|
||||
|
||||
if (!more_to_try)
|
||||
{
|
||||
MQTTAsync_closeSession(m->c, MQTTREASONCODE_SUCCESS, NULL);
|
||||
if (m->connect.onFailure)
|
||||
{
|
||||
MQTTAsync_failureData data;
|
||||
|
||||
data.token = 0;
|
||||
data.code = rc;
|
||||
data.message = message;
|
||||
Log(TRACE_MIN, -1, "Calling connect failure for client %s", m->c->clientID);
|
||||
(*(m->connect.onFailure))(m->connect.context, &data);
|
||||
/* Null out callback pointers so they aren't accidentally called again */
|
||||
m->connect.onFailure = NULL;
|
||||
m->connect.onSuccess = NULL;
|
||||
}
|
||||
else if (m->connect.onFailure5)
|
||||
{
|
||||
MQTTAsync_failureData5 data = MQTTAsync_failureData5_initializer;
|
||||
|
||||
data.token = 0;
|
||||
if (rc > 0) /* MQTT Reason Codes are > 0; C client return codes are < 0 */
|
||||
{
|
||||
/* MQTT 5 reason codes >= 0x00 and < 0x80 are successful,
|
||||
* but in that case we should not get here but be calling
|
||||
* onSuccess instead. */
|
||||
data.reasonCode = rc;
|
||||
data.code = MQTTASYNC_FAILURE;
|
||||
} else
|
||||
data.code = rc;
|
||||
data.message = message;
|
||||
Log(TRACE_MIN, -1, "Calling connect failure for client %s", m->c->clientID);
|
||||
(*(m->connect.onFailure5))(m->connect.context, &data);
|
||||
/* Null out callback pointers so they aren't accidentally called again */
|
||||
m->connect.onFailure5 = NULL;
|
||||
m->connect.onSuccess5 = NULL;
|
||||
}
|
||||
if (connectionLost_called == 0 && m->cl && was_connected)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling connectionLost for client %s", m->c->clientID);
|
||||
(*(m->cl))(m->clContext, NULL);
|
||||
}
|
||||
MQTTAsync_startConnectRetry(m);
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTAsync_checkTimeouts(void)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
static START_TIME_TYPE last = START_TIME_ZERO;
|
||||
START_TIME_TYPE now;
|
||||
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
now = MQTTTime_now();
|
||||
if (MQTTTime_difftime(now, last) < (DIFF_TIME_TYPE)3000)
|
||||
goto exit;
|
||||
last = now;
|
||||
while (ListNextElement(MQTTAsync_handles, ¤t)) /* for each client */
|
||||
{
|
||||
MQTTAsyncs* m = (MQTTAsyncs*)(current->content);
|
||||
|
||||
/* check disconnect timeout */
|
||||
if (m->c->connect_state == DISCONNECTING)
|
||||
MQTTAsync_checkDisconnect(m, &m->disconnect);
|
||||
|
||||
/* check connect timeout */
|
||||
if (m->c->connect_state != NOT_IN_PROGRESS && MQTTTime_elapsed(m->connect.start_time) > (ELAPSED_TIME_TYPE)(m->connectTimeout * 1000))
|
||||
{
|
||||
nextOrClose(m, MQTTASYNC_FAILURE, "TCP connect timeout");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* There was a section here that removed timed-out responses. But if the command had completed and
|
||||
* there was a response, then we may as well report it, no?
|
||||
*
|
||||
* In any case, that section was disabled when automatic reconnect was implemented.
|
||||
*/
|
||||
|
||||
if (m->automaticReconnect && m->retrying)
|
||||
{
|
||||
if (m->reconnectNow || MQTTTime_elapsed(m->lastConnectionFailedTime) > (ELAPSED_TIME_TYPE)(m->currentInterval * 1000))
|
||||
{
|
||||
/* to reconnect put the connect command to the head of the command queue */
|
||||
MQTTAsync_queuedCommand* conn = malloc(sizeof(MQTTAsync_queuedCommand));
|
||||
if (!conn)
|
||||
goto exit;
|
||||
memset(conn, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
conn->client = m;
|
||||
conn->command = m->connect;
|
||||
/* make sure that the version attempts are restarted */
|
||||
if (m->c->MQTTVersion == MQTTVERSION_DEFAULT)
|
||||
conn->command.details.conn.MQTTVersion = 0;
|
||||
if (m->updateConnectOptions)
|
||||
{
|
||||
MQTTAsync_connectData connectData = MQTTAsync_connectData_initializer;
|
||||
int callback_rc = MQTTASYNC_SUCCESS;
|
||||
|
||||
connectData.username = m->c->username;
|
||||
connectData.binarypwd.data = m->c->password;
|
||||
connectData.binarypwd.len = m->c->passwordlen;
|
||||
Log(TRACE_MIN, -1, "Calling updateConnectOptions for client %s", m->c->clientID);
|
||||
callback_rc = (*(m->updateConnectOptions))(m->updateConnectOptions_context, &connectData);
|
||||
|
||||
if (callback_rc)
|
||||
{
|
||||
if (connectData.username != m->c->username)
|
||||
{
|
||||
if (m->c->username)
|
||||
free((void*)m->c->username);
|
||||
if (connectData.username)
|
||||
m->c->username = connectData.username; /* must be allocated by MQTTAsync_malloc in the callback */
|
||||
else
|
||||
m->c->username = NULL;
|
||||
}
|
||||
if (connectData.binarypwd.data != m->c->password)
|
||||
{
|
||||
if (m->c->password)
|
||||
free((void*)m->c->password);
|
||||
if (connectData.binarypwd.data)
|
||||
{
|
||||
m->c->passwordlen = connectData.binarypwd.len;
|
||||
m->c->password = connectData.binarypwd.data; /* must be allocated by MQTTAsync_malloc in the callback */
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->password = NULL;
|
||||
m->c->passwordlen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Log(TRACE_MIN, -1, "Automatically attempting to reconnect");
|
||||
MQTTAsync_addCommand(conn, sizeof(m->connect));
|
||||
m->reconnectNow = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
exit:
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
thread_return_type WINAPI MQTTAsync_sendThread(void* n)
|
||||
{
|
||||
int timeout = 10; /* first time in we have a small timeout. Gets things started more quickly */
|
||||
|
||||
FUNC_ENTRY;
|
||||
Thread_set_name("MQTTAsync_send");
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
sendThread_state = RUNNING;
|
||||
sendThread_id = Paho_thread_getid();
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
while (!MQTTAsync_tostop)
|
||||
{
|
||||
int rc;
|
||||
int command_count = 0;
|
||||
|
||||
MQTTAsync_lock_mutex(mqttcommand_mutex);
|
||||
command_count = MQTTAsync_commands->count;
|
||||
MQTTAsync_unlock_mutex(mqttcommand_mutex);
|
||||
while (command_count > 0)
|
||||
{
|
||||
if (MQTTAsync_processCommand() == 0)
|
||||
break; /* no commands were processed, so go into a wait */
|
||||
MQTTAsync_lock_mutex(mqttcommand_mutex);
|
||||
command_count = MQTTAsync_commands->count;
|
||||
MQTTAsync_unlock_mutex(mqttcommand_mutex);
|
||||
}
|
||||
if ((rc = Thread_wait_evt(send_evt, timeout)) != 0 && rc != ETIMEDOUT)
|
||||
Log(LOG_ERROR, -1, "Error %d waiting for send event", rc);
|
||||
timeout = 1000; /* 1 second for follow on waits */
|
||||
MQTTAsync_checkTimeouts();
|
||||
}
|
||||
sendThread_state = STOPPING;
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
sendThread_state = STOPPED;
|
||||
sendThread_id = 0;
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
|
||||
#if defined(OPENSSL)
|
||||
#if ((OPENSSL_VERSION_NUMBER < 0x1010000fL) || defined(LIBRESSL_VERSION_NUMBER)) || defined(OPENSSL_IS_BORINGSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
OPENSSL_thread_stop();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FUNC_EXIT;
|
||||
#if defined(_WIN32)
|
||||
ExitThread(0);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_emptyMessageQueue(Clients* client)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
/* empty message queue */
|
||||
if (client->messageQueue->count > 0)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
while (ListNextElement(client->messageQueue, ¤t))
|
||||
{
|
||||
qEntry* qe = (qEntry*)(current->content);
|
||||
free(qe->topicName);
|
||||
free(qe->msg->payload);
|
||||
free(qe->msg);
|
||||
}
|
||||
ListEmpty(client->messageQueue);
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_freeResponses(MQTTAsyncs* m)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (m->responses)
|
||||
{
|
||||
ListElement* cur_response = NULL;
|
||||
|
||||
while (ListNextElement(m->responses, &cur_response))
|
||||
{
|
||||
MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(cur_response->content);
|
||||
|
||||
if (command->command.onFailure)
|
||||
{
|
||||
MQTTAsync_failureData data;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.code = MQTTASYNC_OPERATION_INCOMPLETE; /* interrupted return code */
|
||||
data.message = NULL;
|
||||
|
||||
Log(TRACE_MIN, -1, "Calling %s failure for client %s",
|
||||
MQTTPacket_name(command->command.type), m->c->clientID);
|
||||
(*(command->command.onFailure))(command->command.context, &data);
|
||||
}
|
||||
else if (command->command.onFailure5)
|
||||
{
|
||||
MQTTAsync_failureData5 data = MQTTAsync_failureData5_initializer;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.code = MQTTASYNC_OPERATION_INCOMPLETE; /* interrupted return code */
|
||||
data.message = NULL;
|
||||
|
||||
Log(TRACE_MIN, -1, "Calling %s failure for client %s",
|
||||
MQTTPacket_name(command->command.type), m->c->clientID);
|
||||
(*(command->command.onFailure5))(command->command.context, &data);
|
||||
}
|
||||
|
||||
MQTTAsync_freeCommand1(command);
|
||||
count++;
|
||||
}
|
||||
ListEmpty(m->responses);
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d responses removed for client %s", count, m->c->clientID);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_freeCommands(MQTTAsyncs* m)
|
||||
{
|
||||
int count = 0;
|
||||
ListElement* current = NULL;
|
||||
ListElement *next = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* remove commands in the command queue relating to this client */
|
||||
current = ListNextElement(MQTTAsync_commands, &next);
|
||||
ListNextElement(MQTTAsync_commands, &next);
|
||||
while (current)
|
||||
{
|
||||
MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
|
||||
|
||||
if (command->client == m)
|
||||
{
|
||||
ListDetach(MQTTAsync_commands, command);
|
||||
|
||||
if (command->command.onFailure)
|
||||
{
|
||||
MQTTAsync_failureData data;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.code = MQTTASYNC_OPERATION_INCOMPLETE; /* interrupted return code */
|
||||
data.message = NULL;
|
||||
|
||||
Log(TRACE_MIN, -1, "Calling %s failure for client %s",
|
||||
MQTTPacket_name(command->command.type), m->c->clientID);
|
||||
(*(command->command.onFailure))(command->command.context, &data);
|
||||
}
|
||||
else if (command->command.onFailure5)
|
||||
{
|
||||
MQTTAsync_failureData5 data = MQTTAsync_failureData5_initializer;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.code = MQTTASYNC_OPERATION_INCOMPLETE; /* interrupted return code */
|
||||
data.message = NULL;
|
||||
|
||||
Log(TRACE_MIN, -1, "Calling %s failure for client %s",
|
||||
MQTTPacket_name(command->command.type), m->c->clientID);
|
||||
(*(command->command.onFailure5))(command->command.context, &data);
|
||||
}
|
||||
|
||||
MQTTAsync_freeCommand(command);
|
||||
count++;
|
||||
}
|
||||
current = next;
|
||||
ListNextElement(MQTTAsync_commands, &next);
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d commands removed for client %s", count, m->c->clientID);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTAsync_completeConnection(MQTTAsyncs* m, Connack* connack)
|
||||
{
|
||||
int rc = MQTTASYNC_FAILURE;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (m->c->connect_state == WAIT_FOR_CONNACK) /* MQTT connect sent - wait for CONNACK */
|
||||
{
|
||||
Log(LOG_PROTOCOL, 1, NULL, m->c->net.socket, m->c->clientID, connack->rc);
|
||||
if ((rc = connack->rc) == MQTTASYNC_SUCCESS)
|
||||
{
|
||||
m->retrying = 0;
|
||||
m->c->connected = 1;
|
||||
m->c->good = 1;
|
||||
m->c->connect_state = NOT_IN_PROGRESS;
|
||||
if (m->c->cleansession || m->c->cleanstart)
|
||||
rc = MQTTAsync_cleanSession(m->c);
|
||||
else if (m->c->MQTTVersion >= MQTTVERSION_3_1_1 && connack->flags.bits.sessionPresent == 0)
|
||||
{
|
||||
Log(LOG_PROTOCOL, -1, "Cleaning session state on connect because sessionPresent is 0");
|
||||
rc = MQTTAsync_cleanSession(m->c);
|
||||
}
|
||||
if (m->c->outboundMsgs->count > 0)
|
||||
{
|
||||
ListElement* outcurrent = NULL;
|
||||
START_TIME_TYPE zero = START_TIME_ZERO;
|
||||
|
||||
while (ListNextElement(m->c->outboundMsgs, &outcurrent))
|
||||
{
|
||||
Messages* messages = (Messages*)(outcurrent->content);
|
||||
memset(&messages->lastTouch, '\0', sizeof(messages->lastTouch));
|
||||
}
|
||||
MQTTProtocol_retry(zero, 1, 1);
|
||||
if (m->c->connected != 1)
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
}
|
||||
if (m->c->MQTTVersion == MQTTVERSION_5)
|
||||
{
|
||||
if (MQTTProperties_hasProperty(&connack->properties, MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE))
|
||||
{
|
||||
/* update the keep alive from the server keep alive */
|
||||
int server_keep_alive = (int)MQTTProperties_getNumericValue(&connack->properties, MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE);
|
||||
if (server_keep_alive != -999999)
|
||||
{
|
||||
Log(LOG_PROTOCOL, -1, "Setting keep alive interval to server keep alive %d", server_keep_alive);
|
||||
m->c->keepAliveInterval = server_keep_alive;
|
||||
}
|
||||
}
|
||||
else if (m->c->keepAliveInterval != m->c->savedKeepAliveInterval)
|
||||
{
|
||||
/* if the keep alive has been previously updated with a server keep alive, but there is no server keep alive
|
||||
on this connect, reset it to the value requested in the original connect API */
|
||||
Log(LOG_PROTOCOL, -1, "Resetting keep alive interval to %d", m->c->savedKeepAliveInterval);
|
||||
m->c->keepAliveInterval = m->c->savedKeepAliveInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
m->pack = NULL;
|
||||
Thread_signal_evt(send_evt);
|
||||
}
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* This is the thread function that handles the calling of callback functions if set */
|
||||
thread_return_type WINAPI MQTTAsync_receiveThread(void* n)
|
||||
{
|
||||
long timeout = 10L; /* first time in we have a small timeout. Gets things started more quickly */
|
||||
|
||||
FUNC_ENTRY;
|
||||
Thread_set_name("MQTTAsync_rcv");
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
receiveThread_state = RUNNING;
|
||||
receiveThread_id = Paho_thread_getid();
|
||||
while (!MQTTAsync_tostop)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
SOCKET sock = -1;
|
||||
MQTTAsyncs* m = NULL;
|
||||
MQTTPacket* pack = NULL;
|
||||
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
Log(TRACE_MINIMUM, -1, "MQTTAsync_cycle call timeout %lu", timeout);
|
||||
pack = MQTTAsync_cycle(&sock, timeout, &rc);
|
||||
Log(TRACE_MINIMUM, -1, "MQTTAsync_cycle returns pack %p, sock %d, timeout %lu, rc %d", pack, sock, timeout, rc);
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
if (MQTTAsync_tostop)
|
||||
break;
|
||||
|
||||
if (sock == 0)
|
||||
continue;
|
||||
timeout = 1000L;
|
||||
|
||||
/* find client corresponding to socket */
|
||||
if (ListFindItem(MQTTAsync_handles, &sock, clientSockCompare) == NULL)
|
||||
{
|
||||
Log(TRACE_MINIMUM, -1, "Could not find client corresponding to socket %d", sock);
|
||||
/* Socket_close(sock); - removing socket in this case is not necessary (Bug 442400) */
|
||||
continue;
|
||||
}
|
||||
m = (MQTTAsyncs*)(MQTTAsync_handles->current->content);
|
||||
if (m == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, -1, "Client structure was NULL for socket %d - removing socket", sock);
|
||||
Socket_close(sock);
|
||||
continue;
|
||||
}
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
Log(TRACE_MINIMUM, -1, "Error from MQTTAsync_cycle() - removing socket %d", sock);
|
||||
nextOrClose(m, rc, "socket error");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m->c->messageQueue->count > 0 && m->ma)
|
||||
{
|
||||
qEntry* qe = (qEntry*)(m->c->messageQueue->first->content);
|
||||
int topicLen = qe->topicLen;
|
||||
|
||||
if (strlen(qe->topicName) == topicLen)
|
||||
topicLen = 0;
|
||||
|
||||
if (MQTTAsync_deliverMessage(m, qe->topicName, topicLen, qe->msg))
|
||||
{
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (m->c->persistence)
|
||||
MQTTPersistence_unpersistQueueEntry(m->c, (MQTTPersistence_qEntry*)qe);
|
||||
#endif
|
||||
ListRemove(m->c->messageQueue, qe); /* qe is freed here */
|
||||
}
|
||||
else
|
||||
Log(TRACE_MIN, -1, "False returned from messageArrived for client %s, message remains on queue",
|
||||
m->c->clientID);
|
||||
}
|
||||
if (pack)
|
||||
{
|
||||
if (pack->header.bits.type == CONNACK)
|
||||
{
|
||||
Connack* connack = (Connack*)pack;
|
||||
int sessionPresent = connack->flags.bits.sessionPresent;
|
||||
|
||||
rc = MQTTAsync_completeConnection(m, connack);
|
||||
if (rc == MQTTASYNC_SUCCESS)
|
||||
{
|
||||
int onSuccess = 0;
|
||||
if ((m->serverURIcount > 0)
|
||||
&& (m->connect.details.conn.currentURI < m->serverURIcount))
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Connect succeeded to %s",
|
||||
m->serverURIs[m->connect.details.conn.currentURI]);
|
||||
}
|
||||
onSuccess = (m->connect.onSuccess != NULL ||
|
||||
m->connect.onSuccess5 != NULL); /* save setting of onSuccess callback */
|
||||
if (m->connect.onSuccess)
|
||||
{
|
||||
MQTTAsync_successData data;
|
||||
memset(&data, '\0', sizeof(data));
|
||||
Log(TRACE_MIN, -1, "Calling connect success for client %s", m->c->clientID);
|
||||
if ((m->serverURIcount > 0)
|
||||
&& (m->connect.details.conn.currentURI < m->serverURIcount))
|
||||
data.alt.connect.serverURI = m->serverURIs[m->connect.details.conn.currentURI];
|
||||
else
|
||||
data.alt.connect.serverURI = m->serverURI;
|
||||
data.alt.connect.MQTTVersion = m->connect.details.conn.MQTTVersion;
|
||||
data.alt.connect.sessionPresent = sessionPresent;
|
||||
(*(m->connect.onSuccess))(m->connect.context, &data);
|
||||
/* Null out callback pointers so they aren't accidentally called again */
|
||||
m->connect.onSuccess = NULL;
|
||||
m->connect.onFailure = NULL;
|
||||
}
|
||||
else if (m->connect.onSuccess5)
|
||||
{
|
||||
MQTTAsync_successData5 data = MQTTAsync_successData5_initializer;
|
||||
Log(TRACE_MIN, -1, "Calling connect success for client %s", m->c->clientID);
|
||||
if (m->serverURIcount > 0)
|
||||
data.alt.connect.serverURI = m->serverURIs[m->connect.details.conn.currentURI];
|
||||
else
|
||||
data.alt.connect.serverURI = m->serverURI;
|
||||
data.alt.connect.MQTTVersion = m->connect.details.conn.MQTTVersion;
|
||||
data.alt.connect.sessionPresent = sessionPresent;
|
||||
data.properties = connack->properties;
|
||||
data.reasonCode = connack->rc;
|
||||
(*(m->connect.onSuccess5))(m->connect.context, &data);
|
||||
/* Null out callback pointers so they aren't accidentally called again */
|
||||
m->connect.onSuccess5 = NULL;
|
||||
m->connect.onFailure5 = NULL;
|
||||
}
|
||||
if (m->connected)
|
||||
{
|
||||
char* reason = (onSuccess) ? "connect onSuccess called" : "automatic reconnect";
|
||||
Log(TRACE_MIN, -1, "Calling connected for client %s", m->c->clientID);
|
||||
(*(m->connected))(m->connected_context, reason);
|
||||
}
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (MQTTProperties_hasProperty(&connack->properties, MQTTPROPERTY_CODE_RECEIVE_MAXIMUM))
|
||||
{
|
||||
int recv_max = (int)MQTTProperties_getNumericValue(&connack->properties, MQTTPROPERTY_CODE_RECEIVE_MAXIMUM);
|
||||
if (m->c->maxInflightMessages > recv_max)
|
||||
m->c->maxInflightMessages = recv_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nextOrClose(m, rc, "CONNACK return code");
|
||||
}
|
||||
MQTTPacket_freeConnack(connack);
|
||||
}
|
||||
else if (pack->header.bits.type == SUBACK)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
|
||||
/* use the msgid to find the callback to be called */
|
||||
while (ListNextElement(m->responses, ¤t))
|
||||
{
|
||||
MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
|
||||
if (command->command.token == ((Suback*)pack)->msgId)
|
||||
{
|
||||
Suback* sub = (Suback*)pack;
|
||||
if (!ListDetach(m->responses, command)) /* remove the response from the list */
|
||||
Log(LOG_ERROR, -1, "Subscribe command not removed from command list");
|
||||
|
||||
/* Call the failure callback if there is one subscribe in the MQTT packet and
|
||||
* the return code is 0x80 (failure). If the MQTT packet contains >1 subscription
|
||||
* request, then we call onSuccess with the list of returned QoSs, which inelegantly,
|
||||
* could include some failures, or worse, the whole list could have failed.
|
||||
*/
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (sub->qoss->count == 1 && *(int*)(sub->qoss->first->content) >= MQTTREASONCODE_UNSPECIFIED_ERROR)
|
||||
{
|
||||
if (command->command.onFailure5)
|
||||
{
|
||||
MQTTAsync_failureData5 data = MQTTAsync_failureData5_initializer;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.reasonCode = *(int*)(sub->qoss->first->content);
|
||||
data.message = NULL;
|
||||
data.properties = sub->properties;
|
||||
Log(TRACE_MIN, -1, "Calling subscribe failure for client %s", m->c->clientID);
|
||||
(*(command->command.onFailure5))(command->command.context, &data);
|
||||
}
|
||||
}
|
||||
else if (command->command.onSuccess5)
|
||||
{
|
||||
MQTTAsync_successData5 data;
|
||||
enum MQTTReasonCodes* array = NULL;
|
||||
|
||||
data.reasonCode = *(int*)(sub->qoss->first->content);
|
||||
data.alt.sub.reasonCodeCount = sub->qoss->count;
|
||||
if (sub->qoss->count > 1)
|
||||
{
|
||||
ListElement* cur_qos = NULL;
|
||||
enum MQTTReasonCodes* element = array = data.alt.sub.reasonCodes = malloc(sub->qoss->count * sizeof(enum MQTTReasonCodes));
|
||||
if (array)
|
||||
while (ListNextElement(sub->qoss, &cur_qos))
|
||||
*element++ = *(int*)(cur_qos->content);
|
||||
}
|
||||
data.token = command->command.token;
|
||||
data.properties = sub->properties;
|
||||
Log(TRACE_MIN, -1, "Calling subscribe success for client %s", m->c->clientID);
|
||||
(*(command->command.onSuccess5))(command->command.context, &data);
|
||||
if (array)
|
||||
free(array);
|
||||
}
|
||||
}
|
||||
else if (sub->qoss->count == 1 && *(int*)(sub->qoss->first->content) == MQTT_BAD_SUBSCRIBE)
|
||||
{
|
||||
if (command->command.onFailure)
|
||||
{
|
||||
MQTTAsync_failureData data;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.code = *(int*)(sub->qoss->first->content);
|
||||
data.message = NULL;
|
||||
Log(TRACE_MIN, -1, "Calling subscribe failure for client %s", m->c->clientID);
|
||||
(*(command->command.onFailure))(command->command.context, &data);
|
||||
}
|
||||
}
|
||||
else if (command->command.onSuccess)
|
||||
{
|
||||
MQTTAsync_successData data;
|
||||
int* array = NULL;
|
||||
|
||||
if (sub->qoss->count == 1)
|
||||
data.alt.qos = *(int*)(sub->qoss->first->content);
|
||||
else if (sub->qoss->count > 1)
|
||||
{
|
||||
ListElement* cur_qos = NULL;
|
||||
int* element = array = data.alt.qosList = malloc(sub->qoss->count * sizeof(int));
|
||||
if (array)
|
||||
while (ListNextElement(sub->qoss, &cur_qos))
|
||||
*element++ = *(int*)(cur_qos->content);
|
||||
}
|
||||
data.token = command->command.token;
|
||||
Log(TRACE_MIN, -1, "Calling subscribe success for client %s", m->c->clientID);
|
||||
(*(command->command.onSuccess))(command->command.context, &data);
|
||||
if (array)
|
||||
free(array);
|
||||
}
|
||||
MQTTAsync_freeCommand(command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rc = MQTTProtocol_handleSubacks(pack, m->c->net.socket);
|
||||
}
|
||||
else if (pack->header.bits.type == UNSUBACK)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
Unsuback* unsub = (Unsuback*)pack;
|
||||
|
||||
/* use the msgid to find the callback to be called */
|
||||
while (ListNextElement(m->responses, ¤t))
|
||||
{
|
||||
MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
|
||||
if (command->command.token == ((Unsuback*)pack)->msgId)
|
||||
{
|
||||
if (!ListDetach(m->responses, command)) /* remove the response from the list */
|
||||
Log(LOG_ERROR, -1, "Unsubscribe command not removed from command list");
|
||||
if (command->command.onSuccess || command->command.onSuccess5)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling unsubscribe success for client %s", m->c->clientID);
|
||||
if (command->command.onSuccess)
|
||||
{
|
||||
MQTTAsync_successData data;
|
||||
|
||||
memset(&data, '\0', sizeof(data));
|
||||
data.token = command->command.token;
|
||||
(*(command->command.onSuccess))(command->command.context, &data);
|
||||
}
|
||||
else
|
||||
{
|
||||
MQTTAsync_successData5 data = MQTTAsync_successData5_initializer;
|
||||
enum MQTTReasonCodes* array = NULL;
|
||||
|
||||
data.reasonCode = *(enum MQTTReasonCodes*)(unsub->reasonCodes->first->content);
|
||||
data.alt.unsub.reasonCodeCount = unsub->reasonCodes->count;
|
||||
if (unsub->reasonCodes->count > 1)
|
||||
{
|
||||
ListElement* cur_rc = NULL;
|
||||
enum MQTTReasonCodes* element = array = data.alt.unsub.reasonCodes = malloc(unsub->reasonCodes->count * sizeof(enum MQTTReasonCodes));
|
||||
if (array)
|
||||
while (ListNextElement(unsub->reasonCodes, &cur_rc))
|
||||
*element++ = *(enum MQTTReasonCodes*)(cur_rc->content);
|
||||
}
|
||||
data.token = command->command.token;
|
||||
data.properties = unsub->properties;
|
||||
Log(TRACE_MIN, -1, "Calling unsubscribe success for client %s", m->c->clientID);
|
||||
(*(command->command.onSuccess5))(command->command.context, &data);
|
||||
if (array)
|
||||
free(array);
|
||||
}
|
||||
}
|
||||
MQTTAsync_freeCommand(command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rc = MQTTProtocol_handleUnsubacks(pack, m->c->net.socket);
|
||||
}
|
||||
else if (pack->header.bits.type == DISCONNECT)
|
||||
{
|
||||
Ack* disc = (Ack*)pack;
|
||||
int discrc = 0;
|
||||
|
||||
discrc = disc->rc;
|
||||
if (m->disconnected)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling disconnected for client %s", m->c->clientID);
|
||||
(*(m->disconnected))(m->disconnected_context, &disc->properties, disc->rc);
|
||||
}
|
||||
rc = MQTTProtocol_handleDisconnects(pack, m->c->net.socket);
|
||||
m->c->connected = 0; /* don't send disconnect packet back */
|
||||
nextOrClose(m, discrc, "Received disconnect");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(LOG_ERROR, -1, "An unexpected packet type %u has been received", pack->header.bits.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
receiveThread_state = STOPPED;
|
||||
receiveThread_id = 0;
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
if (sendThread_state != STOPPED)
|
||||
Thread_signal_evt(send_evt);
|
||||
|
||||
#if defined(OPENSSL)
|
||||
#if ((OPENSSL_VERSION_NUMBER < 0x1010000fL) || defined(LIBRESSL_VERSION_NUMBER)) || defined(OPENSSL_IS_BORINGSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
OPENSSL_thread_stop();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FUNC_EXIT;
|
||||
#if defined(_WIN32)
|
||||
ExitThread(0);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTAsync_stop(void)
|
||||
{
|
||||
#if !defined(NOSTACKTRACE)
|
||||
int rc = 0;
|
||||
#endif
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (sendThread_state != STOPPED || receiveThread_state != STOPPED)
|
||||
{
|
||||
int conn_count = 0;
|
||||
ListElement* current = NULL;
|
||||
|
||||
if (MQTTAsync_handles != NULL)
|
||||
{
|
||||
/* find out how many handles are still connected */
|
||||
while (ListNextElement(MQTTAsync_handles, ¤t))
|
||||
{
|
||||
if (((MQTTAsyncs*)(current->content))->c->connect_state > NOT_IN_PROGRESS ||
|
||||
((MQTTAsyncs*)(current->content))->c->connected)
|
||||
++conn_count;
|
||||
}
|
||||
}
|
||||
Log(TRACE_MIN, -1, "Conn_count is %d", conn_count);
|
||||
/* stop the background thread, if we are the last one to be using it */
|
||||
if (conn_count == 0)
|
||||
{
|
||||
int count = 0;
|
||||
MQTTAsync_tostop = 1;
|
||||
while ((sendThread_state != STOPPED || receiveThread_state != STOPPED) && MQTTAsync_tostop != 0 && ++count < 100)
|
||||
{
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
Log(TRACE_MIN, -1, "sleeping");
|
||||
MQTTAsync_sleep(100L);
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
}
|
||||
#if !defined(NOSTACKTRACE)
|
||||
rc = 1;
|
||||
#endif
|
||||
MQTTAsync_tostop = 0;
|
||||
}
|
||||
}
|
||||
FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
|
||||
static void MQTTAsync_closeOnly(Clients* client, enum MQTTReasonCodes reasonCode, MQTTProperties* props)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
client->good = 0;
|
||||
client->ping_outstanding = 0;
|
||||
client->ping_due = 0;
|
||||
if (client->net.socket > 0)
|
||||
{
|
||||
MQTTProtocol_checkPendingWrites();
|
||||
if (client->connected && MQTTAsync_Socket_noPendingWrites(client->net.socket))
|
||||
MQTTPacket_send_disconnect(client, reasonCode, props);
|
||||
MQTTAsync_lock_mutex(socket_mutex);
|
||||
WebSocket_close(&client->net, WebSocket_CLOSE_NORMAL, NULL);
|
||||
#if defined(OPENSSL)
|
||||
SSL_SESSION_free(client->session); /* is a no-op if session is NULL */
|
||||
client->session = NULL; /* show the session has been freed */
|
||||
SSLSocket_close(&client->net);
|
||||
#endif
|
||||
MQTTAsync_unlock_mutex(socket_mutex);
|
||||
Socket_close(client->net.socket); /* Socket_close locks socket mutex itself */
|
||||
client->net.socket = 0;
|
||||
#if defined(OPENSSL)
|
||||
client->net.ssl = NULL;
|
||||
#endif
|
||||
}
|
||||
client->connected = 0;
|
||||
client->connect_state = NOT_IN_PROGRESS;
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void MQTTAsync_closeSession(Clients* client, enum MQTTReasonCodes reasonCode, MQTTProperties* props)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
MQTTAsync_closeOnly(client, reasonCode, props);
|
||||
|
||||
if (client->cleansession ||
|
||||
(client->MQTTVersion >= MQTTVERSION_5 && client->sessionExpiry == 0))
|
||||
MQTTAsync_cleanSession(client);
|
||||
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing clients by client structure
|
||||
* @param a Async structure
|
||||
* @param b Client structure
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
static int clientStructCompare(void* a, void* b)
|
||||
{
|
||||
MQTTAsyncs* m = (MQTTAsyncs*)a;
|
||||
return m->c == (Clients*)b;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set destinationName and payload to NULL in all responses
|
||||
* for a client, so that these memory locations aren't freed twice as they
|
||||
* are also stored by MQTTProtocol_storePublication.
|
||||
* @param m the client to process
|
||||
*/
|
||||
void MQTTAsync_NULLPublishResponses(MQTTAsyncs* m)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (m->responses)
|
||||
{
|
||||
ListElement* cur_response = NULL;
|
||||
|
||||
while (ListNextElement(m->responses, &cur_response))
|
||||
{
|
||||
MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(cur_response->content);
|
||||
if (command->command.type == PUBLISH)
|
||||
{
|
||||
/* these values are going to be freed in RemovePublication */
|
||||
command->command.details.pub.destinationName = NULL;
|
||||
command->command.details.pub.payload = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
#if 0 /* removed as part of fix for issue 1474 */
|
||||
/*
|
||||
* Set destinationName and payload to NULL in all commands
|
||||
* for a client, so that these memory locations aren't freed twice as they
|
||||
* are also stored by MQTTProtocol_storePublication.
|
||||
* @param m the client to process
|
||||
*/
|
||||
void MQTTAsync_NULLPublishCommands(MQTTAsyncs* m)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
ListElement *next = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
current = ListNextElement(MQTTAsync_commands, &next);
|
||||
ListNextElement(MQTTAsync_commands, &next);
|
||||
while (current)
|
||||
{
|
||||
MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
|
||||
|
||||
if (command->client == m && command->command.type == PUBLISH)
|
||||
{
|
||||
/* these values are going to be freed in RemovePublication */
|
||||
command->command.details.pub.destinationName = NULL;
|
||||
command->command.details.pub.payload = NULL;
|
||||
}
|
||||
current = next;
|
||||
ListNextElement(MQTTAsync_commands, &next);
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Clean the MQTT session data. This includes the MQTT inflight messages, because
|
||||
* that is part of the MQTT state that will be cleared by the MQTT broker too.
|
||||
* However, queued up messages, outgoing or incoming, need (should?) not be cleared
|
||||
* as they are outside the scope of the MQTT session.
|
||||
*/
|
||||
static int MQTTAsync_cleanSession(Clients* client)
|
||||
{
|
||||
int rc = 0;
|
||||
ListElement* found = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
rc = MQTTAsync_unpersistInflightMessages(client);
|
||||
#endif
|
||||
MQTTProtocol_emptyMessageList(client->inboundMsgs);
|
||||
MQTTProtocol_emptyMessageList(client->outboundMsgs);
|
||||
client->msgID = 0;
|
||||
if ((found = ListFindItem(MQTTAsync_handles, client, clientStructCompare)) != NULL)
|
||||
{
|
||||
MQTTAsyncs* m = (MQTTAsyncs*)(found->content);
|
||||
MQTTAsync_NULLPublishResponses(m);
|
||||
MQTTAsync_freeResponses(m);
|
||||
}
|
||||
else
|
||||
Log(LOG_ERROR, -1, "cleanSession: did not find client structure in handles list");
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Deliver a message to the messageArrived callback
|
||||
* @param m a client structure
|
||||
* @param topicName the name of the topic on which the message is being delivered
|
||||
* @param topicLen the length of the topic name string
|
||||
* @param mm the message to be delivered
|
||||
* @return boolean 1 means message has been delivered, 0 that it has not
|
||||
*/
|
||||
static int MQTTAsync_deliverMessage(MQTTAsyncs* m, char* topicName, size_t topicLen, MQTTAsync_message* mm)
|
||||
{
|
||||
int rc;
|
||||
|
||||
Log(TRACE_MIN, -1, "Calling messageArrived for client %s, queue depth %d",
|
||||
m->c->clientID, m->c->messageQueue->count);
|
||||
rc = (*(m->ma))(m->maContext, topicName, (int)topicLen, mm);
|
||||
/* if 0 (false) is returned by the callback then it failed, so we don't remove the message from
|
||||
* the queue, and it will be retried later. If 1 is returned then the message data may have been freed,
|
||||
* so we must be careful how we use it.
|
||||
*/
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void Protocol_processPublication(Publish* publish, Clients* client, int allocatePayload)
|
||||
{
|
||||
MQTTAsync_message* mm = NULL;
|
||||
MQTTAsync_message initialized = MQTTAsync_message_initializer;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((mm = malloc(sizeof(MQTTAsync_message))) == NULL)
|
||||
goto exit;
|
||||
memcpy(mm, &initialized, sizeof(MQTTAsync_message));
|
||||
|
||||
if (allocatePayload)
|
||||
{
|
||||
if ((mm->payload = malloc(publish->payloadlen)) == NULL)
|
||||
{
|
||||
free(mm);
|
||||
goto exit;
|
||||
}
|
||||
memcpy(mm->payload, publish->payload, publish->payloadlen);
|
||||
} else
|
||||
mm->payload = publish->payload;
|
||||
mm->payloadlen = publish->payloadlen;
|
||||
mm->qos = publish->header.bits.qos;
|
||||
mm->retained = publish->header.bits.retain;
|
||||
if (publish->header.bits.qos == 2)
|
||||
mm->dup = 0; /* ensure that a QoS2 message is not passed to the application with dup = 1 */
|
||||
else
|
||||
mm->dup = publish->header.bits.dup;
|
||||
mm->msgid = publish->msgId;
|
||||
|
||||
if (publish->MQTTVersion >= MQTTVERSION_5)
|
||||
mm->properties = MQTTProperties_copy(&publish->properties);
|
||||
|
||||
if (client->messageQueue->count == 0 && client->connected)
|
||||
{
|
||||
ListElement* found = NULL;
|
||||
|
||||
if ((found = ListFindItem(MQTTAsync_handles, client, clientStructCompare)) == NULL)
|
||||
Log(LOG_ERROR, -1, "processPublication: did not find client structure in handles list");
|
||||
else
|
||||
{
|
||||
MQTTAsyncs* m = (MQTTAsyncs*)(found->content);
|
||||
|
||||
if (m->ma)
|
||||
rc = MQTTAsync_deliverMessage(m, publish->topic, publish->topiclen, mm);
|
||||
else
|
||||
Log(LOG_ERROR, -1, "Message arrived for client %s but can't deliver it. No messageArrived callback",
|
||||
m->c->clientID);
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == 0) /* if message was not delivered, queue it up */
|
||||
{
|
||||
qEntry* qe = malloc(sizeof(qEntry));
|
||||
|
||||
if (!qe)
|
||||
goto exit;
|
||||
qe->msg = mm;
|
||||
qe->topicName = publish->topic;
|
||||
qe->topicLen = publish->topiclen;
|
||||
ListAppend(client->messageQueue, qe, sizeof(qe) + sizeof(mm) + mm->payloadlen + strlen(qe->topicName)+1);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (client->persistence)
|
||||
MQTTPersistence_persistQueueEntry(client, (MQTTPersistence_qEntry*)qe);
|
||||
#endif
|
||||
}
|
||||
exit:
|
||||
publish->topic = NULL;
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static int retryLoopIntervalms = 5000;
|
||||
|
||||
void setRetryLoopInterval(int keepalive)
|
||||
{
|
||||
retryLoopIntervalms = (keepalive*1000) / 10;
|
||||
|
||||
if (retryLoopIntervalms < 100)
|
||||
retryLoopIntervalms = 100;
|
||||
else if (retryLoopIntervalms > 5000)
|
||||
retryLoopIntervalms = 5000;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_disconnect1(MQTTAsync handle, const MQTTAsync_disconnectOptions* options, int internal)
|
||||
{
|
||||
MQTTAsyncs* m = handle;
|
||||
int rc = MQTTASYNC_SUCCESS;
|
||||
MQTTAsync_queuedCommand* dis;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (m == NULL || m->c == NULL)
|
||||
{
|
||||
rc = MQTTASYNC_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
if (!internal)
|
||||
m->shouldBeConnected = 0;
|
||||
if (m->c->connected == 0)
|
||||
{
|
||||
rc = MQTTASYNC_DISCONNECTED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Add disconnect request to operation queue */
|
||||
if ((dis = malloc(sizeof(MQTTAsync_queuedCommand))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(dis, '\0', sizeof(MQTTAsync_queuedCommand));
|
||||
dis->client = m;
|
||||
if (options)
|
||||
{
|
||||
dis->command.onSuccess = options->onSuccess;
|
||||
dis->command.onFailure = options->onFailure;
|
||||
dis->command.onSuccess5 = options->onSuccess5;
|
||||
dis->command.onFailure5 = options->onFailure5;
|
||||
dis->command.context = options->context;
|
||||
dis->command.details.dis.timeout = options->timeout;
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5 && options->struct_version >= 1)
|
||||
{
|
||||
dis->command.properties = MQTTProperties_copy(&options->properties);
|
||||
dis->command.details.dis.reasonCode = options->reasonCode;
|
||||
}
|
||||
}
|
||||
dis->command.type = DISCONNECT;
|
||||
dis->command.details.dis.internal = internal;
|
||||
rc = MQTTAsync_addCommand(dis, sizeof(dis));
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTAsync_disconnect_internal(MQTTAsync handle, int timeout)
|
||||
{
|
||||
MQTTAsync_disconnectOptions options = MQTTAsync_disconnectOptions_initializer;
|
||||
|
||||
options.timeout = timeout;
|
||||
return MQTTAsync_disconnect1(handle, &options, 1);
|
||||
}
|
||||
|
||||
|
||||
void MQTTProtocol_closeSession(Clients* c, int sendwill)
|
||||
{
|
||||
nextOrClose((MQTTAsync)c->context, MQTTASYNC_DISCONNECTED, "MQTTProtocol_closeSession");
|
||||
}
|
||||
|
||||
|
||||
static int cmdMessageIDCompare(void* a, void* b)
|
||||
{
|
||||
MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)a;
|
||||
return cmd->command.token == *(int*)b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assign a new message id for a client. Make sure it isn't already being used and does
|
||||
* not exceed the maximum.
|
||||
* @param m a client structure
|
||||
* @return the next message id to use, or 0 if none available
|
||||
*/
|
||||
int MQTTAsync_assignMsgId(MQTTAsyncs* m)
|
||||
{
|
||||
int start_msgid;
|
||||
int msgid;
|
||||
|
||||
/* need to check: commands list and response list for a client */
|
||||
FUNC_ENTRY;
|
||||
/* Fetch last message ID in locked state */
|
||||
start_msgid = m->c->msgID;
|
||||
msgid = start_msgid;
|
||||
|
||||
MQTTAsync_lock_mutex(mqttcommand_mutex);
|
||||
msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
|
||||
while (ListFindItem(MQTTAsync_commands, &msgid, cmdMessageIDCompare) ||
|
||||
ListFindItem(m->c->outboundMsgs, &msgid, messageIDCompare) ||
|
||||
ListFindItem(m->responses, &msgid, cmdMessageIDCompare))
|
||||
{
|
||||
msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
|
||||
if (msgid == start_msgid)
|
||||
{ /* we've tried them all - none free */
|
||||
msgid = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MQTTAsync_unlock_mutex(mqttcommand_mutex);
|
||||
if (msgid != 0)
|
||||
m->c->msgID = msgid;
|
||||
FUNC_EXIT_RC(msgid);
|
||||
return msgid;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTAsync_retry(void)
|
||||
{
|
||||
static START_TIME_TYPE last = START_TIME_ZERO;
|
||||
START_TIME_TYPE now;
|
||||
|
||||
FUNC_ENTRY;
|
||||
now = MQTTTime_now();
|
||||
if (MQTTTime_difftime(now, last) >= (DIFF_TIME_TYPE)(retryLoopIntervalms))
|
||||
{
|
||||
last = MQTTTime_now();
|
||||
MQTTProtocol_keepalive(now);
|
||||
MQTTProtocol_retry(now, 1, 0);
|
||||
}
|
||||
else
|
||||
MQTTProtocol_retry(now, 0, 0);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTAsync_connecting(MQTTAsyncs* m)
|
||||
{
|
||||
int rc = -1;
|
||||
char* serverURI = m->serverURI;
|
||||
#if defined(OPENSSL)
|
||||
int default_port = MQTT_DEFAULT_PORT;
|
||||
#endif
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
||||
/* This was reported in #1007, but I've not been able to reproduce it. It feels like this is
|
||||
* covering up the issue, if it exists. If the error message is ever seen, please consider
|
||||
* reporting the circumstances so that more debugging can occur. Thanks - IGC.
|
||||
*/
|
||||
if (m->connect.details.conn.MQTTVersion == MQTTVERSION_DEFAULT) /* should not happen - #1007 */
|
||||
{
|
||||
Log(LOG_ERROR, -1, "MQTT version is 0 in MQTTAsync_connecting");
|
||||
m->connect.details.conn.MQTTVersion = (m->c->MQTTVersion == MQTTVERSION_DEFAULT) ? MQTTVERSION_3_1_1 : m->c->MQTTVersion;
|
||||
}
|
||||
/* End of #1007 avoiding code */
|
||||
|
||||
if (m->serverURIcount > 0)
|
||||
{
|
||||
serverURI = m->serverURIs[m->connect.details.conn.currentURI];
|
||||
|
||||
/* skip URI scheme */
|
||||
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
|
||||
serverURI += strlen(URI_TCP);
|
||||
else if (strncmp(URI_MQTT, serverURI, strlen(URI_MQTT)) == 0)
|
||||
serverURI += strlen(URI_MQTT);
|
||||
else if (strncmp(URI_WS, serverURI, strlen(URI_WS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WS);
|
||||
#if defined(OPENSSL)
|
||||
default_port = WS_DEFAULT_PORT;
|
||||
#endif
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_SSL);
|
||||
default_port = SECURE_MQTT_DEFAULT_PORT;
|
||||
}
|
||||
else if (strncmp(URI_TLS, serverURI, strlen(URI_TLS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_TLS);
|
||||
default_port = SECURE_MQTT_DEFAULT_PORT;
|
||||
}
|
||||
else if (strncmp(URI_MQTTS, serverURI, strlen(URI_MQTTS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_MQTTS);
|
||||
default_port = SECURE_MQTT_DEFAULT_PORT;
|
||||
}
|
||||
else if (strncmp(URI_WSS, serverURI, strlen(URI_WSS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WSS);
|
||||
default_port = WSS_DEFAULT_PORT;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (m->c->connect_state == TCP_IN_PROGRESS) /* TCP connect started - check for completion */
|
||||
{
|
||||
int error;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
if ((rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
|
||||
rc = error;
|
||||
|
||||
if (rc != 0)
|
||||
goto exit;
|
||||
|
||||
Socket_clearPendingWrite(m->c->net.socket);
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (m->ssl)
|
||||
{
|
||||
int port;
|
||||
size_t hostname_len;
|
||||
int setSocketForSSLrc = 0;
|
||||
|
||||
if (m->c->net.https_proxy) {
|
||||
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
|
||||
if ((rc = Proxy_connect( &m->c->net, 1, serverURI)) == SOCKET_ERROR )
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hostname_len = MQTTProtocol_addressPort(serverURI, &port, NULL, default_port);
|
||||
setSocketForSSLrc = SSLSocket_setSocketForSSL(&m->c->net, m->c->sslopts,
|
||||
serverURI, hostname_len);
|
||||
|
||||
if (setSocketForSSLrc == 1)
|
||||
{
|
||||
if (m->c->session != NULL)
|
||||
if ((rc = SSL_set_session(m->c->net.ssl, m->c->session)) != 1)
|
||||
Log(TRACE_MIN, -1, "Failed to set SSL session with stored data, non critical");
|
||||
rc = m->c->sslopts->struct_version >= 3 ?
|
||||
SSLSocket_connect(m->c->net.ssl, m->c->net.socket, serverURI,
|
||||
m->c->sslopts->verify, m->c->sslopts->ssl_error_cb, m->c->sslopts->ssl_error_context) :
|
||||
SSLSocket_connect(m->c->net.ssl, m->c->net.socket, serverURI,
|
||||
m->c->sslopts->verify, NULL, NULL);
|
||||
if (rc == TCPSOCKET_INTERRUPTED)
|
||||
{
|
||||
rc = MQTTCLIENT_SUCCESS; /* the connect is still in progress */
|
||||
m->c->connect_state = SSL_IN_PROGRESS;
|
||||
}
|
||||
else if (rc == SSL_FATAL)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
else if (rc == 1)
|
||||
{
|
||||
if ( m->websocket )
|
||||
{
|
||||
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
|
||||
if ((rc = WebSocket_connect(&m->c->net, m->ssl, serverURI)) == SOCKET_ERROR )
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = MQTTCLIENT_SUCCESS;
|
||||
m->c->connect_state = WAIT_FOR_CONNACK;
|
||||
if (MQTTPacket_send_connect(m->c, m->connect.details.conn.MQTTVersion,
|
||||
m->connectProps, m->willProps) == SOCKET_ERROR)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (!m->c->cleansession && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
if (m->c->net.http_proxy) {
|
||||
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
|
||||
if ((rc = Proxy_connect( &m->c->net, 0, serverURI)) == SOCKET_ERROR )
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ( m->websocket )
|
||||
{
|
||||
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
|
||||
if ((rc = WebSocket_connect(&m->c->net, 0, serverURI)) == SOCKET_ERROR )
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->connect_state = WAIT_FOR_CONNACK; /* TCP/SSL connect completed, in which case send the MQTT connect packet */
|
||||
if ((rc = MQTTPacket_send_connect(m->c, m->connect.details.conn.MQTTVersion,
|
||||
m->connectProps, m->willProps)) == SOCKET_ERROR)
|
||||
goto exit;
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (m->c->connect_state == SSL_IN_PROGRESS) /* SSL connect sent - wait for completion */
|
||||
{
|
||||
rc = m->c->sslopts->struct_version >= 3 ?
|
||||
SSLSocket_connect(m->c->net.ssl, m->c->net.socket, serverURI,
|
||||
m->c->sslopts->verify, m->c->sslopts->ssl_error_cb, m->c->sslopts->ssl_error_context) :
|
||||
SSLSocket_connect(m->c->net.ssl, m->c->net.socket, serverURI,
|
||||
m->c->sslopts->verify, NULL, NULL);
|
||||
if (rc != 1)
|
||||
goto exit;
|
||||
|
||||
if(!m->c->cleansession && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
|
||||
if ( m->websocket )
|
||||
{
|
||||
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
|
||||
if ((rc = WebSocket_connect(&m->c->net, 1, serverURI)) == SOCKET_ERROR )
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->connect_state = WAIT_FOR_CONNACK; /* SSL connect completed, in which case send the MQTT connect packet */
|
||||
if ((rc = MQTTPacket_send_connect(m->c, m->connect.details.conn.MQTTVersion,
|
||||
m->connectProps, m->willProps)) == SOCKET_ERROR)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (m->c->connect_state == WEBSOCKET_IN_PROGRESS) /* Websocket connect sent - wait for completion */
|
||||
{
|
||||
if ((rc = WebSocket_upgrade( &m->c->net ) ) == SOCKET_ERROR )
|
||||
goto exit;
|
||||
else if (rc != TCPSOCKET_INTERRUPTED)
|
||||
{
|
||||
m->c->connect_state = WAIT_FOR_CONNACK; /* Websocket upgrade completed, in which case send the MQTT connect packet */
|
||||
if ((rc = MQTTPacket_send_connect(m->c, m->connect.details.conn.MQTTVersion, m->connectProps, m->willProps)) == SOCKET_ERROR)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if ((rc != 0 && rc != TCPSOCKET_INTERRUPTED && (m->c->connect_state != SSL_IN_PROGRESS && m->c->connect_state != WEBSOCKET_IN_PROGRESS)) || (rc == SSL_FATAL))
|
||||
nextOrClose(m, MQTTASYNC_FAILURE, "TCP/TLS connect failure");
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static MQTTPacket* MQTTAsync_cycle(SOCKET* sock, unsigned long timeout, int* rc)
|
||||
{
|
||||
MQTTPacket* pack = NULL;
|
||||
int rc1 = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if defined(OPENSSL)
|
||||
if ((*sock = SSLSocket_getPendingRead()) == -1)
|
||||
{
|
||||
#endif
|
||||
/*int should_stop = 0;*/
|
||||
int interrupted = 0;
|
||||
|
||||
/* 0 from getReadySocket indicates no work to do, rc -1 == error */
|
||||
*sock = Socket_getReadySocket(0, (int)timeout, socket_mutex, &rc1, &interrupted);
|
||||
*rc = rc1;
|
||||
/*MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
should_stop = MQTTAsync_tostop;
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);*/
|
||||
/* Frank deleted this small delay on the suggestion of Claude.
|
||||
I want to preserve it as a comment in case we need it in the future.
|
||||
The preceeding 3 lines of code are only needed if the following line
|
||||
is reinstated - Ian
|
||||
if (!should_stop && *sock == 0 && (timeout > 0L) && (interrupted == 0))
|
||||
MQTTAsync_sleep(50L);*/
|
||||
#if defined(OPENSSL)
|
||||
}
|
||||
#endif
|
||||
MQTTAsync_lock_mutex(mqttasync_mutex);
|
||||
if (*sock > 0 && rc1 == 0)
|
||||
{
|
||||
MQTTAsyncs* m = NULL;
|
||||
if (ListFindItem(MQTTAsync_handles, sock, clientSockCompare) != NULL)
|
||||
m = (MQTTAsync)(MQTTAsync_handles->current->content);
|
||||
if (m != NULL)
|
||||
{
|
||||
Log(TRACE_MINIMUM, -1, "m->c->connect_state = %d", m->c->connect_state);
|
||||
if (m->c->connect_state == TCP_IN_PROGRESS || m->c->connect_state == SSL_IN_PROGRESS || m->c->connect_state == WEBSOCKET_IN_PROGRESS)
|
||||
*rc = MQTTAsync_connecting(m);
|
||||
else
|
||||
pack = MQTTPacket_Factory(m->c->MQTTVersion, &m->c->net, rc);
|
||||
if (m->c->connect_state == WAIT_FOR_CONNACK && *rc == SOCKET_ERROR)
|
||||
{
|
||||
Log(TRACE_MINIMUM, -1, "CONNECT sent but MQTTPacket_Factory has returned SOCKET_ERROR");
|
||||
nextOrClose(m, MQTTASYNC_FAILURE, "TCP connect completion failure");
|
||||
}
|
||||
}
|
||||
if (pack)
|
||||
{
|
||||
int freed = 1;
|
||||
|
||||
/* Note that these handle... functions free the packet structure that they are dealing with */
|
||||
if (pack->header.bits.type == PUBLISH)
|
||||
*rc = MQTTProtocol_handlePublishes(pack, *sock);
|
||||
else if (pack->header.bits.type == PUBACK || pack->header.bits.type == PUBCOMP ||
|
||||
pack->header.bits.type == PUBREC)
|
||||
{
|
||||
int msgid = 0,
|
||||
mqttversion = 0;
|
||||
unsigned int msgtype = 0,
|
||||
ackrc = 0;
|
||||
MQTTProperties msgprops = MQTTProperties_initializer;
|
||||
Publications* pubToRemove = NULL;
|
||||
|
||||
/* This block is so that the ack variable is local and isn't accidentally reused */
|
||||
{
|
||||
static Ack ack;
|
||||
ack = *(Ack*)pack;
|
||||
/* these values are stored because the packet structure is freed in the handle functions */
|
||||
msgid = ack.msgId;
|
||||
msgtype = pack->header.bits.type;
|
||||
if (ack.MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
ackrc = ack.rc;
|
||||
msgprops = MQTTProperties_copy(&ack.properties);
|
||||
mqttversion = ack.MQTTVersion;
|
||||
}
|
||||
}
|
||||
|
||||
if (msgtype == PUBCOMP)
|
||||
{
|
||||
*rc = MQTTProtocol_handlePubcomps(pack, *sock, &pubToRemove);
|
||||
if (sendThread_state != STOPPED)
|
||||
Thread_signal_evt(send_evt);
|
||||
}
|
||||
else if (msgtype == PUBREC)
|
||||
*rc = MQTTProtocol_handlePubrecs(pack, *sock, &pubToRemove);
|
||||
else if (msgtype == PUBACK)
|
||||
{
|
||||
*rc = MQTTProtocol_handlePubacks(pack, *sock, &pubToRemove);
|
||||
if (sendThread_state != STOPPED)
|
||||
Thread_signal_evt(send_evt);
|
||||
}
|
||||
if (!m)
|
||||
Log(LOG_ERROR, -1, "PUBCOMP, PUBACK or PUBREC received for no client, msgid %d", msgid);
|
||||
if (m && *rc == 0 && (msgtype != PUBREC || ackrc >= MQTTREASONCODE_UNSPECIFIED_ERROR))
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
|
||||
if (m->dc)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling deliveryComplete for client %s, msgid %d", m->c->clientID, msgid);
|
||||
(*(m->dc))(m->dcContext, msgid);
|
||||
}
|
||||
/* use the msgid to find the callback to be called */
|
||||
while (ListNextElement(m->responses, ¤t))
|
||||
{
|
||||
MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
|
||||
if (command->command.token == msgid)
|
||||
{
|
||||
if (!ListDetach(m->responses, command)) /* then remove the response from the list */
|
||||
Log(LOG_ERROR, -1, "Publish command not removed from command list");
|
||||
if (command->command.onSuccess)
|
||||
{
|
||||
MQTTAsync_successData data;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.alt.pub.destinationName = command->command.details.pub.destinationName;
|
||||
data.alt.pub.message.payload = command->command.details.pub.payload;
|
||||
data.alt.pub.message.payloadlen = command->command.details.pub.payloadlen;
|
||||
data.alt.pub.message.qos = command->command.details.pub.qos;
|
||||
data.alt.pub.message.retained = command->command.details.pub.retained;
|
||||
Log(TRACE_MIN, -1, "Calling publish success for client %s", m->c->clientID);
|
||||
(*(command->command.onSuccess))(command->command.context, &data);
|
||||
}
|
||||
else if (command->command.onSuccess5 && ackrc < MQTTREASONCODE_UNSPECIFIED_ERROR)
|
||||
{
|
||||
MQTTAsync_successData5 data = MQTTAsync_successData5_initializer;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.alt.pub.destinationName = command->command.details.pub.destinationName;
|
||||
data.alt.pub.message.payload = command->command.details.pub.payload;
|
||||
data.alt.pub.message.payloadlen = command->command.details.pub.payloadlen;
|
||||
data.alt.pub.message.qos = command->command.details.pub.qos;
|
||||
data.alt.pub.message.retained = command->command.details.pub.retained;
|
||||
data.properties = command->command.properties;
|
||||
Log(TRACE_MIN, -1, "Calling publish success for client %s", m->c->clientID);
|
||||
(*(command->command.onSuccess5))(command->command.context, &data);
|
||||
}
|
||||
else if (command->command.onFailure5 && ackrc >= MQTTREASONCODE_UNSPECIFIED_ERROR)
|
||||
{
|
||||
MQTTAsync_failureData5 data = MQTTAsync_failureData5_initializer;
|
||||
|
||||
data.token = command->command.token;
|
||||
data.reasonCode = ackrc;
|
||||
data.properties = msgprops;
|
||||
data.packet_type = msgtype;
|
||||
Log(TRACE_MIN, -1, "Calling publish failure for client %s", m->c->clientID);
|
||||
(*(command->command.onFailure5))(command->command.context, &data);
|
||||
}
|
||||
if (pubToRemove != NULL)
|
||||
{
|
||||
MQTTProtocol_removePublication(pubToRemove);
|
||||
pubToRemove = NULL;
|
||||
/* removePublication has freed the topic and payload memory, so here we indicate that
|
||||
* so freeCommand doesn't try to free them again.
|
||||
*/
|
||||
command->command.details.pub.destinationName = NULL;
|
||||
command->command.details.pub.payload = NULL;
|
||||
}
|
||||
MQTTAsync_freeCommand(command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mqttversion >= MQTTVERSION_5)
|
||||
MQTTProperties_free(&msgprops);
|
||||
}
|
||||
if (pubToRemove != NULL)
|
||||
MQTTProtocol_removePublication(pubToRemove);
|
||||
}
|
||||
else if (pack->header.bits.type == PUBREL)
|
||||
*rc = MQTTProtocol_handlePubrels(pack, *sock);
|
||||
else if (pack->header.bits.type == PINGRESP)
|
||||
*rc = MQTTProtocol_handlePingresps(pack, *sock);
|
||||
else
|
||||
freed = 0;
|
||||
if (freed)
|
||||
pack = NULL;
|
||||
}
|
||||
}
|
||||
MQTTAsync_retry();
|
||||
MQTTAsync_unlock_mutex(mqttasync_mutex);
|
||||
FUNC_EXIT_RC(*rc);
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
int MQTTAsync_getNoBufferedMessages(MQTTAsyncs* m)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
MQTTAsync_lock_mutex(mqttcommand_mutex);
|
||||
count = m->noBufferedMessages;
|
||||
MQTTAsync_unlock_mutex(mqttcommand_mutex);
|
||||
return count;
|
||||
}
|
||||
188
3rd/paho.mqtt.c/src/MQTTAsyncUtils.h
Normal file
188
3rd/paho.mqtt.c/src/MQTTAsyncUtils.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2024 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial implementation and documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTASYNCUTILS_H_)
|
||||
#define MQTTASYNCUTILS_H_
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#define URI_TCP "tcp://"
|
||||
#define URI_MQTT "mqtt://"
|
||||
#define URI_WS "ws://"
|
||||
#define URI_WSS "wss://"
|
||||
#define URI_UNIX "unix://"
|
||||
|
||||
enum MQTTAsync_threadStates
|
||||
{
|
||||
STOPPED, STARTING, RUNNING, STOPPING
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MQTTAsync_message* msg;
|
||||
char* topicName;
|
||||
int topicLen;
|
||||
unsigned int seqno; /* only used on restore */
|
||||
} qEntry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int type;
|
||||
MQTTAsync_onSuccess* onSuccess;
|
||||
MQTTAsync_onFailure* onFailure;
|
||||
MQTTAsync_onSuccess5* onSuccess5;
|
||||
MQTTAsync_onFailure5* onFailure5;
|
||||
MQTTAsync_token token;
|
||||
void* context;
|
||||
START_TIME_TYPE start_time;
|
||||
MQTTProperties properties;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
int count;
|
||||
char** topics;
|
||||
int* qoss;
|
||||
MQTTSubscribe_options opts;
|
||||
MQTTSubscribe_options* optlist;
|
||||
} sub;
|
||||
struct
|
||||
{
|
||||
int count;
|
||||
char** topics;
|
||||
} unsub;
|
||||
struct
|
||||
{
|
||||
char* destinationName;
|
||||
int payloadlen;
|
||||
void* payload;
|
||||
int qos;
|
||||
int retained;
|
||||
} pub;
|
||||
struct
|
||||
{
|
||||
int internal;
|
||||
int timeout;
|
||||
enum MQTTReasonCodes reasonCode;
|
||||
} dis;
|
||||
struct
|
||||
{
|
||||
int currentURI;
|
||||
int MQTTVersion; /**< current MQTT version being used to connect */
|
||||
} conn;
|
||||
} details;
|
||||
} MQTTAsync_command;
|
||||
|
||||
typedef struct MQTTAsync_struct
|
||||
{
|
||||
char* serverURI;
|
||||
int unixsock;
|
||||
int ssl;
|
||||
int websocket;
|
||||
Clients* c;
|
||||
|
||||
/* "Global", to the client, callback definitions */
|
||||
MQTTAsync_connectionLost* cl;
|
||||
MQTTAsync_messageArrived* ma;
|
||||
MQTTAsync_deliveryComplete* dc;
|
||||
void* clContext; /* the context to be associated with the conn lost callback*/
|
||||
void* maContext; /* the context to be associated with the msg arrived callback*/
|
||||
void* dcContext; /* the context to be associated with the deliv complete callback*/
|
||||
|
||||
MQTTAsync_connected* connected;
|
||||
void* connected_context; /* the context to be associated with the connected callback*/
|
||||
|
||||
MQTTAsync_disconnected* disconnected;
|
||||
void* disconnected_context; /* the context to be associated with the disconnected callback*/
|
||||
|
||||
MQTTAsync_updateConnectOptions* updateConnectOptions;
|
||||
void* updateConnectOptions_context;
|
||||
|
||||
/* Each time connect is called, we store the options that were used. These are reused in
|
||||
any call to reconnect, or an automatic reconnect attempt */
|
||||
MQTTAsync_command connect; /* Connect operation properties */
|
||||
MQTTAsync_command disconnect; /* Disconnect operation properties */
|
||||
MQTTAsync_command* pending_write; /* Is there a socket write pending? */
|
||||
|
||||
List* responses;
|
||||
unsigned int command_seqno;
|
||||
|
||||
MQTTPacket* pack;
|
||||
|
||||
/* added for offline buffering */
|
||||
MQTTAsync_createOptions* createOptions;
|
||||
int shouldBeConnected;
|
||||
int noBufferedMessages; /* the current number of buffered (publish) messages for this client */
|
||||
|
||||
/* added for automatic reconnect */
|
||||
int automaticReconnect;
|
||||
int minRetryInterval;
|
||||
int maxRetryInterval;
|
||||
int serverURIcount;
|
||||
char** serverURIs;
|
||||
int connectTimeout;
|
||||
|
||||
int currentInterval;
|
||||
int currentIntervalBase;
|
||||
START_TIME_TYPE lastConnectionFailedTime;
|
||||
int retrying;
|
||||
int reconnectNow;
|
||||
|
||||
/* MQTT V5 properties */
|
||||
MQTTProperties* connectProps;
|
||||
MQTTProperties* willProps;
|
||||
|
||||
} MQTTAsyncs;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MQTTAsync_command command;
|
||||
MQTTAsyncs* client;
|
||||
unsigned int seqno; /* only used on restore */
|
||||
int not_restored;
|
||||
char* key; /* if not_restored, this holds the key */
|
||||
} MQTTAsync_queuedCommand;
|
||||
|
||||
void MQTTAsync_lock_mutex(mutex_type amutex);
|
||||
void MQTTAsync_unlock_mutex(mutex_type amutex);
|
||||
void MQTTAsync_terminate(void);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
int MQTTAsync_restoreCommands(MQTTAsyncs* client);
|
||||
#endif
|
||||
int MQTTAsync_addCommand(MQTTAsync_queuedCommand* command, int command_size);
|
||||
void MQTTAsync_emptyMessageQueue(Clients* client);
|
||||
void MQTTAsync_freeResponses(MQTTAsyncs* m);
|
||||
void MQTTAsync_freeCommands(MQTTAsyncs* m);
|
||||
int MQTTAsync_unpersistCommandsAndMessages(Clients* c);
|
||||
void MQTTAsync_closeSession(Clients* client, enum MQTTReasonCodes reasonCode, MQTTProperties* props);
|
||||
int MQTTAsync_disconnect1(MQTTAsync handle, const MQTTAsync_disconnectOptions* options, int internal);
|
||||
int MQTTAsync_assignMsgId(MQTTAsyncs* m);
|
||||
int MQTTAsync_getNoBufferedMessages(MQTTAsyncs* m);
|
||||
void MQTTAsync_writeContinue(SOCKET socket);
|
||||
void MQTTAsync_writeComplete(SOCKET socket, int rc);
|
||||
void setRetryLoopInterval(int keepalive);
|
||||
void MQTTAsync_NULLPublishResponses(MQTTAsyncs* m);
|
||||
|
||||
#if defined(_WIN32)
|
||||
#else
|
||||
#define WINAPI
|
||||
#endif
|
||||
|
||||
thread_return_type WINAPI MQTTAsync_sendThread(void* n);
|
||||
thread_return_type WINAPI MQTTAsync_receiveThread(void* n);
|
||||
|
||||
#endif /* MQTTASYNCUTILS_H_ */
|
||||
3174
3rd/paho.mqtt.c/src/MQTTClient.c
Normal file
3174
3rd/paho.mqtt.c/src/MQTTClient.c
Normal file
@@ -0,0 +1,3174 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2026 IBM Corp., Ian Craggs and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - bug 384016 - segv setting will message
|
||||
* Ian Craggs - bug 384053 - v1.0.0.7 - stop MQTTClient_receive on socket error
|
||||
* Ian Craggs, Allan Stockdill-Mander - add ability to connect with SSL
|
||||
* Ian Craggs - multiple server connection support
|
||||
* Ian Craggs - fix for bug 413429 - connectionLost not called
|
||||
* Ian Craggs - fix for bug 421103 - trying to write to same socket, in publish/retries
|
||||
* Ian Craggs - fix for bug 419233 - mutexes not reporting errors
|
||||
* Ian Craggs - fix for bug 420851
|
||||
* Ian Craggs - fix for bug 432903 - queue persistence
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - fix for bug 438176 - MQTT version selection
|
||||
* Rong Xiang, Ian Craggs - C++ compatibility
|
||||
* Ian Craggs - fix for bug 443724 - stack corruption
|
||||
* Ian Craggs - fix for bug 447672 - simultaneous access to socket structure
|
||||
* Ian Craggs - fix for bug 459791 - deadlock in WaitForCompletion for bad client
|
||||
* Ian Craggs - fix for bug 474905 - insufficient synchronization for subscribe, unsubscribe, connect
|
||||
* Ian Craggs - make it clear that yield and receive are not intended for multi-threaded mode (bug 474748)
|
||||
* Ian Craggs - SNI support, message queue unpersist bug
|
||||
* Ian Craggs - binary will message support
|
||||
* Ian Craggs - waitforCompletion fix #240
|
||||
* Ian Craggs - check for NULL SSL options #334
|
||||
* Ian Craggs - allocate username/password buffers #431
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
* Sven Gambel - add generic proxy support
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief Synchronous API implementation
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "MQTTClient.h"
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
#include "MQTTPersistence.h"
|
||||
#endif
|
||||
|
||||
#include "utf-8.h"
|
||||
#include "MQTTProtocol.h"
|
||||
#include "MQTTProtocolOut.h"
|
||||
#include "Thread.h"
|
||||
#include "SocketBuffer.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Heap.h"
|
||||
|
||||
#if defined(OPENSSL)
|
||||
#include <openssl/ssl.h>
|
||||
#else
|
||||
#define URI_SSL "ssl://"
|
||||
#define URI_TLS "tls://"
|
||||
#define URI_MQTTS "mqtts://"
|
||||
#endif
|
||||
|
||||
#include "OsWrapper.h"
|
||||
|
||||
#define URI_TCP "tcp://"
|
||||
#define URI_MQTT "mqtt://"
|
||||
#define URI_WS "ws://"
|
||||
#define URI_WSS "wss://"
|
||||
#define URI_UNIX "unix://"
|
||||
|
||||
#include "VersionInfo.h"
|
||||
#include "WebSocket.h"
|
||||
#include "Proxy.h"
|
||||
|
||||
const char *client_timestamp_eye = "MQTTClientV3_Timestamp " BUILD_TIMESTAMP;
|
||||
const char *client_version_eye = "MQTTClientV3_Version " CLIENT_VERSION;
|
||||
|
||||
struct conlost_sync_data {
|
||||
evt_type evt;
|
||||
void *m;
|
||||
};
|
||||
|
||||
int MQTTClient_init(void);
|
||||
|
||||
void MQTTClient_global_init(MQTTClient_init_options* inits)
|
||||
{
|
||||
MQTTClient_init();
|
||||
#if defined(OPENSSL)
|
||||
SSLSocket_handleOpensslInit(inits->do_openssl_init);
|
||||
#endif
|
||||
}
|
||||
|
||||
static ClientStates ClientState =
|
||||
{
|
||||
CLIENT_VERSION, /* version */
|
||||
NULL /* client list */
|
||||
};
|
||||
|
||||
ClientStates* bstate = &ClientState;
|
||||
|
||||
MQTTProtocol state;
|
||||
|
||||
#if defined(_WIN32)
|
||||
static mutex_type mqttclient_mutex = NULL;
|
||||
mutex_type socket_mutex = NULL;
|
||||
static mutex_type subscribe_mutex = NULL;
|
||||
static mutex_type connect_mutex = NULL;
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
extern mutex_type stack_mutex;
|
||||
extern mutex_type heap_mutex;
|
||||
#endif
|
||||
extern mutex_type log_mutex;
|
||||
|
||||
int MQTTClient_init(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (mqttclient_mutex == NULL)
|
||||
{
|
||||
mqttclient_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("mqttclient_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
subscribe_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("subscribe_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
connect_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("connect_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
stack_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("stack_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
heap_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("heap_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
log_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("log_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
socket_mutex = Paho_thread_create_mutex(&rc);
|
||||
if (rc != 0)
|
||||
{
|
||||
printf("socket_mutex error %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void MQTTClient_cleanup(void)
|
||||
{
|
||||
if (connect_mutex)
|
||||
Paho_thread_destroy_mutex(connect_mutex);
|
||||
if (subscribe_mutex)
|
||||
Paho_thread_destroy_mutex(subscribe_mutex);
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
if (stack_mutex)
|
||||
Paho_thread_destroy_mutex(stack_mutex);
|
||||
if (heap_mutex)
|
||||
Paho_thread_destroy_mutex(heap_mutex);
|
||||
#endif
|
||||
if (log_mutex)
|
||||
Paho_thread_destroy_mutex(log_mutex);
|
||||
if (socket_mutex)
|
||||
Paho_thread_destroy_mutex(socket_mutex);
|
||||
if (mqttclient_mutex)
|
||||
Paho_thread_destroy_mutex(mqttclient_mutex);
|
||||
}
|
||||
|
||||
#if defined(PAHO_MQTT_STATIC)
|
||||
/* Global variable for one-time initialization structure */
|
||||
static INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT; /* Static initialization */
|
||||
|
||||
/* One time initialization function */
|
||||
BOOL CALLBACK InitOnceFunction (
|
||||
PINIT_ONCE InitOnce, /* Pointer to one-time initialization structure */
|
||||
PVOID Parameter, /* Optional parameter passed by InitOnceExecuteOnce */
|
||||
PVOID *lpContext) /* Receives pointer to event object */
|
||||
{
|
||||
int rc = MQTTClient_init();
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
#else
|
||||
BOOL APIENTRY DllMain(HANDLE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
MQTTClient_init();
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
break;
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (lpReserved)
|
||||
MQTTClient_cleanup();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
static pthread_mutex_t mqttclient_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type mqttclient_mutex = &mqttclient_mutex_store;
|
||||
|
||||
static pthread_mutex_t socket_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
mutex_type socket_mutex = &socket_mutex_store;
|
||||
|
||||
static pthread_mutex_t subscribe_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type subscribe_mutex = &subscribe_mutex_store;
|
||||
|
||||
static pthread_mutex_t connect_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type connect_mutex = &connect_mutex_store;
|
||||
|
||||
int MQTTClient_init(void)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
int rc;
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
#if !defined(_WRS_KERNEL)
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
|
||||
#else
|
||||
/* #warning "no pthread_mutexattr_settype" */
|
||||
#endif /* !defined(_WRS_KERNEL) */
|
||||
if ((rc = pthread_mutex_init(mqttclient_mutex, &attr)) != 0)
|
||||
printf("MQTTClient: error %d initializing client_mutex\n", rc);
|
||||
else if ((rc = pthread_mutex_init(socket_mutex, &attr)) != 0)
|
||||
printf("MQTTClient: error %d initializing socket_mutex\n", rc);
|
||||
else if ((rc = pthread_mutex_init(subscribe_mutex, &attr)) != 0)
|
||||
printf("MQTTClient: error %d initializing subscribe_mutex\n", rc);
|
||||
else if ((rc = pthread_mutex_init(connect_mutex, &attr)) != 0)
|
||||
printf("MQTTClient: error %d initializing connect_mutex\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define WINAPI
|
||||
#endif
|
||||
|
||||
static volatile int library_initialized = 0;
|
||||
static List* handles = NULL;
|
||||
static int running = 0;
|
||||
static int tostop = 0;
|
||||
static thread_id_type run_id = 0;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MQTTClient_message* msg;
|
||||
char* topicName;
|
||||
int topicLen;
|
||||
unsigned int seqno; /* only used on restore */
|
||||
} qEntry;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* serverURI;
|
||||
const char* currentServerURI; /* when using HA options, set the currently used serverURI */
|
||||
int unixsock;
|
||||
#if defined(OPENSSL)
|
||||
int ssl;
|
||||
#endif
|
||||
int websocket;
|
||||
Clients* c;
|
||||
MQTTClient_connectionLost* cl;
|
||||
MQTTClient_messageArrived* ma;
|
||||
MQTTClient_deliveryComplete* dc;
|
||||
void* context;
|
||||
|
||||
MQTTClient_disconnected* disconnected;
|
||||
void* disconnected_context; /* the context to be associated with the disconnected callback*/
|
||||
|
||||
MQTTClient_published* published;
|
||||
void* published_context; /* the context to be associated with the disconnected callback*/
|
||||
|
||||
#if 0
|
||||
MQTTClient_authHandle* auth_handle;
|
||||
void* auth_handle_context; /* the context to be associated with the authHandle callback*/
|
||||
#endif
|
||||
|
||||
int rc; /* getsockopt return code in connect */
|
||||
evt_type connect_evt;
|
||||
evt_type connack_evt;
|
||||
evt_type suback_evt;
|
||||
evt_type unsuback_evt;
|
||||
MQTTPacket* pack;
|
||||
|
||||
unsigned long commandTimeout;
|
||||
} MQTTClients;
|
||||
|
||||
struct props_rc_parms
|
||||
{
|
||||
MQTTClients* m;
|
||||
MQTTProperties* properties;
|
||||
enum MQTTReasonCodes reasonCode;
|
||||
};
|
||||
|
||||
static void MQTTClient_terminate(void);
|
||||
static void MQTTClient_emptyMessageQueue(Clients* client);
|
||||
static int MQTTClient_deliverMessage(
|
||||
int rc, MQTTClients* m,
|
||||
char** topicName, int* topicLen,
|
||||
MQTTClient_message** message);
|
||||
static int clientSockCompare(void* a, void* b);
|
||||
static thread_return_type WINAPI connectionLost_call(void* context);
|
||||
static thread_return_type WINAPI MQTTClient_run(void* n);
|
||||
static int MQTTClient_stop(void);
|
||||
static void MQTTClient_closeSession(Clients* client, enum MQTTReasonCodes reason, MQTTProperties* props);
|
||||
static int MQTTClient_cleanSession(Clients* client);
|
||||
static MQTTResponse MQTTClient_connectURIVersion(
|
||||
MQTTClient handle, MQTTClient_connectOptions* options,
|
||||
const char* serverURI, int MQTTVersion,
|
||||
START_TIME_TYPE start, ELAPSED_TIME_TYPE millisecsTimeout,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties);
|
||||
static MQTTResponse MQTTClient_connectURI(MQTTClient handle, MQTTClient_connectOptions* options, const char* serverURI,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties);
|
||||
static int MQTTClient_disconnect1(MQTTClient handle, int timeout, int internal, int stop, enum MQTTReasonCodes, MQTTProperties*);
|
||||
static int MQTTClient_disconnect_internal(MQTTClient handle, int timeout);
|
||||
static void MQTTClient_retry(void);
|
||||
static MQTTPacket* MQTTClient_cycle(SOCKET* sock, ELAPSED_TIME_TYPE timeout, int* rc);
|
||||
static MQTTPacket* MQTTClient_waitfor(MQTTClient handle, int packet_type, int* rc, int64_t timeout);
|
||||
/*static int pubCompare(void* a, void* b); */
|
||||
static void MQTTProtocol_checkPendingWrites(void);
|
||||
static void MQTTClient_writeComplete(SOCKET socket, int rc);
|
||||
static void MQTTClient_writeContinue(SOCKET socket);
|
||||
|
||||
|
||||
int MQTTClient_createWithOptions(MQTTClient* handle, const char* serverURI, const char* clientId,
|
||||
int persistence_type, void* persistence_context, MQTTClient_createOptions* options)
|
||||
{
|
||||
int rc = 0;
|
||||
MQTTClients *m = NULL;
|
||||
|
||||
#if (defined(_WIN32)) && defined(PAHO_MQTT_STATIC)
|
||||
/* intializes mutexes once. Must come before FUNC_ENTRY */
|
||||
BOOL bStatus = InitOnceExecuteOnce(&g_InitOnce, InitOnceFunction, NULL, NULL);
|
||||
#endif
|
||||
FUNC_ENTRY;
|
||||
if ((rc = Paho_thread_lock_mutex(mqttclient_mutex)) != 0)
|
||||
goto nounlock_exit;
|
||||
|
||||
if (serverURI == NULL || clientId == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!UTF8_validateString(clientId))
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_UTF8_STRING;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strlen(clientId) == 0 && persistence_type == MQTTCLIENT_PERSISTENCE_DEFAULT)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strstr(serverURI, "://") != NULL)
|
||||
{
|
||||
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) != 0
|
||||
&& strncmp(URI_MQTT, serverURI, strlen(URI_MQTT)) != 0
|
||||
&& strncmp(URI_WS, serverURI, strlen(URI_WS)) != 0
|
||||
#if defined(OPENSSL)
|
||||
&& strncmp(URI_SSL, serverURI, strlen(URI_SSL)) != 0
|
||||
&& strncmp(URI_TLS, serverURI, strlen(URI_TLS)) != 0
|
||||
&& strncmp(URI_MQTTS, serverURI, strlen(URI_MQTTS)) != 0
|
||||
&& strncmp(URI_WSS, serverURI, strlen(URI_WSS)) != 0
|
||||
#endif
|
||||
#if defined(UNIXSOCK)
|
||||
&& strncmp(URI_UNIX, serverURI, strlen(URI_UNIX)) != 0
|
||||
#endif
|
||||
)
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_PROTOCOL;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (options && (strncmp(options->struct_id, "MQCO", 4) != 0 || options->struct_version != 0))
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!library_initialized)
|
||||
{
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
Heap_initialize();
|
||||
#endif
|
||||
Log_initialize((Log_nameValue*)MQTTClient_getVersionInfo());
|
||||
bstate->clients = ListInitialize();
|
||||
Socket_outInitialize();
|
||||
Socket_setWriteCompleteCallback(MQTTClient_writeComplete);
|
||||
Socket_setWriteContinueCallback(MQTTClient_writeContinue);
|
||||
Socket_setWriteAvailableCallback(MQTTProtocol_writeAvailable);
|
||||
handles = ListInitialize();
|
||||
#if defined(OPENSSL)
|
||||
SSLSocket_initialize();
|
||||
#endif
|
||||
library_initialized = 1;
|
||||
}
|
||||
|
||||
if ((m = malloc(sizeof(MQTTClients))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
*handle = m;
|
||||
memset(m, '\0', sizeof(MQTTClients));
|
||||
m->commandTimeout = 10000L;
|
||||
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
|
||||
serverURI += strlen(URI_TCP);
|
||||
else if (strncmp(URI_MQTT, serverURI, strlen(URI_MQTT)) == 0)
|
||||
serverURI += strlen(URI_MQTT);
|
||||
else if (strncmp(URI_WS, serverURI, strlen(URI_WS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WS);
|
||||
m->websocket = 1;
|
||||
}
|
||||
else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
|
||||
{
|
||||
#if defined(OPENSSL)
|
||||
serverURI += strlen(URI_SSL);
|
||||
m->ssl = 1;
|
||||
#else
|
||||
rc = MQTTCLIENT_SSL_NOT_SUPPORTED;
|
||||
goto exit;
|
||||
#endif
|
||||
}
|
||||
else if (strncmp(URI_TLS, serverURI, strlen(URI_TLS)) == 0)
|
||||
{
|
||||
#if defined(OPENSSL)
|
||||
serverURI += strlen(URI_TLS);
|
||||
m->ssl = 1;
|
||||
#else
|
||||
rc = MQTTCLIENT_SSL_NOT_SUPPORTED;
|
||||
goto exit;
|
||||
#endif
|
||||
}
|
||||
else if (strncmp(URI_MQTTS, serverURI, strlen(URI_MQTTS)) == 0)
|
||||
{
|
||||
#if defined(OPENSSL)
|
||||
serverURI += strlen(URI_MQTTS);
|
||||
m->ssl = 1;
|
||||
#else
|
||||
rc = MQTTCLIENT_SSL_NOT_SUPPORTED;
|
||||
goto exit;
|
||||
#endif
|
||||
}
|
||||
else if (strncmp(URI_WSS, serverURI, strlen(URI_WSS)) == 0)
|
||||
{
|
||||
#if defined(OPENSSL)
|
||||
serverURI += strlen(URI_WSS);
|
||||
m->ssl = 1;
|
||||
m->websocket = 1;
|
||||
#else
|
||||
rc = MQTTCLIENT_SSL_NOT_SUPPORTED;
|
||||
goto exit;
|
||||
#endif
|
||||
}
|
||||
#if defined(UNIXSOCK)
|
||||
else if (strncmp(URI_UNIX, serverURI, strlen(URI_UNIX)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_UNIX);
|
||||
m->unixsock = 1;
|
||||
}
|
||||
#endif
|
||||
m->serverURI = MQTTStrdup(serverURI);
|
||||
ListAppend(handles, m, sizeof(MQTTClients));
|
||||
|
||||
if ((m->c = malloc(sizeof(Clients))) == NULL)
|
||||
{
|
||||
ListRemove(handles, m);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(m->c, '\0', sizeof(Clients));
|
||||
m->c->context = m;
|
||||
m->c->MQTTVersion = (options) ? options->MQTTVersion : MQTTVERSION_DEFAULT;
|
||||
m->c->outboundMsgs = ListInitialize();
|
||||
m->c->inboundMsgs = ListInitialize();
|
||||
m->c->messageQueue = ListInitialize();
|
||||
m->c->outboundQueue = ListInitialize();
|
||||
m->c->clientID = MQTTStrdup(clientId);
|
||||
// TODO (fmp): None of these events are being checked for failure
|
||||
m->connect_evt = Thread_create_evt(&rc);
|
||||
m->connack_evt = Thread_create_evt(&rc);
|
||||
m->suback_evt = Thread_create_evt(&rc);
|
||||
m->unsuback_evt = Thread_create_evt(&rc);
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
rc = MQTTPersistence_create(&(m->c->persistence), persistence_type, persistence_context);
|
||||
if (rc == 0)
|
||||
{
|
||||
rc = MQTTPersistence_initialize(m->c, m->serverURI);
|
||||
if (rc == 0)
|
||||
MQTTPersistence_restoreMessageQueue(m->c);
|
||||
}
|
||||
#endif
|
||||
ListAppend(bstate->clients, m->c, sizeof(Clients) + 3*sizeof(List));
|
||||
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
nounlock_exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_create(MQTTClient* handle, const char* serverURI, const char* clientId,
|
||||
int persistence_type, void* persistence_context)
|
||||
{
|
||||
return MQTTClient_createWithOptions(handle, serverURI, clientId, persistence_type,
|
||||
persistence_context, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void MQTTClient_terminate(void)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
MQTTClient_stop();
|
||||
if (library_initialized)
|
||||
{
|
||||
ListFree(bstate->clients);
|
||||
ListFree(handles);
|
||||
handles = NULL;
|
||||
WebSocket_terminate();
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
Heap_terminate();
|
||||
#endif
|
||||
Log_terminate();
|
||||
library_initialized = 0;
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTClient_emptyMessageQueue(Clients* client)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
/* empty message queue */
|
||||
if (client->messageQueue->count > 0)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
while (ListNextElement(client->messageQueue, ¤t))
|
||||
{
|
||||
qEntry* qe = (qEntry*)(current->content);
|
||||
free(qe->topicName);
|
||||
MQTTProperties_free(&qe->msg->properties);
|
||||
free(qe->msg->payload);
|
||||
free(qe->msg);
|
||||
}
|
||||
ListEmpty(client->messageQueue);
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void MQTTClient_destroy(MQTTClient* handle)
|
||||
{
|
||||
MQTTClients* m = *handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(connect_mutex);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
if (m == NULL)
|
||||
goto exit;
|
||||
|
||||
if (m->c)
|
||||
{
|
||||
SOCKET saved_socket = m->c->net.socket;
|
||||
char* saved_clientid = MQTTStrdup(m->c->clientID);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
MQTTPersistence_close(m->c);
|
||||
#endif
|
||||
MQTTClient_emptyMessageQueue(m->c);
|
||||
MQTTProtocol_freeClient(m->c);
|
||||
if (!ListRemove(bstate->clients, m->c))
|
||||
Log(LOG_ERROR, 0, NULL);
|
||||
else
|
||||
Log(TRACE_MIN, 1, NULL, saved_clientid, saved_socket);
|
||||
free(saved_clientid);
|
||||
}
|
||||
if (m->serverURI)
|
||||
free(m->serverURI);
|
||||
Thread_destroy_evt(m->connect_evt);
|
||||
Thread_destroy_evt(m->connack_evt);
|
||||
Thread_destroy_evt(m->suback_evt);
|
||||
Thread_destroy_evt(m->unsuback_evt);
|
||||
if (!ListRemove(handles, m))
|
||||
Log(LOG_ERROR, -1, "free error");
|
||||
*handle = NULL;
|
||||
if (bstate->clients->count == 0)
|
||||
MQTTClient_terminate();
|
||||
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
Paho_thread_unlock_mutex(connect_mutex);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void MQTTClient_freeMessage(MQTTClient_message** message)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
MQTTProperties_free(&(*message)->properties);
|
||||
free((*message)->payload);
|
||||
free(*message);
|
||||
*message = NULL;
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void MQTTClient_free(void* memory)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
free(memory);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
void* MQTTClient_malloc(size_t size)
|
||||
{
|
||||
void* val;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
val = malloc(size);
|
||||
rc = (val != NULL);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void MQTTResponse_free(MQTTResponse response)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (response.reasonCodeCount > 0 && response.reasonCodes)
|
||||
free(response.reasonCodes);
|
||||
if (response.properties)
|
||||
{
|
||||
MQTTProperties_free(response.properties);
|
||||
free(response.properties);
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTClient_deliverMessage(int rc, MQTTClients* m, char** topicName, int* topicLen, MQTTClient_message** message)
|
||||
{
|
||||
qEntry* qe = (qEntry*)(m->c->messageQueue->first->content);
|
||||
|
||||
FUNC_ENTRY;
|
||||
*message = qe->msg;
|
||||
*topicName = qe->topicName;
|
||||
*topicLen = qe->topicLen;
|
||||
if (strlen(*topicName) != *topicLen)
|
||||
rc = MQTTCLIENT_TOPICNAME_TRUNCATED;
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (m->c->persistence)
|
||||
MQTTPersistence_unpersistQueueEntry(m->c, (MQTTPersistence_qEntry*)qe);
|
||||
#endif
|
||||
ListRemove(m->c->messageQueue, m->c->messageQueue->first->content);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing clients by socket
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
static int clientSockCompare(void* a, void* b)
|
||||
{
|
||||
MQTTClients* m = (MQTTClients*)a;
|
||||
return m->c->net.socket == *(int*)b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper function to call connection lost on a separate thread. A separate thread is needed to allow the
|
||||
* connectionLost function to make API calls (e.g. connect)
|
||||
* @param context a pointer to the relevant client
|
||||
* @return thread_return_type standard thread return value - not used here
|
||||
*/
|
||||
static thread_return_type WINAPI connectionLost_call(void* context)
|
||||
{
|
||||
struct conlost_sync_data *data = (struct conlost_sync_data *)context;
|
||||
MQTTClients* m = (MQTTClients *)data->m;
|
||||
|
||||
(*(m->cl))(m->context, NULL);
|
||||
|
||||
Thread_signal_evt(data->evt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_setDisconnected(MQTTClient handle, void* context, MQTTClient_disconnected* disconnected)
|
||||
{
|
||||
int rc = MQTTCLIENT_SUCCESS;
|
||||
MQTTClients* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
if (m == NULL || m->c->connect_state != NOT_IN_PROGRESS)
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
else
|
||||
{
|
||||
m->disconnected_context = context;
|
||||
m->disconnected = disconnected;
|
||||
}
|
||||
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper function to call disconnected on a separate thread. A separate thread is needed to allow the
|
||||
* disconnected function to make API calls (e.g. connect)
|
||||
* @param context a pointer to the relevant client
|
||||
* @return thread_return_type standard thread return value - not used here
|
||||
*/
|
||||
static thread_return_type WINAPI call_disconnected(void* context)
|
||||
{
|
||||
struct props_rc_parms* pr = (struct props_rc_parms*)context;
|
||||
|
||||
(*(pr->m->disconnected))(pr->m->disconnected_context, pr->properties, pr->reasonCode);
|
||||
MQTTProperties_free(pr->properties);
|
||||
free(pr->properties);
|
||||
free(pr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_setPublished(MQTTClient handle, void* context, MQTTClient_published* published)
|
||||
{
|
||||
int rc = MQTTCLIENT_SUCCESS;
|
||||
MQTTClients* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
if (m == NULL || m->c->connect_state != NOT_IN_PROGRESS)
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
else
|
||||
{
|
||||
m->published_context = context;
|
||||
m->published = published;
|
||||
}
|
||||
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
int MQTTClient_setHandleAuth(MQTTClient handle, void* context, MQTTClient_handleAuth* auth_handle)
|
||||
{
|
||||
int rc = MQTTCLIENT_SUCCESS;
|
||||
MQTTClients* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
if (m == NULL || m->c->connect_state != NOT_IN_PROGRESS)
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
else
|
||||
{
|
||||
m->auth_handle_context = context;
|
||||
m->auth_handle = auth_handle;
|
||||
}
|
||||
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper function to call authHandle on a separate thread. A separate thread is needed to allow the
|
||||
* disconnected function to make API calls (e.g. MQTTClient_auth)
|
||||
* @param context a pointer to the relevant client
|
||||
* @return thread_return_type standard thread return value - not used here
|
||||
*/
|
||||
static thread_return_type WINAPI call_auth_handle(void* context)
|
||||
{
|
||||
struct props_rc_parms* pr = (struct props_rc_parms*)context;
|
||||
|
||||
(*(pr->m->auth_handle))(pr->m->auth_handle_context, pr->properties, pr->reasonCode);
|
||||
MQTTProperties_free(pr->properties);
|
||||
free(pr->properties);
|
||||
free(pr);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* This is the thread function that handles the calling of callback functions if set */
|
||||
static thread_return_type WINAPI MQTTClient_run(void* n)
|
||||
{
|
||||
long timeout = 10L; /* first time in we have a small timeout. Gets things started more quickly */
|
||||
|
||||
FUNC_ENTRY;
|
||||
Thread_set_name("MQTTClient_run");
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
run_id = Paho_thread_getid();
|
||||
running = 1;
|
||||
while (!tostop)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
SOCKET sock = -1;
|
||||
MQTTClients* m = NULL;
|
||||
MQTTPacket* pack = NULL;
|
||||
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
pack = MQTTClient_cycle(&sock, timeout, &rc);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (tostop)
|
||||
break;
|
||||
timeout = 100L;
|
||||
|
||||
/* find client corresponding to socket */
|
||||
if (ListFindItem(handles, &sock, clientSockCompare) == NULL)
|
||||
{
|
||||
/* assert: should not happen */
|
||||
continue;
|
||||
}
|
||||
m = (MQTTClient)(handles->current->content);
|
||||
if (m == NULL)
|
||||
{
|
||||
/* assert: should not happen */
|
||||
continue;
|
||||
}
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
if (m->c->connected)
|
||||
MQTTClient_disconnect_internal(m, 0);
|
||||
else
|
||||
{
|
||||
if (m->c->connect_state == SSL_IN_PROGRESS)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Signaling connect event for client %s", m->c->clientID);
|
||||
m->c->connect_state = NOT_IN_PROGRESS;
|
||||
Thread_signal_evt(m->connect_evt);
|
||||
}
|
||||
if (m->c->connect_state == WAIT_FOR_CONNACK)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Signaling connack event for client %s", m->c->clientID);
|
||||
m->c->connect_state = NOT_IN_PROGRESS;
|
||||
Thread_signal_evt(m->connack_evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m->c->messageQueue->count > 0 && m->ma)
|
||||
{
|
||||
qEntry* qe = (qEntry*)(m->c->messageQueue->first->content);
|
||||
int topicLen = qe->topicLen;
|
||||
|
||||
if (strlen(qe->topicName) == topicLen)
|
||||
topicLen = 0;
|
||||
|
||||
Log(TRACE_MIN, -1, "Calling messageArrived for client %s, queue depth %d",
|
||||
m->c->clientID, m->c->messageQueue->count);
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
rc = (*(m->ma))(m->context, qe->topicName, topicLen, qe->msg);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
/* if 0 (false) is returned by the callback then it failed, so we don't remove the message from
|
||||
* the queue, and it will be retried later. If 1 is returned then the message data may have been freed,
|
||||
* so we must be careful how we use it.
|
||||
*/
|
||||
if (rc)
|
||||
{
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (m->c->persistence)
|
||||
MQTTPersistence_unpersistQueueEntry(m->c, (MQTTPersistence_qEntry*)qe);
|
||||
#endif
|
||||
ListRemove(m->c->messageQueue, qe);
|
||||
}
|
||||
else
|
||||
Log(TRACE_MIN, -1, "False returned from messageArrived for client %s, message remains on queue",
|
||||
m->c->clientID);
|
||||
}
|
||||
if (pack)
|
||||
{
|
||||
if (pack->header.bits.type == CONNACK)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Signaling connack event for client %s", m->c->clientID);
|
||||
m->pack = pack;
|
||||
Thread_signal_evt(m->connack_evt);
|
||||
}
|
||||
else if (pack->header.bits.type == SUBACK)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Signaling suback event for client %s", m->c->clientID);
|
||||
m->pack = pack;
|
||||
Thread_signal_evt(m->suback_evt);
|
||||
}
|
||||
else if (pack->header.bits.type == UNSUBACK)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Signaling unsuback event for client %s", m->c->clientID);
|
||||
m->pack = pack;
|
||||
Thread_signal_evt(m->unsuback_evt);
|
||||
}
|
||||
else if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (pack->header.bits.type == DISCONNECT && m->disconnected)
|
||||
{
|
||||
struct props_rc_parms* dp;
|
||||
Ack* disc = (Ack*)pack;
|
||||
|
||||
dp = malloc(sizeof(struct props_rc_parms));
|
||||
if (dp)
|
||||
{
|
||||
dp->m = m;
|
||||
dp->reasonCode = disc->rc;
|
||||
dp->properties = malloc(sizeof(MQTTProperties));
|
||||
if (dp->properties)
|
||||
{
|
||||
*(dp->properties) = disc->properties;
|
||||
MQTTClient_disconnect1(m, 10, 0, 1, MQTTREASONCODE_SUCCESS, NULL);
|
||||
Log(TRACE_MIN, -1, "Calling disconnected for client %s", m->c->clientID);
|
||||
Paho_thread_start(call_disconnected, dp);
|
||||
}
|
||||
else
|
||||
free(dp);
|
||||
}
|
||||
free(disc);
|
||||
}
|
||||
#if 0
|
||||
if (pack->header.bits.type == AUTH && m->auth_handle)
|
||||
{
|
||||
struct props_rc_parms dp;
|
||||
Ack* disc = (Ack*)pack;
|
||||
|
||||
dp.m = m;
|
||||
dp.properties = &disc->properties;
|
||||
dp.reasonCode = disc->rc;
|
||||
free(pack);
|
||||
Log(TRACE_MIN, -1, "Calling auth_handle for client %s", m->c->clientID);
|
||||
Paho_thread_start(call_auth_handle, &dp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (m->c->connect_state == TCP_IN_PROGRESS)
|
||||
{
|
||||
int error;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
if ((m->rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
|
||||
m->rc = error;
|
||||
Log(TRACE_MIN, -1, "Signaling connect event for client %s rc %d", m->c->clientID, m->rc);
|
||||
m->c->connect_state = NOT_IN_PROGRESS;
|
||||
Thread_signal_evt(m->connect_evt);
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (m->c->connect_state == SSL_IN_PROGRESS)
|
||||
{
|
||||
rc = m->c->sslopts->struct_version >= 3 ?
|
||||
SSLSocket_connect(m->c->net.ssl, m->c->net.socket, m->serverURI,
|
||||
m->c->sslopts->verify, m->c->sslopts->ssl_error_cb, m->c->sslopts->ssl_error_context) :
|
||||
SSLSocket_connect(m->c->net.ssl, m->c->net.socket, m->serverURI,
|
||||
m->c->sslopts->verify, NULL, NULL);
|
||||
if (rc == 1 || rc == SSL_FATAL)
|
||||
{
|
||||
if (rc == 1 && (m->c->cleansession == 0 && m->c->cleanstart == 0) && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
m->rc = rc;
|
||||
Log(TRACE_MIN, -1, "Signaling connect event for SSL client %s rc %d", m->c->clientID, m->rc);
|
||||
m->c->connect_state = NOT_IN_PROGRESS;
|
||||
Thread_signal_evt(m->connect_evt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (m->c->connect_state == WEBSOCKET_IN_PROGRESS)
|
||||
{
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Signaling websocket handshake for client %s rc %d", m->c->clientID, m->rc);
|
||||
m->c->connect_state = WAIT_FOR_CONNACK;
|
||||
Thread_signal_evt(m->connect_evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
run_id = 0;
|
||||
running = tostop = 0;
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT;
|
||||
#if defined(_WIN32)
|
||||
ExitThread(0);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTClient_stop(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (running == 1 && tostop == 0)
|
||||
{
|
||||
int conn_count = 0;
|
||||
ListElement* current = NULL;
|
||||
|
||||
if (handles != NULL)
|
||||
{
|
||||
/* find out how many handles are still connected */
|
||||
while (ListNextElement(handles, ¤t))
|
||||
{
|
||||
if (((MQTTClients*)(current->content))->c->connect_state > NOT_IN_PROGRESS ||
|
||||
((MQTTClients*)(current->content))->c->connected)
|
||||
++conn_count;
|
||||
}
|
||||
}
|
||||
Log(TRACE_MIN, -1, "Conn_count is %d", conn_count);
|
||||
/* stop the background thread, if we are the last one to be using it */
|
||||
if (conn_count == 0)
|
||||
{
|
||||
int count = 0;
|
||||
tostop = 1;
|
||||
if (Paho_thread_getid() != run_id)
|
||||
{
|
||||
while (running && ++count < 100)
|
||||
{
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
Log(TRACE_MIN, -1, "sleeping");
|
||||
MQTTTime_sleep(100L);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
}
|
||||
}
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl,
|
||||
MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc)
|
||||
{
|
||||
int rc = MQTTCLIENT_SUCCESS;
|
||||
MQTTClients* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
if (m == NULL || ma == NULL || m->c->connect_state != NOT_IN_PROGRESS)
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
else
|
||||
{
|
||||
m->context = context;
|
||||
m->cl = cl;
|
||||
m->ma = ma;
|
||||
m->dc = dc;
|
||||
}
|
||||
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTClient_closeSession(Clients* client, enum MQTTReasonCodes reason, MQTTProperties* props)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
client->good = 0;
|
||||
client->ping_outstanding = 0;
|
||||
client->ping_due = 0;
|
||||
if (client->net.socket > 0)
|
||||
{
|
||||
if (client->connected)
|
||||
MQTTPacket_send_disconnect(client, reason, props);
|
||||
Paho_thread_lock_mutex(socket_mutex);
|
||||
WebSocket_close(&client->net, WebSocket_CLOSE_NORMAL, NULL);
|
||||
|
||||
#if defined(OPENSSL)
|
||||
SSL_SESSION_free(client->session); /* is a no-op if session is NULL */
|
||||
client->session = NULL; /* show the session has been freed */
|
||||
SSLSocket_close(&client->net);
|
||||
#endif
|
||||
Paho_thread_unlock_mutex(socket_mutex);
|
||||
Socket_close(client->net.socket);
|
||||
client->net.socket = 0;
|
||||
#if defined(OPENSSL)
|
||||
client->net.ssl = NULL;
|
||||
#endif
|
||||
}
|
||||
client->connected = 0;
|
||||
client->connect_state = NOT_IN_PROGRESS;
|
||||
|
||||
if (client->MQTTVersion < MQTTVERSION_5 && client->cleansession)
|
||||
MQTTClient_cleanSession(client);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static int MQTTClient_cleanSession(Clients* client)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
rc = MQTTPersistence_clear(client);
|
||||
#endif
|
||||
MQTTProtocol_emptyMessageList(client->inboundMsgs);
|
||||
MQTTProtocol_emptyMessageList(client->outboundMsgs);
|
||||
MQTTClient_emptyMessageQueue(client);
|
||||
client->msgID = 0;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void Protocol_processPublication(Publish* publish, Clients* client, int allocatePayload)
|
||||
{
|
||||
qEntry* qe = NULL;
|
||||
MQTTClient_message* mm = NULL;
|
||||
MQTTClient_message initialized = MQTTClient_message_initializer;
|
||||
|
||||
FUNC_ENTRY;
|
||||
qe = malloc(sizeof(qEntry));
|
||||
if (!qe)
|
||||
goto exit;
|
||||
mm = malloc(sizeof(MQTTClient_message));
|
||||
if (!mm)
|
||||
{
|
||||
free(qe);
|
||||
goto exit;
|
||||
}
|
||||
memcpy(mm, &initialized, sizeof(MQTTClient_message));
|
||||
|
||||
qe->msg = mm;
|
||||
qe->topicName = publish->topic;
|
||||
qe->topicLen = publish->topiclen;
|
||||
publish->topic = NULL;
|
||||
if (allocatePayload)
|
||||
{
|
||||
mm->payload = malloc(publish->payloadlen);
|
||||
if (mm->payload == NULL)
|
||||
{
|
||||
free(mm);
|
||||
free(qe);
|
||||
goto exit;
|
||||
}
|
||||
memcpy(mm->payload, publish->payload, publish->payloadlen);
|
||||
}
|
||||
else
|
||||
mm->payload = publish->payload;
|
||||
mm->payloadlen = publish->payloadlen;
|
||||
mm->qos = publish->header.bits.qos;
|
||||
mm->retained = publish->header.bits.retain;
|
||||
if (publish->header.bits.qos == 2)
|
||||
mm->dup = 0; /* ensure that a QoS2 message is not passed to the application with dup = 1 */
|
||||
else
|
||||
mm->dup = publish->header.bits.dup;
|
||||
mm->msgid = publish->msgId;
|
||||
|
||||
if (publish->MQTTVersion >= 5)
|
||||
mm->properties = MQTTProperties_copy(&publish->properties);
|
||||
|
||||
ListAppend(client->messageQueue, qe, sizeof(qe) + sizeof(mm) + mm->payloadlen + strlen(qe->topicName)+1);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (client->persistence)
|
||||
MQTTPersistence_persistQueueEntry(client, (MQTTPersistence_qEntry*)qe);
|
||||
#endif
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static MQTTResponse MQTTClient_connectURIVersion(MQTTClient handle, MQTTClient_connectOptions* options, const char* serverURI, int MQTTVersion,
|
||||
START_TIME_TYPE start, ELAPSED_TIME_TYPE millisecsTimeout, MQTTProperties* connectProperties, MQTTProperties* willProperties)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
int rc = SOCKET_ERROR;
|
||||
int sessionPresent = 0;
|
||||
MQTTResponse resp = MQTTResponse_initializer;
|
||||
|
||||
FUNC_ENTRY;
|
||||
resp.reasonCode = SOCKET_ERROR;
|
||||
if (m->ma && !running)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
Paho_thread_start(MQTTClient_run, handle);
|
||||
if (MQTTTime_elapsed(start) >= millisecsTimeout)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
while (!running && ++count < 5)
|
||||
{
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTTime_sleep(100L);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
}
|
||||
if (!running)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
Log(TRACE_MIN, -1, "Connecting to serverURI %s with MQTT version %d", serverURI, MQTTVersion);
|
||||
#if defined(OPENSSL)
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
rc = MQTTProtocol_connect(serverURI, m->c, m->unixsock, m->ssl, m->websocket, MQTTVersion, connectProperties, willProperties,
|
||||
millisecsTimeout - MQTTTime_elapsed(start));
|
||||
#else
|
||||
rc = MQTTProtocol_connect(serverURI, m->c, m->unixsock, m->ssl, m->websocket, MQTTVersion, connectProperties, willProperties);
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
rc = MQTTProtocol_connect(serverURI, m->c, m->unixsock, m->websocket, MQTTVersion, connectProperties, willProperties,
|
||||
millisecsTimeout - MQTTTime_elapsed(start));
|
||||
#else
|
||||
rc = MQTTProtocol_connect(serverURI, m->c, m->unixsock, m->websocket, MQTTVersion, connectProperties, willProperties);
|
||||
#endif
|
||||
#endif
|
||||
if (rc == SOCKET_ERROR)
|
||||
goto exit;
|
||||
|
||||
if (m->c->connect_state == NOT_IN_PROGRESS)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (m->c->connect_state == TCP_IN_PROGRESS) /* TCP connect started - wait for completion */
|
||||
{
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTTime_elapsed(start));
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (rc != 0)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
if (m->ssl)
|
||||
{
|
||||
int port1;
|
||||
size_t hostname_len;
|
||||
const char *topic;
|
||||
int setSocketForSSLrc = 0;
|
||||
|
||||
if (m->c->net.https_proxy) {
|
||||
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
|
||||
if ((rc = Proxy_connect( &m->c->net, 1, serverURI)) == SOCKET_ERROR )
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hostname_len = MQTTProtocol_addressPort(serverURI, &port1, &topic, MQTT_DEFAULT_PORT);
|
||||
setSocketForSSLrc = SSLSocket_setSocketForSSL(&m->c->net, m->c->sslopts,
|
||||
serverURI, hostname_len);
|
||||
|
||||
if (setSocketForSSLrc == 1)
|
||||
{
|
||||
if (m->c->session != NULL)
|
||||
if ((rc = SSL_set_session(m->c->net.ssl, m->c->session)) != 1)
|
||||
Log(TRACE_MIN, -1, "Failed to set SSL session with stored data, non critical");
|
||||
rc = m->c->sslopts->struct_version >= 3 ?
|
||||
SSLSocket_connect(m->c->net.ssl, m->c->net.socket, serverURI,
|
||||
m->c->sslopts->verify, m->c->sslopts->ssl_error_cb, m->c->sslopts->ssl_error_context) :
|
||||
SSLSocket_connect(m->c->net.ssl, m->c->net.socket, serverURI,
|
||||
m->c->sslopts->verify, NULL, NULL);
|
||||
if (rc == TCPSOCKET_INTERRUPTED)
|
||||
m->c->connect_state = SSL_IN_PROGRESS; /* the connect is still in progress */
|
||||
else if (rc == SSL_FATAL)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
else if (rc == 1)
|
||||
{
|
||||
if (m->websocket)
|
||||
{
|
||||
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
|
||||
rc = WebSocket_connect(&m->c->net, 1, serverURI);
|
||||
if ( rc == SOCKET_ERROR )
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = MQTTCLIENT_SUCCESS;
|
||||
m->c->connect_state = WAIT_FOR_CONNACK;
|
||||
if (MQTTPacket_send_connect(m->c, MQTTVersion, connectProperties, willProperties) == SOCKET_ERROR)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if ((m->c->cleansession == 0 && m->c->cleanstart == 0) && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
if (m->c->net.http_proxy) {
|
||||
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
|
||||
if ((rc = Proxy_connect( &m->c->net, 0, serverURI)) == SOCKET_ERROR )
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (m->websocket)
|
||||
{
|
||||
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
|
||||
if ( WebSocket_connect(&m->c->net, 0, serverURI) == SOCKET_ERROR )
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->connect_state = WAIT_FOR_CONNACK; /* TCP connect completed, in which case send the MQTT connect packet */
|
||||
if (MQTTPacket_send_connect(m->c, MQTTVersion, connectProperties, willProperties) == SOCKET_ERROR)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (m->c->connect_state == SSL_IN_PROGRESS) /* SSL connect sent - wait for completion */
|
||||
{
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTTime_elapsed(start));
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (rc != 1)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if((m->c->cleansession == 0 && m->c->cleanstart == 0) && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
|
||||
if ( m->websocket )
|
||||
{
|
||||
/* wait for websocket connect */
|
||||
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
|
||||
rc = WebSocket_connect( &m->c->net, 1, serverURI);
|
||||
if ( rc != 1 )
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->connect_state = WAIT_FOR_CONNACK; /* TCP connect completed, in which case send the MQTT connect packet */
|
||||
if (MQTTPacket_send_connect(m->c, MQTTVersion, connectProperties, willProperties) == SOCKET_ERROR)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m->c->connect_state == WEBSOCKET_IN_PROGRESS) /* websocket request sent - wait for upgrade */
|
||||
{
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTTime_elapsed(start));
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
m->c->connect_state = WAIT_FOR_CONNACK; /* websocket upgrade complete */
|
||||
if (MQTTPacket_send_connect(m->c, MQTTVersion, connectProperties, willProperties) == SOCKET_ERROR)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (m->c->connect_state == WAIT_FOR_CONNACK) /* MQTT connect sent - wait for CONNACK */
|
||||
{
|
||||
MQTTPacket* pack = NULL;
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
pack = MQTTClient_waitfor(handle, CONNACK, &rc, millisecsTimeout - MQTTTime_elapsed(start));
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (pack == NULL)
|
||||
rc = SOCKET_ERROR;
|
||||
else
|
||||
{
|
||||
Connack* connack = (Connack*)pack;
|
||||
Log(TRACE_PROTOCOL, 1, NULL, m->c->net.socket, m->c->clientID, connack->rc);
|
||||
if ((rc = connack->rc) == MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
m->c->connected = 1;
|
||||
m->c->good = 1;
|
||||
m->c->connect_state = NOT_IN_PROGRESS;
|
||||
if (MQTTVersion >= MQTTVERSION_3_1_1)
|
||||
sessionPresent = connack->flags.bits.sessionPresent;
|
||||
if (m->c->cleansession || m->c->cleanstart)
|
||||
rc = MQTTClient_cleanSession(m->c);
|
||||
if (m->c->outboundMsgs->count > 0)
|
||||
{
|
||||
ListElement* outcurrent = NULL;
|
||||
START_TIME_TYPE zero = START_TIME_ZERO;
|
||||
|
||||
while (ListNextElement(m->c->outboundMsgs, &outcurrent))
|
||||
{
|
||||
Messages* m2 = (Messages*)(outcurrent->content);
|
||||
memset(&m2->lastTouch, '\0', sizeof(m2->lastTouch));
|
||||
}
|
||||
MQTTProtocol_retry(zero, 1, 1);
|
||||
if (m->c->connected != 1)
|
||||
rc = MQTTCLIENT_DISCONNECTED;
|
||||
}
|
||||
if (m->c->MQTTVersion == MQTTVERSION_5)
|
||||
{
|
||||
if ((resp.properties = malloc(sizeof(MQTTProperties))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
*resp.properties = MQTTProperties_copy(&connack->properties);
|
||||
|
||||
if (MQTTProperties_hasProperty(&connack->properties, MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE))
|
||||
{
|
||||
/* update the keep alive from the server keep alive */
|
||||
int server_keep_alive = (int)MQTTProperties_getNumericValue(&connack->properties, MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE);
|
||||
if (server_keep_alive != -999999)
|
||||
{
|
||||
Log(LOG_PROTOCOL, -1, "Setting keep alive interval to server keep alive %d", server_keep_alive);
|
||||
m->c->keepAliveInterval = server_keep_alive;
|
||||
}
|
||||
}
|
||||
else if (m->c->keepAliveInterval != m->c->savedKeepAliveInterval)
|
||||
{
|
||||
/* if the keep alive has been previously updated with a server keep alive, but there is no server keep alive
|
||||
on this connect, reset it to the value requested in the original connect API */
|
||||
Log(LOG_PROTOCOL, -1, "Resetting keep alive interval to %d", m->c->savedKeepAliveInterval);
|
||||
m->c->keepAliveInterval = m->c->savedKeepAliveInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
MQTTPacket_freeConnack(connack);
|
||||
m->pack = NULL;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if (rc == MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
if (options->struct_version >= 4) /* means we have to fill out return values */
|
||||
{
|
||||
options->returned.serverURI = serverURI;
|
||||
options->returned.MQTTVersion = MQTTVersion;
|
||||
options->returned.sessionPresent = sessionPresent;
|
||||
}
|
||||
}
|
||||
else
|
||||
MQTTClient_disconnect1(handle, 0, 0, (MQTTVersion == 3), MQTTREASONCODE_SUCCESS, NULL); /* don't want to call connection lost */
|
||||
|
||||
resp.reasonCode = rc;
|
||||
FUNC_EXIT_RC(resp.reasonCode);
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
static int retryLoopIntervalms = 5000;
|
||||
|
||||
void setRetryLoopInterval(int keepalive)
|
||||
{
|
||||
retryLoopIntervalms = (keepalive*1000) / 10;
|
||||
|
||||
if (retryLoopIntervalms < 100)
|
||||
retryLoopIntervalms = 100;
|
||||
else if (retryLoopIntervalms > 5000)
|
||||
retryLoopIntervalms = 5000;
|
||||
}
|
||||
|
||||
|
||||
static MQTTResponse MQTTClient_connectURI(MQTTClient handle, MQTTClient_connectOptions* options, const char* serverURI,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
START_TIME_TYPE start;
|
||||
ELAPSED_TIME_TYPE millisecsTimeout = 30000L;
|
||||
MQTTResponse rc = MQTTResponse_initializer;
|
||||
int MQTTVersion = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc.reasonCode = SOCKET_ERROR;
|
||||
millisecsTimeout = options->connectTimeout * 1000;
|
||||
start = MQTTTime_start_clock();
|
||||
|
||||
m->currentServerURI = serverURI;
|
||||
m->c->keepAliveInterval = m->c->savedKeepAliveInterval = options->keepAliveInterval;
|
||||
m->c->retryInterval = options->retryInterval;
|
||||
setRetryLoopInterval(options->keepAliveInterval);
|
||||
m->c->MQTTVersion = options->MQTTVersion;
|
||||
m->c->cleanstart = m->c->cleansession = 0;
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
m->c->cleanstart = options->cleanstart;
|
||||
else
|
||||
m->c->cleansession = options->cleansession;
|
||||
m->c->maxInflightMessages = (options->reliable) ? 1 : 10;
|
||||
if (options->struct_version >= 6)
|
||||
{
|
||||
if (options->maxInflightMessages > 0)
|
||||
m->c->maxInflightMessages = options->maxInflightMessages;
|
||||
}
|
||||
|
||||
if (options->struct_version >= 7)
|
||||
{
|
||||
m->c->net.httpHeaders = options->httpHeaders;
|
||||
}
|
||||
if (options->struct_version >= 8)
|
||||
{
|
||||
if (options->httpProxy)
|
||||
m->c->httpProxy = MQTTStrdup(options->httpProxy);
|
||||
if (options->httpsProxy)
|
||||
m->c->httpsProxy = MQTTStrdup(options->httpsProxy);
|
||||
}
|
||||
|
||||
if (m->c->will)
|
||||
{
|
||||
free(m->c->will->payload);
|
||||
free(m->c->will->topic);
|
||||
free(m->c->will);
|
||||
m->c->will = NULL;
|
||||
}
|
||||
|
||||
if (options->will && (options->will->struct_version == 0 || options->will->struct_version == 1))
|
||||
{
|
||||
const void* source = NULL;
|
||||
|
||||
if ((m->c->will = malloc(sizeof(willMessages))) == NULL)
|
||||
{
|
||||
rc.reasonCode = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (options->will->message || (options->will->struct_version == 1 && options->will->payload.data))
|
||||
{
|
||||
if (options->will->struct_version == 1 && options->will->payload.data)
|
||||
{
|
||||
m->c->will->payloadlen = options->will->payload.len;
|
||||
source = options->will->payload.data;
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->will->payloadlen = (int)strlen(options->will->message);
|
||||
source = (void*)options->will->message;
|
||||
}
|
||||
if ((m->c->will->payload = malloc(m->c->will->payloadlen)) == NULL)
|
||||
{
|
||||
free(m->c->will);
|
||||
rc.reasonCode = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(m->c->will->payload, source, m->c->will->payloadlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
m->c->will->payload = NULL;
|
||||
m->c->will->payloadlen = 0;
|
||||
}
|
||||
m->c->will->qos = options->will->qos;
|
||||
m->c->will->retained = options->will->retained;
|
||||
m->c->will->topic = MQTTStrdup(options->will->topicName);
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (m->c->sslopts)
|
||||
{
|
||||
if (m->c->sslopts->trustStore)
|
||||
free((void*)m->c->sslopts->trustStore);
|
||||
if (m->c->sslopts->keyStore)
|
||||
free((void*)m->c->sslopts->keyStore);
|
||||
if (m->c->sslopts->privateKey)
|
||||
free((void*)m->c->sslopts->privateKey);
|
||||
if (m->c->sslopts->privateKeyPassword)
|
||||
free((void*)m->c->sslopts->privateKeyPassword);
|
||||
if (m->c->sslopts->enabledCipherSuites)
|
||||
free((void*)m->c->sslopts->enabledCipherSuites);
|
||||
if (m->c->sslopts->struct_version >= 2)
|
||||
{
|
||||
if (m->c->sslopts->CApath)
|
||||
free((void*)m->c->sslopts->CApath);
|
||||
}
|
||||
free(m->c->sslopts);
|
||||
m->c->sslopts = NULL;
|
||||
}
|
||||
|
||||
if (options->struct_version != 0 && options->ssl)
|
||||
{
|
||||
if ((m->c->sslopts = malloc(sizeof(MQTTClient_SSLOptions))) == NULL)
|
||||
{
|
||||
rc.reasonCode = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memset(m->c->sslopts, '\0', sizeof(MQTTClient_SSLOptions));
|
||||
m->c->sslopts->struct_version = options->ssl->struct_version;
|
||||
if (options->ssl->trustStore)
|
||||
m->c->sslopts->trustStore = MQTTStrdup(options->ssl->trustStore);
|
||||
if (options->ssl->keyStore)
|
||||
m->c->sslopts->keyStore = MQTTStrdup(options->ssl->keyStore);
|
||||
if (options->ssl->privateKey)
|
||||
m->c->sslopts->privateKey = MQTTStrdup(options->ssl->privateKey);
|
||||
if (options->ssl->privateKeyPassword)
|
||||
m->c->sslopts->privateKeyPassword = MQTTStrdup(options->ssl->privateKeyPassword);
|
||||
if (options->ssl->enabledCipherSuites)
|
||||
m->c->sslopts->enabledCipherSuites = MQTTStrdup(options->ssl->enabledCipherSuites);
|
||||
m->c->sslopts->enableServerCertAuth = options->ssl->enableServerCertAuth;
|
||||
if (m->c->sslopts->struct_version >= 1)
|
||||
m->c->sslopts->sslVersion = options->ssl->sslVersion;
|
||||
if (m->c->sslopts->struct_version >= 2)
|
||||
{
|
||||
m->c->sslopts->verify = options->ssl->verify;
|
||||
if (options->ssl->CApath)
|
||||
m->c->sslopts->CApath = MQTTStrdup(options->ssl->CApath);
|
||||
}
|
||||
if (m->c->sslopts->struct_version >= 3)
|
||||
{
|
||||
m->c->sslopts->ssl_error_cb = options->ssl->ssl_error_cb;
|
||||
m->c->sslopts->ssl_error_context = options->ssl->ssl_error_context;
|
||||
}
|
||||
if (m->c->sslopts->struct_version >= 4)
|
||||
{
|
||||
m->c->sslopts->ssl_psk_cb = options->ssl->ssl_psk_cb;
|
||||
m->c->sslopts->ssl_psk_context = options->ssl->ssl_psk_context;
|
||||
m->c->sslopts->disableDefaultTrustStore = options->ssl->disableDefaultTrustStore;
|
||||
}
|
||||
if (m->c->sslopts->struct_version >= 5)
|
||||
{
|
||||
m->c->sslopts->protos = options->ssl->protos;
|
||||
m->c->sslopts->protos_len = options->ssl->protos_len;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m->c->username)
|
||||
{
|
||||
free((void*)m->c->username);
|
||||
m->c->username = NULL;
|
||||
}
|
||||
if (options->username)
|
||||
m->c->username = MQTTStrdup(options->username);
|
||||
if (m->c->password)
|
||||
{
|
||||
free((void*)m->c->password);
|
||||
m->c->password = NULL;
|
||||
}
|
||||
if (options->password)
|
||||
{
|
||||
m->c->password = MQTTStrdup(options->password);
|
||||
m->c->passwordlen = (int)strlen(options->password);
|
||||
}
|
||||
else if (options->struct_version >= 5 && options->binarypwd.data)
|
||||
{
|
||||
m->c->passwordlen = options->binarypwd.len;
|
||||
if ((m->c->password = malloc(m->c->passwordlen)) == NULL)
|
||||
{
|
||||
rc.reasonCode = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
memcpy((void*)m->c->password, options->binarypwd.data, m->c->passwordlen);
|
||||
}
|
||||
|
||||
if (options->struct_version >= 3)
|
||||
MQTTVersion = options->MQTTVersion;
|
||||
else
|
||||
MQTTVersion = MQTTVERSION_DEFAULT;
|
||||
|
||||
if (MQTTVersion == MQTTVERSION_DEFAULT)
|
||||
{
|
||||
rc = MQTTClient_connectURIVersion(handle, options, serverURI, 4, start, millisecsTimeout,
|
||||
connectProperties, willProperties);
|
||||
if (rc.reasonCode != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
rc = MQTTClient_connectURIVersion(handle, options, serverURI, 3, start, millisecsTimeout,
|
||||
connectProperties, willProperties);
|
||||
}
|
||||
}
|
||||
else
|
||||
rc = MQTTClient_connectURIVersion(handle, options, serverURI, MQTTVersion, start, millisecsTimeout,
|
||||
connectProperties, willProperties);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc.reasonCode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MQTTResponse MQTTClient_connectAll(MQTTClient handle, MQTTClient_connectOptions* options,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties);
|
||||
|
||||
int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
MQTTResponse response;
|
||||
|
||||
if (m != NULL && m->c != NULL && m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
return MQTTCLIENT_WRONG_MQTT_VERSION;
|
||||
|
||||
response = MQTTClient_connectAll(handle, options, NULL, NULL);
|
||||
|
||||
return response.reasonCode;
|
||||
}
|
||||
|
||||
|
||||
MQTTResponse MQTTClient_connect5(MQTTClient handle, MQTTClient_connectOptions* options,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
MQTTResponse response = MQTTResponse_initializer;
|
||||
|
||||
if (m != NULL && m->c != NULL && m->c->MQTTVersion < MQTTVERSION_5)
|
||||
{
|
||||
response.reasonCode = MQTTCLIENT_WRONG_MQTT_VERSION;
|
||||
return response;
|
||||
}
|
||||
|
||||
return MQTTClient_connectAll(handle, options, connectProperties, willProperties);
|
||||
}
|
||||
|
||||
|
||||
MQTTResponse MQTTClient_connectAll(MQTTClient handle, MQTTClient_connectOptions* options,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
MQTTResponse rc = MQTTResponse_initializer;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(connect_mutex);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
rc.reasonCode = SOCKET_ERROR;
|
||||
if (!library_initialized)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (options == NULL || m == NULL || m->c == NULL)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strncmp(options->struct_id, "MQTC", 4) != 0 || options->struct_version < 0 || options->struct_version > 8)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (m->ssl && options->ssl == NULL)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (options->will) /* check validity of will options structure */
|
||||
{
|
||||
if (strncmp(options->will->struct_id, "MQTW", 4) != 0 || (options->will->struct_version != 0 && options->will->struct_version != 1))
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
if (options->will->qos < 0 || options->will->qos > 2)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_QOS;
|
||||
goto exit;
|
||||
}
|
||||
if (options->will->topicName == NULL)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_NULL_PARAMETER;
|
||||
goto exit;
|
||||
} else if (strlen(options->will->topicName) == 0)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_0_LEN_WILL_TOPIC;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */
|
||||
{
|
||||
if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 5)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((options->username && !UTF8_validateString(options->username)) ||
|
||||
(options->password && !UTF8_validateString(options->password)))
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_UTF8_STRING;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (options->MQTTVersion != MQTTVERSION_DEFAULT &&
|
||||
(options->MQTTVersion < MQTTVERSION_3_1 || options->MQTTVersion > MQTTVERSION_5))
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_MQTT_VERSION;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (options->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (options->cleansession != 0)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_MQTT_OPTION;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else if (options->cleanstart != 0)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_MQTT_OPTION;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (options->struct_version < 2 || options->serverURIcount == 0)
|
||||
{
|
||||
if ( !m )
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
rc = MQTTClient_connectURI(handle, options, m->serverURI, connectProperties, willProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < options->serverURIcount; ++i)
|
||||
{
|
||||
char* serverURI = options->serverURIs[i];
|
||||
|
||||
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
|
||||
serverURI += strlen(URI_TCP);
|
||||
else if (strncmp(URI_MQTT, serverURI, strlen(URI_MQTT)) == 0)
|
||||
serverURI += strlen(URI_TCP);
|
||||
else if (strncmp(URI_WS, serverURI, strlen(URI_WS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WS);
|
||||
m->websocket = 1;
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_SSL);
|
||||
m->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_TLS, serverURI, strlen(URI_TLS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_TLS);
|
||||
m->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_MQTTS, serverURI, strlen(URI_MQTTS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_MQTTS);
|
||||
m->ssl = 1;
|
||||
}
|
||||
else if (strncmp(URI_WSS, serverURI, strlen(URI_WSS)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_WSS);
|
||||
m->ssl = 1;
|
||||
m->websocket = 1;
|
||||
}
|
||||
#endif
|
||||
#if defined(UNIXSOCK)
|
||||
else if (strncmp(URI_UNIX, serverURI, strlen(URI_UNIX)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_UNIX);
|
||||
m->unixsock = 1;
|
||||
}
|
||||
#endif
|
||||
rc = MQTTClient_connectURI(handle, options, serverURI, connectProperties, willProperties);
|
||||
if (rc.reasonCode == MQTTREASONCODE_SUCCESS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rc.reasonCode == MQTTREASONCODE_SUCCESS)
|
||||
{
|
||||
if (rc.properties && MQTTProperties_hasProperty(rc.properties, MQTTPROPERTY_CODE_RECEIVE_MAXIMUM))
|
||||
{
|
||||
int recv_max = (int)MQTTProperties_getNumericValue(rc.properties, MQTTPROPERTY_CODE_RECEIVE_MAXIMUM);
|
||||
if (m->c->maxInflightMessages > recv_max)
|
||||
m->c->maxInflightMessages = recv_max;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (m && m->c && m->c->will)
|
||||
{
|
||||
if (m->c->will->payload)
|
||||
free(m->c->will->payload);
|
||||
if (m->c->will->topic)
|
||||
free(m->c->will->topic);
|
||||
free(m->c->will);
|
||||
m->c->will = NULL;
|
||||
}
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
Paho_thread_unlock_mutex(connect_mutex);
|
||||
FUNC_EXIT_RC(rc.reasonCode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mqttclient_mutex must be locked when you call this function, if multi threaded
|
||||
*/
|
||||
static int MQTTClient_disconnect1(MQTTClient handle, int timeout, int call_connection_lost, int stop,
|
||||
enum MQTTReasonCodes reason, MQTTProperties* props)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
START_TIME_TYPE start;
|
||||
int rc = MQTTCLIENT_SUCCESS;
|
||||
int was_connected = 0;
|
||||
struct conlost_sync_data sync = {
|
||||
NULL, m
|
||||
};
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (m == NULL || m->c == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
was_connected = m->c->connected; /* should be 1 */
|
||||
if (m->c->connected != 0)
|
||||
{
|
||||
start = MQTTTime_start_clock();
|
||||
m->c->connect_state = DISCONNECTING; /* indicate disconnecting */
|
||||
while (m->c->inboundMsgs->count > 0 || m->c->outboundMsgs->count > 0)
|
||||
{ /* wait for all inflight message flows to finish, up to timeout */
|
||||
if (MQTTTime_elapsed(start) >= (ELAPSED_TIME_TYPE)timeout)
|
||||
break;
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_yield();
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
MQTTClient_closeSession(m->c, reason, props);
|
||||
|
||||
exit:
|
||||
if (stop)
|
||||
MQTTClient_stop();
|
||||
if (call_connection_lost && m->cl && was_connected)
|
||||
{
|
||||
sync.evt = Thread_create_evt(&rc);
|
||||
Log(TRACE_MIN, -1, "Calling connectionLost for client %s", m->c->clientID);
|
||||
Paho_thread_start(connectionLost_call, &sync);
|
||||
Thread_wait_evt(sync.evt, 5000);
|
||||
Thread_destroy_evt(sync.evt);
|
||||
}
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mqttclient_mutex must be locked when you call this function, if multi threaded
|
||||
*/
|
||||
static int MQTTClient_disconnect_internal(MQTTClient handle, int timeout)
|
||||
{
|
||||
return MQTTClient_disconnect1(handle, timeout, 1, 1, MQTTREASONCODE_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mqttclient_mutex must be locked when you call this function, if multi threaded
|
||||
*/
|
||||
void MQTTProtocol_closeSession(Clients* c, int sendwill)
|
||||
{
|
||||
MQTTClient_disconnect_internal((MQTTClient)c->context, 0);
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_disconnect(MQTTClient handle, int timeout)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
rc = MQTTClient_disconnect1(handle, timeout, 0, 1, MQTTREASONCODE_SUCCESS, NULL);
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_disconnect5(MQTTClient handle, int timeout, enum MQTTReasonCodes reason, MQTTProperties* props)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
rc = MQTTClient_disconnect1(handle, timeout, 0, 1, reason, props);
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_isConnected(MQTTClient handle)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (m && m->c)
|
||||
rc = m->c->connected;
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
MQTTResponse MQTTClient_subscribeMany5(MQTTClient handle, int count, char* const* topic,
|
||||
int* qos, MQTTSubscribe_options* opts, MQTTProperties* props)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
List* topics = NULL;
|
||||
List* qoss = NULL;
|
||||
int i = 0;
|
||||
int rc = MQTTCLIENT_FAILURE;
|
||||
MQTTResponse resp = MQTTResponse_initializer;
|
||||
int msgid = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(subscribe_mutex);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
resp.reasonCode = MQTTCLIENT_FAILURE;
|
||||
if (m == NULL || m->c == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
if (m->c->connected == 0)
|
||||
{
|
||||
rc = MQTTCLIENT_DISCONNECTED;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (!UTF8_validateString(topic[i]))
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_UTF8_STRING;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (qos[i] < 0 || qos[i] > 2)
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_QOS;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if ((msgid = MQTTProtocol_assignMsgId(m->c)) == 0)
|
||||
{
|
||||
rc = MQTTCLIENT_MAX_MESSAGES_INFLIGHT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
topics = ListInitialize();
|
||||
qoss = ListInitialize();
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
ListAppend(topics, topic[i], strlen(topic[i]));
|
||||
ListAppend(qoss, &qos[i], sizeof(int));
|
||||
}
|
||||
|
||||
rc = MQTTProtocol_subscribe(m->c, topics, qoss, msgid, opts, props);
|
||||
ListFreeNoContent(topics);
|
||||
ListFreeNoContent(qoss);
|
||||
|
||||
if (rc == TCPSOCKET_COMPLETE)
|
||||
{
|
||||
MQTTPacket* pack = NULL;
|
||||
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
pack = MQTTClient_waitfor(handle, SUBACK, &rc, m->commandTimeout);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (pack != NULL)
|
||||
{
|
||||
Suback* sub = (Suback*)pack;
|
||||
|
||||
if (m->c->MQTTVersion == MQTTVERSION_5)
|
||||
{
|
||||
if (sub->properties.count > 0)
|
||||
{
|
||||
if ((resp.properties = malloc(sizeof(MQTTProperties))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
*resp.properties = MQTTProperties_copy(&sub->properties);
|
||||
}
|
||||
resp.reasonCodeCount = sub->qoss->count;
|
||||
resp.reasonCode = *(int*)sub->qoss->first->content;
|
||||
if (sub->qoss->count > 1)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
int rc_count = 0;
|
||||
|
||||
if ((resp.reasonCodes = malloc(sizeof(enum MQTTReasonCodes) * (sub->qoss->count))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
while (ListNextElement(sub->qoss, ¤t))
|
||||
(resp.reasonCodes)[rc_count++] = *(enum MQTTReasonCodes*)(current->content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ListElement *current = NULL;
|
||||
|
||||
/* if the returned count is greater than requested, it's an error*/
|
||||
if (sub->qoss->count > count)
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
else
|
||||
{
|
||||
i = 0;
|
||||
while (ListNextElement(sub->qoss, ¤t))
|
||||
{
|
||||
int *reqqos = (int*) (current->content);
|
||||
qos[i++] = *reqqos;
|
||||
}
|
||||
}
|
||||
resp.reasonCode = rc;
|
||||
}
|
||||
rc = MQTTProtocol_handleSubacks(pack, m->c->net.socket);
|
||||
m->pack = NULL;
|
||||
}
|
||||
else
|
||||
rc = SOCKET_ERROR;
|
||||
}
|
||||
|
||||
if (rc == SOCKET_ERROR)
|
||||
MQTTClient_disconnect_internal(handle, 0);
|
||||
else if (rc == TCPSOCKET_COMPLETE)
|
||||
rc = MQTTCLIENT_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (rc < 0)
|
||||
resp.reasonCode = rc;
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
Paho_thread_unlock_mutex(subscribe_mutex);
|
||||
FUNC_EXIT_RC(resp.reasonCode);
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int MQTTClient_subscribeMany(MQTTClient handle, int count, char* const* topic, int* qos)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
MQTTResponse response = MQTTResponse_initializer;
|
||||
|
||||
if (m != NULL && m->c != NULL && m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
response.reasonCode = MQTTCLIENT_WRONG_MQTT_VERSION;
|
||||
else
|
||||
response = MQTTClient_subscribeMany5(handle, count, topic, qos, NULL, NULL);
|
||||
|
||||
return response.reasonCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
MQTTResponse MQTTClient_subscribe5(MQTTClient handle, const char* topic, int qos,
|
||||
MQTTSubscribe_options* opts, MQTTProperties* props)
|
||||
{
|
||||
MQTTResponse rc;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTClient_subscribeMany5(handle, 1, (char * const *)(&topic), &qos, opts, props);
|
||||
if (qos == MQTT_BAD_SUBSCRIBE) /* addition for MQTT 3.1.1 - error code from subscribe */
|
||||
rc.reasonCode = MQTT_BAD_SUBSCRIBE;
|
||||
FUNC_EXIT_RC(rc.reasonCode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_subscribe(MQTTClient handle, const char* topic, int qos)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
MQTTResponse response = MQTTResponse_initializer;
|
||||
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
response.reasonCode = MQTTCLIENT_WRONG_MQTT_VERSION;
|
||||
else
|
||||
response = MQTTClient_subscribe5(handle, topic, qos, NULL, NULL);
|
||||
|
||||
return response.reasonCode;
|
||||
}
|
||||
|
||||
|
||||
MQTTResponse MQTTClient_unsubscribeMany5(MQTTClient handle, int count, char* const* topic, MQTTProperties* props)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
List* topics = NULL;
|
||||
int i = 0;
|
||||
int rc = SOCKET_ERROR;
|
||||
MQTTResponse resp = MQTTResponse_initializer;
|
||||
int msgid = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(subscribe_mutex);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
resp.reasonCode = MQTTCLIENT_FAILURE;
|
||||
if (m == NULL || m->c == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
if (m->c->connected == 0)
|
||||
{
|
||||
rc = MQTTCLIENT_DISCONNECTED;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (!UTF8_validateString(topic[i]))
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_UTF8_STRING;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if ((msgid = MQTTProtocol_assignMsgId(m->c)) == 0)
|
||||
{
|
||||
rc = MQTTCLIENT_MAX_MESSAGES_INFLIGHT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
topics = ListInitialize();
|
||||
for (i = 0; i < count; i++)
|
||||
ListAppend(topics, topic[i], strlen(topic[i]));
|
||||
rc = MQTTProtocol_unsubscribe(m->c, topics, msgid, props);
|
||||
ListFreeNoContent(topics);
|
||||
|
||||
if (rc == TCPSOCKET_COMPLETE)
|
||||
{
|
||||
MQTTPacket* pack = NULL;
|
||||
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
pack = MQTTClient_waitfor(handle, UNSUBACK, &rc, m->commandTimeout);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (pack != NULL)
|
||||
{
|
||||
Unsuback* unsub = (Unsuback*)pack;
|
||||
|
||||
if (m->c->MQTTVersion == MQTTVERSION_5)
|
||||
{
|
||||
if (unsub->properties.count > 0)
|
||||
{
|
||||
if ((resp.properties = malloc(sizeof(MQTTProperties))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
*resp.properties = MQTTProperties_copy(&unsub->properties);
|
||||
}
|
||||
resp.reasonCodeCount = unsub->reasonCodes->count;
|
||||
resp.reasonCode = *(int*)unsub->reasonCodes->first->content;
|
||||
if (unsub->reasonCodes->count > 1)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
int rc_count = 0;
|
||||
|
||||
if ((resp.reasonCodes = malloc(sizeof(enum MQTTReasonCodes) * (unsub->reasonCodes->count))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
while (ListNextElement(unsub->reasonCodes, ¤t))
|
||||
(resp.reasonCodes)[rc_count++] = *(enum MQTTReasonCodes*)(current->content);
|
||||
}
|
||||
}
|
||||
else
|
||||
resp.reasonCode = rc;
|
||||
rc = MQTTProtocol_handleUnsubacks(pack, m->c->net.socket);
|
||||
m->pack = NULL;
|
||||
}
|
||||
else
|
||||
rc = SOCKET_ERROR;
|
||||
}
|
||||
|
||||
if (rc == SOCKET_ERROR)
|
||||
MQTTClient_disconnect_internal(handle, 0);
|
||||
|
||||
exit:
|
||||
if (rc < 0)
|
||||
resp.reasonCode = rc;
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
Paho_thread_unlock_mutex(subscribe_mutex);
|
||||
FUNC_EXIT_RC(resp.reasonCode);
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char* const* topic)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
MQTTResponse response = MQTTResponse_initializer;
|
||||
|
||||
if (m != NULL && m->c != NULL && m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
response.reasonCode = MQTTCLIENT_WRONG_MQTT_VERSION;
|
||||
else
|
||||
response = MQTTClient_unsubscribeMany5(handle, count, topic, NULL);
|
||||
|
||||
return response.reasonCode;
|
||||
}
|
||||
|
||||
|
||||
MQTTResponse MQTTClient_unsubscribe5(MQTTClient handle, const char* topic, MQTTProperties* props)
|
||||
{
|
||||
MQTTResponse rc;
|
||||
|
||||
rc = MQTTClient_unsubscribeMany5(handle, 1, (char * const *)(&topic), props);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_unsubscribe(MQTTClient handle, const char* topic)
|
||||
{
|
||||
MQTTResponse response = MQTTClient_unsubscribe5(handle, topic, NULL);
|
||||
|
||||
return response.reasonCode;
|
||||
}
|
||||
|
||||
|
||||
MQTTResponse MQTTClient_publish5(MQTTClient handle, const char* topicName, int payloadlen, const void* payload,
|
||||
int qos, int retained, MQTTProperties* properties, MQTTClient_deliveryToken* deliveryToken)
|
||||
{
|
||||
int rc = MQTTCLIENT_SUCCESS;
|
||||
MQTTClients* m = handle;
|
||||
Messages* msg = NULL;
|
||||
Publish* p = NULL;
|
||||
int blocked = 0;
|
||||
int msgid = 0;
|
||||
MQTTResponse resp = MQTTResponse_initializer;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
if (m == NULL || m->c == NULL)
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
else if (m->c->connected == 0)
|
||||
rc = MQTTCLIENT_DISCONNECTED;
|
||||
else if (!UTF8_validateString(topicName))
|
||||
rc = MQTTCLIENT_BAD_UTF8_STRING;
|
||||
|
||||
if (rc != MQTTCLIENT_SUCCESS)
|
||||
goto exit;
|
||||
|
||||
/* If outbound queue is full, block until it is not */
|
||||
while (m->c->outboundMsgs->count >= m->c->maxInflightMessages ||
|
||||
Socket_noPendingWrites(m->c->net.socket) == 0) /* wait until the socket is free of large packets being written */
|
||||
{
|
||||
if (blocked == 0)
|
||||
{
|
||||
blocked = 1;
|
||||
Log(TRACE_MIN, -1, "Blocking publish on queue full for client %s", m->c->clientID);
|
||||
}
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_yield();
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (m->c->connected == 0)
|
||||
{
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (blocked == 1)
|
||||
Log(TRACE_MIN, -1, "Resuming publish now queue not full for client %s", m->c->clientID);
|
||||
if (qos > 0 && (msgid = MQTTProtocol_assignMsgId(m->c)) == 0)
|
||||
{ /* this should never happen as we've waited for spaces in the queue */
|
||||
rc = MQTTCLIENT_MAX_MESSAGES_INFLIGHT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((p = malloc(sizeof(Publish))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit_and_free;
|
||||
}
|
||||
memset(p->mask, '\0', sizeof(p->mask));
|
||||
p->payload = NULL;
|
||||
p->payloadlen = payloadlen;
|
||||
if (payloadlen > 0)
|
||||
{
|
||||
if ((p->payload = malloc(payloadlen)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit_and_free;
|
||||
}
|
||||
memcpy(p->payload, payload, payloadlen);
|
||||
}
|
||||
if ((p->topic = MQTTStrdup(topicName)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit_and_free;
|
||||
}
|
||||
p->msgId = msgid;
|
||||
p->MQTTVersion = m->c->MQTTVersion;
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
if (properties)
|
||||
p->properties = *properties;
|
||||
else
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
p->properties = props;
|
||||
}
|
||||
}
|
||||
|
||||
rc = MQTTProtocol_startPublish(m->c, p, qos, retained, &msg);
|
||||
|
||||
/* If the packet was partially written to the socket, wait for it to complete.
|
||||
* However, if the client is disconnected during this time and qos is not 0, still return success, as
|
||||
* the packet has already been written to persistence and assigned a message id so will
|
||||
* be sent when the client next connects.
|
||||
*/
|
||||
if (rc == TCPSOCKET_INTERRUPTED)
|
||||
{
|
||||
while (m->c->connected == 1)
|
||||
{
|
||||
pending_writes* writing = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(socket_mutex);
|
||||
writing = SocketBuffer_getWrite(m->c->net.socket);
|
||||
Paho_thread_unlock_mutex(socket_mutex);
|
||||
|
||||
if (writing == NULL)
|
||||
break;
|
||||
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_yield();
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
}
|
||||
rc = (qos > 0 || m->c->connected == 1) ? MQTTCLIENT_SUCCESS : MQTTCLIENT_FAILURE;
|
||||
}
|
||||
|
||||
if (deliveryToken && qos > 0)
|
||||
*deliveryToken = msg->msgid;
|
||||
|
||||
exit_and_free:
|
||||
if (p)
|
||||
{
|
||||
if (p->topic)
|
||||
free(p->topic);
|
||||
if (p->payload)
|
||||
free(p->payload);
|
||||
free(p);
|
||||
}
|
||||
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
MQTTClient_disconnect_internal(handle, 0);
|
||||
/* Return success for qos > 0 as the send will be retried automatically */
|
||||
rc = (qos > 0) ? MQTTCLIENT_SUCCESS : MQTTCLIENT_FAILURE;
|
||||
}
|
||||
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
resp.reasonCode = rc;
|
||||
FUNC_EXIT_RC(resp.reasonCode);
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_publish(MQTTClient handle, const char* topicName, int payloadlen, const void* payload,
|
||||
int qos, int retained, MQTTClient_deliveryToken* deliveryToken)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
MQTTResponse rc = MQTTResponse_initializer;
|
||||
|
||||
if (m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
rc.reasonCode = MQTTCLIENT_WRONG_MQTT_VERSION;
|
||||
else
|
||||
rc = MQTTClient_publish5(handle, topicName, payloadlen, payload, qos, retained, NULL, deliveryToken);
|
||||
return rc.reasonCode;
|
||||
}
|
||||
|
||||
|
||||
MQTTResponse MQTTClient_publishMessage5(MQTTClient handle, const char* topicName, MQTTClient_message* message,
|
||||
MQTTClient_deliveryToken* deliveryToken)
|
||||
{
|
||||
MQTTResponse rc = MQTTResponse_initializer;
|
||||
MQTTProperties* props = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (message == NULL)
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_NULL_PARAMETER;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strncmp(message->struct_id, "MQTM", 4) != 0 ||
|
||||
(message->struct_version != 0 && message->struct_version != 1))
|
||||
{
|
||||
rc.reasonCode = MQTTCLIENT_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (message->struct_version >= 1)
|
||||
props = &message->properties;
|
||||
|
||||
rc = MQTTClient_publish5(handle, topicName, message->payloadlen, message->payload,
|
||||
message->qos, message->retained, props, deliveryToken);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc.reasonCode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_publishMessage(MQTTClient handle, const char* topicName, MQTTClient_message* message,
|
||||
MQTTClient_deliveryToken* deliveryToken)
|
||||
{
|
||||
MQTTClients* m = handle;
|
||||
MQTTResponse rc = MQTTResponse_initializer;
|
||||
|
||||
if (strncmp(message->struct_id, "MQTM", 4) != 0 ||
|
||||
(message->struct_version != 0 && message->struct_version != 1))
|
||||
rc.reasonCode = MQTTCLIENT_BAD_STRUCTURE;
|
||||
else if (m != NULL && m->c != NULL && m->c->MQTTVersion >= MQTTVERSION_5)
|
||||
rc.reasonCode = MQTTCLIENT_WRONG_MQTT_VERSION;
|
||||
else
|
||||
rc = MQTTClient_publishMessage5(handle, topicName, message, deliveryToken);
|
||||
return rc.reasonCode;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTClient_retry(void)
|
||||
{
|
||||
static START_TIME_TYPE last = START_TIME_ZERO;
|
||||
START_TIME_TYPE now;
|
||||
|
||||
FUNC_ENTRY;
|
||||
now = MQTTTime_now();
|
||||
if (MQTTTime_difftime(now, last) >= (DIFF_TIME_TYPE)(retryLoopIntervalms))
|
||||
{
|
||||
last = MQTTTime_now();
|
||||
MQTTProtocol_keepalive(now);
|
||||
MQTTProtocol_retry(now, 1, 0);
|
||||
}
|
||||
else
|
||||
MQTTProtocol_retry(now, 0, 0);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static MQTTPacket* MQTTClient_cycle(SOCKET* sock, ELAPSED_TIME_TYPE timeout, int* rc)
|
||||
{
|
||||
static Ack ack;
|
||||
MQTTPacket* pack = NULL;
|
||||
int rc1 = 0;
|
||||
int interrupted = 0;
|
||||
START_TIME_TYPE start;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if defined(OPENSSL)
|
||||
if ((*sock = SSLSocket_getPendingRead()) == -1)
|
||||
{
|
||||
/* 0 from getReadySocket indicates no work to do, rc -1 == error */
|
||||
#endif
|
||||
start = MQTTTime_start_clock();
|
||||
*sock = Socket_getReadySocket(0, (int)timeout, socket_mutex, rc, &interrupted);
|
||||
*rc = rc1;
|
||||
if (*sock == 0 && timeout >= 100L && MQTTTime_elapsed(start) < (int64_t)10)
|
||||
MQTTTime_sleep(100L);
|
||||
#if defined(OPENSSL)
|
||||
}
|
||||
#endif
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (*sock > 0 && rc1 == 0)
|
||||
{
|
||||
MQTTClients* m = NULL;
|
||||
if (ListFindItem(handles, sock, clientSockCompare) != NULL)
|
||||
m = (MQTTClient)(handles->current->content);
|
||||
if (m != NULL)
|
||||
{
|
||||
if (m->c->connect_state == TCP_IN_PROGRESS || m->c->connect_state == SSL_IN_PROGRESS)
|
||||
*rc = 0; /* waiting for connect state to clear */
|
||||
else if (m->c->connect_state == WEBSOCKET_IN_PROGRESS)
|
||||
*rc = WebSocket_upgrade(&m->c->net);
|
||||
else
|
||||
{
|
||||
pack = MQTTPacket_Factory(m->c->MQTTVersion, &m->c->net, rc);
|
||||
if (*rc == TCPSOCKET_INTERRUPTED)
|
||||
*rc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pack)
|
||||
{
|
||||
int freed = 1;
|
||||
|
||||
/* Note that these handle... functions free the packet structure that they are dealing with */
|
||||
if (pack->header.bits.type == PUBLISH)
|
||||
*rc = MQTTProtocol_handlePublishes(pack, *sock);
|
||||
else if (pack->header.bits.type == PUBACK || pack->header.bits.type == PUBCOMP)
|
||||
{
|
||||
int msgid;
|
||||
|
||||
ack = (pack->header.bits.type == PUBCOMP) ? *(Pubcomp*)pack : *(Puback*)pack;
|
||||
msgid = ack.msgId;
|
||||
if (m && m->c->MQTTVersion >= MQTTVERSION_5 && m->published)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling published for client %s, msgid %d", m->c->clientID, msgid);
|
||||
(*(m->published))(m->published_context, msgid, pack->header.bits.type, &ack.properties, ack.rc);
|
||||
}
|
||||
*rc = (pack->header.bits.type == PUBCOMP) ?
|
||||
MQTTProtocol_handlePubcomps(pack, *sock, NULL) : MQTTProtocol_handlePubacks(pack, *sock, NULL);
|
||||
if (m && m->dc)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling deliveryComplete for client %s, msgid %d", m->c->clientID, msgid);
|
||||
(*(m->dc))(m->context, msgid);
|
||||
}
|
||||
}
|
||||
else if (pack->header.bits.type == PUBREC)
|
||||
{
|
||||
Pubrec* pubrec = (Pubrec*)pack;
|
||||
|
||||
if (m && m->c->MQTTVersion >= MQTTVERSION_5 && m->published && pubrec->rc >= MQTTREASONCODE_UNSPECIFIED_ERROR)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Calling published for client %s, msgid %d", m->c->clientID, ack.msgId);
|
||||
(*(m->published))(m->published_context, pubrec->msgId, pack->header.bits.type,
|
||||
&pubrec->properties, pubrec->rc);
|
||||
}
|
||||
*rc = MQTTProtocol_handlePubrecs(pack, *sock, NULL);
|
||||
}
|
||||
else if (pack->header.bits.type == PUBREL)
|
||||
*rc = MQTTProtocol_handlePubrels(pack, *sock);
|
||||
else if (pack->header.bits.type == PINGRESP)
|
||||
*rc = MQTTProtocol_handlePingresps(pack, *sock);
|
||||
else
|
||||
freed = 0;
|
||||
if (freed)
|
||||
pack = NULL;
|
||||
}
|
||||
}
|
||||
MQTTClient_retry();
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(*rc);
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
static MQTTPacket* MQTTClient_waitfor(MQTTClient handle, int packet_type, int* rc, int64_t timeout)
|
||||
{
|
||||
MQTTPacket* pack = NULL;
|
||||
MQTTClients* m = handle;
|
||||
START_TIME_TYPE start = MQTTTime_start_clock();
|
||||
int is_running = 0; /* local copy of running */
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (((MQTTClients*)handle) == NULL || timeout <= 0L)
|
||||
{
|
||||
*rc = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
is_running = running;
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
|
||||
if (is_running)
|
||||
{
|
||||
if (packet_type == CONNECT)
|
||||
{
|
||||
if ((*rc = Thread_wait_evt(m->connect_evt, (int)timeout)) == 0)
|
||||
*rc = m->rc;
|
||||
}
|
||||
else if (packet_type == CONNACK)
|
||||
*rc = Thread_wait_evt(m->connack_evt, (int)timeout);
|
||||
else if (packet_type == SUBACK)
|
||||
*rc = Thread_wait_evt(m->suback_evt, (int)timeout);
|
||||
else if (packet_type == UNSUBACK)
|
||||
*rc = Thread_wait_evt(m->unsuback_evt, (int)timeout);
|
||||
if (*rc == 0 && packet_type != CONNECT && m->pack == NULL)
|
||||
Log(LOG_ERROR, -1, "waitfor unexpectedly is NULL for client %s, packet_type %d, timeout %ld", m->c->clientID, packet_type, timeout);
|
||||
pack = m->pack;
|
||||
}
|
||||
else
|
||||
{
|
||||
*rc = TCPSOCKET_COMPLETE;
|
||||
while (1)
|
||||
{
|
||||
SOCKET sock = -1;
|
||||
pack = MQTTClient_cycle(&sock, 100L, rc);
|
||||
if (sock == m->c->net.socket)
|
||||
{
|
||||
if (*rc == SOCKET_ERROR)
|
||||
break;
|
||||
if (pack && (pack->header.bits.type == packet_type))
|
||||
break;
|
||||
if (m->c->connect_state == TCP_IN_PROGRESS)
|
||||
{
|
||||
int error;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
if ((*rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
|
||||
*rc = error;
|
||||
break;
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (m->c->connect_state == SSL_IN_PROGRESS)
|
||||
{
|
||||
|
||||
*rc = m->c->sslopts->struct_version >= 3 ?
|
||||
SSLSocket_connect(m->c->net.ssl, sock, m->currentServerURI,
|
||||
m->c->sslopts->verify, m->c->sslopts->ssl_error_cb, m->c->sslopts->ssl_error_context) :
|
||||
SSLSocket_connect(m->c->net.ssl, sock, m->currentServerURI,
|
||||
m->c->sslopts->verify, NULL, NULL);
|
||||
if (*rc == SSL_FATAL)
|
||||
break;
|
||||
else if (*rc == 1) /* rc == 1 means SSL connect has finished and succeeded */
|
||||
{
|
||||
if ((m->c->cleansession == 0 && m->c->cleanstart == 0) && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (m->c->connect_state == WEBSOCKET_IN_PROGRESS && *rc != TCPSOCKET_INTERRUPTED)
|
||||
{
|
||||
*rc = 1;
|
||||
break;
|
||||
}
|
||||
else if (m->c->connect_state == PROXY_CONNECT_IN_PROGRESS )
|
||||
{
|
||||
*rc = 1;
|
||||
break;
|
||||
}
|
||||
else if (m->c->connect_state == WAIT_FOR_CONNACK)
|
||||
{
|
||||
int error;
|
||||
socklen_t len = sizeof(error);
|
||||
if (getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len) == 0)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
*rc = error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MQTTTime_elapsed(start) > (uint64_t)timeout)
|
||||
{
|
||||
pack = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(*rc);
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_receive(MQTTClient handle, char** topicName, int* topicLen, MQTTClient_message** message,
|
||||
unsigned long timeout)
|
||||
{
|
||||
int rc = TCPSOCKET_COMPLETE;
|
||||
START_TIME_TYPE start = MQTTTime_start_clock();
|
||||
ELAPSED_TIME_TYPE elapsed = 0L;
|
||||
MQTTClients* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (m == NULL || m->c == NULL
|
||||
|| running) /* receive is not meant to be called in a multi-thread environment */
|
||||
{
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
if (m->c->connected == 0)
|
||||
{
|
||||
rc = MQTTCLIENT_DISCONNECTED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*topicName = NULL;
|
||||
*message = NULL;
|
||||
|
||||
/* if there is already a message waiting, don't hang around but still do some packet handling */
|
||||
if (m->c->messageQueue->count > 0)
|
||||
timeout = 0L;
|
||||
|
||||
elapsed = MQTTTime_elapsed(start);
|
||||
do
|
||||
{
|
||||
SOCKET sock = 0;
|
||||
MQTTClient_cycle(&sock, (timeout > elapsed) ? timeout - elapsed : 0L, &rc);
|
||||
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
if (ListFindItem(handles, &sock, clientSockCompare) && /* find client corresponding to socket */
|
||||
(MQTTClient)(handles->current->content) == handle)
|
||||
break; /* there was an error on the socket we are interested in */
|
||||
}
|
||||
elapsed = MQTTTime_elapsed(start);
|
||||
}
|
||||
while (elapsed < timeout && m->c->messageQueue->count == 0);
|
||||
|
||||
if (m->c->messageQueue->count > 0)
|
||||
rc = MQTTClient_deliverMessage(rc, m, topicName, topicLen, message);
|
||||
|
||||
if (rc == SOCKET_ERROR)
|
||||
MQTTClient_disconnect_internal(handle, 0);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void MQTTClient_yield(void)
|
||||
{
|
||||
START_TIME_TYPE start = MQTTTime_start_clock();
|
||||
ELAPSED_TIME_TYPE elapsed = 0L;
|
||||
ELAPSED_TIME_TYPE timeout = 100L;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (running) /* yield is not meant to be called in a multi-thread environment */
|
||||
{
|
||||
MQTTTime_sleep(timeout);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
elapsed = MQTTTime_elapsed(start);
|
||||
do
|
||||
{
|
||||
SOCKET sock = -1;
|
||||
MQTTClient_cycle(&sock, (timeout > elapsed) ? timeout - elapsed : 0L, &rc);
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
if (rc == SOCKET_ERROR && ListFindItem(handles, &sock, clientSockCompare))
|
||||
{
|
||||
MQTTClients* m = (MQTTClient)(handles->current->content);
|
||||
if (m->c->connect_state != DISCONNECTING)
|
||||
MQTTClient_disconnect_internal(m, 0);
|
||||
}
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
elapsed = MQTTTime_elapsed(start);
|
||||
}
|
||||
while (elapsed < timeout);
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
/*
|
||||
static int pubCompare(void* a, void* b)
|
||||
{
|
||||
Messages* msg = (Messages*)a;
|
||||
return msg->publish == (Publications*)b;
|
||||
}*/
|
||||
|
||||
|
||||
int MQTTClient_waitForCompletion(MQTTClient handle, MQTTClient_deliveryToken mdt, unsigned long timeout)
|
||||
{
|
||||
int rc = MQTTCLIENT_FAILURE;
|
||||
START_TIME_TYPE start = MQTTTime_start_clock();
|
||||
ELAPSED_TIME_TYPE elapsed = 0L;
|
||||
MQTTClients* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
elapsed = MQTTTime_elapsed(start);
|
||||
while (elapsed < timeout)
|
||||
{
|
||||
if (m == NULL || m->c == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
if (m->c->connected == 0)
|
||||
{
|
||||
rc = MQTTCLIENT_DISCONNECTED;
|
||||
goto exit;
|
||||
}
|
||||
if (ListFindItem(m->c->outboundMsgs, &mdt, messageIDCompare) == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_SUCCESS; /* well we couldn't find it */
|
||||
goto exit;
|
||||
}
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_yield();
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
elapsed = MQTTTime_elapsed(start);
|
||||
}
|
||||
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_getPendingDeliveryTokens(MQTTClient handle, MQTTClient_deliveryToken **tokens)
|
||||
{
|
||||
int rc = MQTTCLIENT_SUCCESS;
|
||||
MQTTClients* m = handle;
|
||||
*tokens = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
Paho_thread_lock_mutex(mqttclient_mutex);
|
||||
|
||||
if (m == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (m->c && m->c->outboundMsgs->count > 0)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
int count = 0;
|
||||
|
||||
*tokens = malloc(sizeof(MQTTClient_deliveryToken) * (m->c->outboundMsgs->count + 1));
|
||||
if (!*tokens)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
while (ListNextElement(m->c->outboundMsgs, ¤t))
|
||||
{
|
||||
Messages* m2 = (Messages*)(current->content);
|
||||
(*tokens)[count++] = m2->msgid;
|
||||
}
|
||||
(*tokens)[count] = -1;
|
||||
}
|
||||
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void MQTTClient_setTraceLevel(enum MQTTCLIENT_TRACE_LEVELS level)
|
||||
{
|
||||
Log_setTraceLevel((enum LOG_LEVELS)level);
|
||||
}
|
||||
|
||||
|
||||
void MQTTClient_setTraceCallback(MQTTClient_traceCallback* callback)
|
||||
{
|
||||
Log_setTraceCallback((Log_traceCallback*)callback);
|
||||
}
|
||||
|
||||
|
||||
int MQTTClient_setCommandTimeout(MQTTClient handle, unsigned long milliSeconds)
|
||||
{
|
||||
int rc = MQTTCLIENT_SUCCESS;
|
||||
MQTTClients* m = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (milliSeconds < 5000L)
|
||||
rc = MQTTCLIENT_FAILURE;
|
||||
else
|
||||
m->commandTimeout = milliSeconds;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
MQTTClient_nameValue* MQTTClient_getVersionInfo(void)
|
||||
{
|
||||
#define MAX_INFO_STRINGS 8
|
||||
static MQTTClient_nameValue libinfo[MAX_INFO_STRINGS + 1];
|
||||
int i = 0;
|
||||
|
||||
libinfo[i].name = "Product name";
|
||||
libinfo[i++].value = "Eclipse Paho Synchronous MQTT C Client Library";
|
||||
|
||||
libinfo[i].name = "Version";
|
||||
libinfo[i++].value = CLIENT_VERSION;
|
||||
|
||||
libinfo[i].name = "Build level";
|
||||
libinfo[i++].value = BUILD_TIMESTAMP;
|
||||
#if defined(OPENSSL)
|
||||
libinfo[i].name = "OpenSSL version";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_VERSION);
|
||||
|
||||
libinfo[i].name = "OpenSSL flags";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_CFLAGS);
|
||||
|
||||
libinfo[i].name = "OpenSSL build timestamp";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_BUILT_ON);
|
||||
|
||||
libinfo[i].name = "OpenSSL platform";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_PLATFORM);
|
||||
|
||||
libinfo[i].name = "OpenSSL directory";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_DIR);
|
||||
#endif
|
||||
libinfo[i].name = NULL;
|
||||
libinfo[i].value = NULL;
|
||||
return libinfo;
|
||||
}
|
||||
|
||||
|
||||
const char* MQTTClient_strerror(int code)
|
||||
{
|
||||
static char buf[30];
|
||||
int chars = 0;
|
||||
|
||||
switch (code) {
|
||||
case MQTTCLIENT_SUCCESS:
|
||||
return "Success";
|
||||
case MQTTCLIENT_FAILURE:
|
||||
return "Failure";
|
||||
case MQTTCLIENT_DISCONNECTED:
|
||||
return "Disconnected";
|
||||
case MQTTCLIENT_MAX_MESSAGES_INFLIGHT:
|
||||
return "Maximum in-flight messages amount reached";
|
||||
case MQTTCLIENT_BAD_UTF8_STRING:
|
||||
return "Invalid UTF8 string";
|
||||
case MQTTCLIENT_NULL_PARAMETER:
|
||||
return "Invalid (NULL) parameter";
|
||||
case MQTTCLIENT_TOPICNAME_TRUNCATED:
|
||||
return "Topic containing NULL characters has been truncated";
|
||||
case MQTTCLIENT_BAD_STRUCTURE:
|
||||
return "Bad structure";
|
||||
case MQTTCLIENT_BAD_QOS:
|
||||
return "Invalid QoS value";
|
||||
case MQTTCLIENT_SSL_NOT_SUPPORTED:
|
||||
return "SSL is not supported";
|
||||
case MQTTCLIENT_BAD_MQTT_VERSION:
|
||||
return "Unrecognized MQTT version";
|
||||
case MQTTCLIENT_BAD_PROTOCOL:
|
||||
return "Invalid protocol scheme";
|
||||
case MQTTCLIENT_BAD_MQTT_OPTION:
|
||||
return "Options for wrong MQTT version";
|
||||
case MQTTCLIENT_WRONG_MQTT_VERSION:
|
||||
return "Client created for another version of MQTT";
|
||||
case MQTTCLIENT_0_LEN_WILL_TOPIC:
|
||||
return "Zero length will topic on connect";
|
||||
}
|
||||
|
||||
chars = snprintf(buf, sizeof(buf), "Unknown error code %d", code);
|
||||
if (chars >= sizeof(buf))
|
||||
{
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See if any pending writes have been completed, and cleanup if so.
|
||||
* Cleaning up means removing any publication data that was stored because the write did
|
||||
* not originally complete.
|
||||
*/
|
||||
static void MQTTProtocol_checkPendingWrites(void)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (state.pending_writes.count > 0)
|
||||
{
|
||||
ListElement* le = state.pending_writes.first;
|
||||
while (le)
|
||||
{
|
||||
if (Socket_noPendingWrites(((pending_write*)(le->content))->socket))
|
||||
{
|
||||
MQTTProtocol_removePublication(((pending_write*)(le->content))->p);
|
||||
state.pending_writes.current = le;
|
||||
ListRemove(&(state.pending_writes), le->content); /* does NextElement itself */
|
||||
le = state.pending_writes.current;
|
||||
}
|
||||
else
|
||||
ListNextElement(&(state.pending_writes), &le);
|
||||
}
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTClient_writeComplete(SOCKET socket, int rc)
|
||||
{
|
||||
ListElement* found = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* a partial write is now complete for a socket - this will be on a publish*/
|
||||
|
||||
MQTTProtocol_checkPendingWrites();
|
||||
|
||||
/* find the client using this socket */
|
||||
if ((found = ListFindItem(handles, &socket, clientSockCompare)) != NULL)
|
||||
{
|
||||
MQTTClients* m = (MQTTClients*)(found->content);
|
||||
|
||||
m->c->net.lastSent = MQTTTime_now();
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTClient_writeContinue(SOCKET socket)
|
||||
{
|
||||
ListElement* found = NULL;
|
||||
|
||||
if ((found = ListFindItem(handles, &socket, clientSockCompare)) != NULL)
|
||||
{
|
||||
MQTTClients* m = (MQTTClients*)(found->content);
|
||||
|
||||
m->c->net.lastSent = MQTTTime_now();
|
||||
}
|
||||
}
|
||||
2004
3rd/paho.mqtt.c/src/MQTTClient.h
Normal file
2004
3rd/paho.mqtt.c/src/MQTTClient.h
Normal file
@@ -0,0 +1,2004 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2026 IBM Corp., Ian Craggs and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - multiple server connection support
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - remove const from eyecatchers #168
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @cond MQTTClient_internal
|
||||
* @mainpage MQTT Client Library Internals
|
||||
* In the beginning there was one MQTT C client library, MQTTClient, as implemented in MQTTClient.c
|
||||
* This library was designed to be easy to use for applications which didn't mind if some of the calls
|
||||
* blocked for a while. For instance, the MQTTClient_connect call will block until a successful
|
||||
* connection has completed, or a connection has failed, which could be as long as the "connection
|
||||
* timeout" interval, whose default is 30 seconds.
|
||||
*
|
||||
* However in mobile devices and other windowing environments, blocking on the GUI thread is a bad
|
||||
* thing as it causes the user interface to freeze. Hence a new API, MQTTAsync, implemented
|
||||
* in MQTTAsync.c, was devised. There are no blocking calls in this library, so it is well suited
|
||||
* to GUI and mobile environments, at the expense of some extra complexity.
|
||||
*
|
||||
* Both libraries are designed to be sparing in the use of threads. So multiple client objects are
|
||||
* handled by one or two threads, with a select call in Socket_getReadySocket(), used to determine
|
||||
* when a socket has incoming data. This API is thread safe: functions may be called by multiple application
|
||||
* threads, with the exception of ::MQTTClient_yield and ::MQTTClient_receive, which are intended
|
||||
* for single threaded environments only.
|
||||
*
|
||||
* @endcond
|
||||
* @cond MQTTClient_main
|
||||
* @mainpage MQTT Client library for C (MQTTClient)
|
||||
* © Copyright 2009, 2026 IBM Corp., Ian Craggs and others
|
||||
*
|
||||
* @brief An MQTT client library in C.
|
||||
*
|
||||
* Version 1.3.16
|
||||
*
|
||||
* These pages describe the original more synchronous API which might be
|
||||
* considered easier to use. Some of the calls will block. For the new
|
||||
* totally asynchronous API where no calls block, which is especially suitable
|
||||
* for use in windowed environments, see the
|
||||
* <a href="../../MQTTAsync/html/index.html">MQTT C Client Asynchronous API Documentation</a>.
|
||||
* The MQTTClient API is not thread safe, whereas the MQTTAsync API is.
|
||||
*
|
||||
* An MQTT client application connects to MQTT-capable servers.
|
||||
* A typical client is responsible for collecting information from a telemetry
|
||||
* device and publishing the information to the server. It can also subscribe
|
||||
* to topics, receive messages, and use this information to control the
|
||||
* telemetry device.
|
||||
*
|
||||
* MQTT clients implement the published MQTT v3 protocol. You can write your own
|
||||
* API to the MQTT protocol using the programming language and platform of your
|
||||
* choice. This can be time-consuming and error-prone.
|
||||
*
|
||||
* To simplify writing MQTT client applications, this library encapsulates
|
||||
* the MQTT v3 protocol for you. Using this library enables a fully functional
|
||||
* MQTT client application to be written in a few lines of code.
|
||||
* The information presented here documents the API provided
|
||||
* by the MQTT Client library for C.
|
||||
*
|
||||
* <b>Using the client</b><br>
|
||||
* Applications that use the client library typically use a similar structure:
|
||||
* <ul>
|
||||
* <li>Create a client object</li>
|
||||
* <li>Set the options to connect to an MQTT server</li>
|
||||
* <li>Set up callback functions if multi-threaded (asynchronous mode)
|
||||
* operation is being used (see @ref async).</li>
|
||||
* <li>Subscribe to any topics the client needs to receive</li>
|
||||
* <li>Repeat until finished:</li>
|
||||
* <ul>
|
||||
* <li>Publish any messages the client needs to</li>
|
||||
* <li>Handle any incoming messages</li>
|
||||
* </ul>
|
||||
* <li>Disconnect the client</li>
|
||||
* <li>Free any memory being used by the client</li>
|
||||
* </ul>
|
||||
* Some simple examples are shown here:
|
||||
* <ul>
|
||||
* <li>@ref pubsync</li>
|
||||
* <li>@ref pubasync</li>
|
||||
* <li>@ref subasync</li>
|
||||
* </ul>
|
||||
* Additional information about important concepts is provided here:
|
||||
* <ul>
|
||||
* <li>@ref async</li>
|
||||
* <li>@ref callbacks</li>
|
||||
* <li>@ref wildcard</li>
|
||||
* <li>@ref qos</li>
|
||||
* <li>@ref tracing</li>
|
||||
* <li>@ref HTTP_proxies</li>
|
||||
* </ul>
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/*
|
||||
/// @cond EXCLUDE
|
||||
*/
|
||||
#if !defined(MQTTCLIENT_H)
|
||||
#define MQTTCLIENT_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
/*
|
||||
/// @endcond
|
||||
*/
|
||||
|
||||
#include "MQTTExportDeclarations.h"
|
||||
|
||||
#include "MQTTProperties.h"
|
||||
#include "MQTTReasonCodes.h"
|
||||
#include "MQTTSubscribeOpts.h"
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
#include "MQTTClientPersistence.h"
|
||||
#else
|
||||
#define MQTTCLIENT_PERSISTENCE_NONE 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return code: No error. Indicates successful completion of an MQTT client
|
||||
* operation.
|
||||
*/
|
||||
#define MQTTCLIENT_SUCCESS 0
|
||||
/**
|
||||
* Return code: A generic error code indicating the failure of an MQTT client
|
||||
* operation.
|
||||
*/
|
||||
#define MQTTCLIENT_FAILURE -1
|
||||
|
||||
/* error code -2 is MQTTCLIENT_PERSISTENCE_ERROR */
|
||||
|
||||
/**
|
||||
* Return code: The client is disconnected.
|
||||
*/
|
||||
#define MQTTCLIENT_DISCONNECTED -3
|
||||
/**
|
||||
* Return code: The maximum number of messages allowed to be simultaneously
|
||||
* in-flight has been reached.
|
||||
*/
|
||||
#define MQTTCLIENT_MAX_MESSAGES_INFLIGHT -4
|
||||
/**
|
||||
* Return code: An invalid UTF-8 string has been detected.
|
||||
*/
|
||||
#define MQTTCLIENT_BAD_UTF8_STRING -5
|
||||
/**
|
||||
* Return code: A NULL parameter has been supplied when this is invalid.
|
||||
*/
|
||||
#define MQTTCLIENT_NULL_PARAMETER -6
|
||||
/**
|
||||
* Return code: The topic has been truncated (the topic string includes
|
||||
* embedded NULL characters). String functions will not access the full topic.
|
||||
* Use the topic length value to access the full topic.
|
||||
*/
|
||||
#define MQTTCLIENT_TOPICNAME_TRUNCATED -7
|
||||
/**
|
||||
* Return code: A structure parameter does not have the correct eyecatcher
|
||||
* and version number.
|
||||
*/
|
||||
#define MQTTCLIENT_BAD_STRUCTURE -8
|
||||
/**
|
||||
* Return code: A QoS value that falls outside of the acceptable range (0,1,2)
|
||||
*/
|
||||
#define MQTTCLIENT_BAD_QOS -9
|
||||
/**
|
||||
* Return code: Attempting SSL connection using non-SSL version of library
|
||||
*/
|
||||
#define MQTTCLIENT_SSL_NOT_SUPPORTED -10
|
||||
/**
|
||||
* Return code: unrecognized MQTT version
|
||||
*/
|
||||
#define MQTTCLIENT_BAD_MQTT_VERSION -11
|
||||
/**
|
||||
* Return code: protocol prefix in serverURI should be:
|
||||
* @li @em tcp:// or @em mqtt:// - Insecure TCP
|
||||
* @li @em ssl:// or @em tls:// or @em mqtts:// - Encrypted SSL/TLS
|
||||
* @li @em ws:// - Insecure websockets
|
||||
* @li @em wss:// - Secure web sockets
|
||||
* The TLS enabled prefixes (ssl, tls, mqtts, wss) are only valid if a TLS
|
||||
* version of the library is linked with.
|
||||
*/
|
||||
#define MQTTCLIENT_BAD_PROTOCOL -14
|
||||
/**
|
||||
* Return code: option not applicable to the requested version of MQTT
|
||||
*/
|
||||
#define MQTTCLIENT_BAD_MQTT_OPTION -15
|
||||
/**
|
||||
* Return code: call not applicable to the requested version of MQTT
|
||||
*/
|
||||
#define MQTTCLIENT_WRONG_MQTT_VERSION -16
|
||||
/**
|
||||
* Return code: 0 length will topic on connect
|
||||
*/
|
||||
#define MQTTCLIENT_0_LEN_WILL_TOPIC -17
|
||||
|
||||
|
||||
/**
|
||||
* Default MQTT version to connect with. Use 3.1.1 then fall back to 3.1
|
||||
*/
|
||||
#define MQTTVERSION_DEFAULT 0
|
||||
/**
|
||||
* MQTT version to connect with: 3.1
|
||||
*/
|
||||
#define MQTTVERSION_3_1 3
|
||||
/**
|
||||
* MQTT version to connect with: 3.1.1
|
||||
*/
|
||||
#define MQTTVERSION_3_1_1 4
|
||||
/**
|
||||
* MQTT version to connect with: 5
|
||||
*/
|
||||
#define MQTTVERSION_5 5
|
||||
/**
|
||||
* Bad return code from subscribe, as defined in the 3.1.1 specification
|
||||
*/
|
||||
#define MQTT_BAD_SUBSCRIBE 0x80
|
||||
|
||||
/**
|
||||
* Initialization options
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. Must be MQTG. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** 1 = we do openssl init, 0 = leave it to the application */
|
||||
int do_openssl_init;
|
||||
} MQTTClient_init_options;
|
||||
|
||||
#define MQTTClient_init_options_initializer { {'M', 'Q', 'T', 'G'}, 0, 0 }
|
||||
|
||||
/**
|
||||
* Global init of mqtt library. Call once on program start to set global behaviour.
|
||||
* do_openssl_init - if mqtt library should initialize OpenSSL (1) or rely on the caller to do it before using the library (0)
|
||||
*/
|
||||
LIBMQTT_API void MQTTClient_global_init(MQTTClient_init_options* inits);
|
||||
|
||||
/**
|
||||
* A handle representing an MQTT client. A valid client handle is available
|
||||
* following a successful call to MQTTClient_create().
|
||||
*/
|
||||
typedef void* MQTTClient;
|
||||
/**
|
||||
* A value representing an MQTT message. A delivery token is returned to the
|
||||
* client application when a message is published. The token can then be used to
|
||||
* check that the message was successfully delivered to its destination (see
|
||||
* MQTTClient_publish(),
|
||||
* MQTTClient_publishMessage(),
|
||||
* MQTTClient_deliveryComplete(),
|
||||
* MQTTClient_waitForCompletion() and
|
||||
* MQTTClient_getPendingDeliveryTokens()).
|
||||
*/
|
||||
typedef int MQTTClient_deliveryToken;
|
||||
typedef int MQTTClient_token;
|
||||
|
||||
/**
|
||||
* A structure representing the payload and attributes of an MQTT message. The
|
||||
* message topic is not part of this structure (see MQTTClient_publishMessage(),
|
||||
* MQTTClient_publish(), MQTTClient_receive(), MQTTClient_freeMessage()
|
||||
* and MQTTClient_messageArrived()).
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTM. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 or 1
|
||||
* 0 indicates no message properties */
|
||||
int struct_version;
|
||||
/** The length of the MQTT message payload in bytes. */
|
||||
int payloadlen;
|
||||
/** A pointer to the payload of the MQTT message. */
|
||||
void* payload;
|
||||
/**
|
||||
* The quality of service (QoS) assigned to the message.
|
||||
* There are three levels of QoS:
|
||||
* <DL>
|
||||
* <DT><B>QoS0</B></DT>
|
||||
* <DD>Fire and forget - the message may not be delivered</DD>
|
||||
* <DT><B>QoS1</B></DT>
|
||||
* <DD>At least once - the message will be delivered, but may be
|
||||
* delivered more than once in some circumstances.</DD>
|
||||
* <DT><B>QoS2</B></DT>
|
||||
* <DD>Once and one only - the message will be delivered exactly once.</DD>
|
||||
* </DL>
|
||||
*/
|
||||
int qos;
|
||||
/**
|
||||
* The retained flag serves two purposes depending on whether the message
|
||||
* it is associated with is being published or received.
|
||||
*
|
||||
* <b>retained = true</b><br>
|
||||
* For messages being published, a true setting indicates that the MQTT
|
||||
* server should retain a copy of the message. The message will then be
|
||||
* transmitted to new subscribers to a topic that matches the message topic.
|
||||
* For subscribers registering a new subscription, the flag being true
|
||||
* indicates that the received message is not a new one, but one that has
|
||||
* been retained by the MQTT server.
|
||||
*
|
||||
* <b>retained = false</b> <br>
|
||||
* For publishers, this indicates that this message should not be retained
|
||||
* by the MQTT server. For subscribers, a false setting indicates this is
|
||||
* a normal message, received as a result of it being published to the
|
||||
* server.
|
||||
*/
|
||||
int retained;
|
||||
/**
|
||||
* The dup flag indicates whether or not this message is a duplicate.
|
||||
* It is only meaningful when receiving QoS1 messages. When true, the
|
||||
* client application should take appropriate action to deal with the
|
||||
* duplicate message.
|
||||
*/
|
||||
int dup;
|
||||
/** The message identifier is normally reserved for internal use by the
|
||||
* MQTT client and server.
|
||||
*/
|
||||
int msgid;
|
||||
/**
|
||||
* The MQTT V5 properties associated with the message.
|
||||
*/
|
||||
MQTTProperties properties;
|
||||
} MQTTClient_message;
|
||||
|
||||
#define MQTTClient_message_initializer { {'M', 'Q', 'T', 'M'}, 1, 0, NULL, 0, 0, 0, 0, MQTTProperties_initializer }
|
||||
|
||||
/**
|
||||
* This is a callback function. The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* receipt of messages. The function is registered with the client library by
|
||||
* passing it as an argument to MQTTClient_setCallbacks(). It is
|
||||
* called by the client library when a new message that matches a client
|
||||
* subscription has been received from the server. This function is executed on
|
||||
* a separate thread to the one on which the client application is running.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTClient_setCallbacks(), which contains any application-specific context.
|
||||
* @param topicName The topic associated with the received message.
|
||||
* @param topicLen The length of the topic if there are one
|
||||
* more NULL characters embedded in <i>topicName</i>, otherwise <i>topicLen</i>
|
||||
* is 0. If <i>topicLen</i> is 0, the value returned by <i>strlen(topicName)</i>
|
||||
* can be trusted. If <i>topicLen</i> is greater than 0, the full topic name
|
||||
* can be retrieved by accessing <i>topicName</i> as a byte array of length
|
||||
* <i>topicLen</i>.
|
||||
* @param message The MQTTClient_message structure for the received message.
|
||||
* This structure contains the message payload and attributes.
|
||||
* @return This function must return 0 or 1 indicating whether or not
|
||||
* the message has been safely received by the client application. <br>
|
||||
* Returning 1 indicates that the message has been successfully handled.
|
||||
* To free the message storage, ::MQTTClient_freeMessage must be called.
|
||||
* To free the topic name storage, ::MQTTClient_free must be called.<br>
|
||||
* Returning 0 indicates that there was a problem. In this
|
||||
* case, the client library will reinvoke MQTTClient_messageArrived() to
|
||||
* attempt to deliver the message to the application again.
|
||||
* Do not free the message and topic storage when returning 0, otherwise
|
||||
* the redelivery will fail.
|
||||
*/
|
||||
typedef int MQTTClient_messageArrived(void* context, char* topicName, int topicLen, MQTTClient_message* message);
|
||||
|
||||
/**
|
||||
* This is a callback function. The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* notification of delivery of messages. The function is registered with the
|
||||
* client library by passing it as an argument to MQTTClient_setCallbacks().
|
||||
* It is called by the client library after the client application has
|
||||
* published a message to the server. It indicates that the necessary
|
||||
* handshaking and acknowledgements for the requested quality of service (see
|
||||
* MQTTClient_message.qos) have been completed. This function is executed on a
|
||||
* separate thread to the one on which the client application is running.
|
||||
* <b>Note:</b>MQTTClient_deliveryComplete() is not called when messages are
|
||||
* published at QoS0.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTClient_setCallbacks(), which contains any application-specific context.
|
||||
* @param dt The ::MQTTClient_deliveryToken associated with
|
||||
* the published message. Applications can check that all messages have been
|
||||
* correctly published by matching the delivery tokens returned from calls to
|
||||
* MQTTClient_publish() and MQTTClient_publishMessage() with the tokens passed
|
||||
* to this callback.
|
||||
*/
|
||||
typedef void MQTTClient_deliveryComplete(void* context, MQTTClient_deliveryToken dt);
|
||||
|
||||
/**
|
||||
* This is a callback function. The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* notification of the loss of connection to the server. The function is
|
||||
* registered with the client library by passing it as an argument to
|
||||
* MQTTClient_setCallbacks(). It is called by the client library if the client
|
||||
* loses its connection to the server. The client application must take
|
||||
* appropriate action, such as trying to reconnect or reporting the problem.
|
||||
* This function is executed on a separate thread to the one on which the
|
||||
* client application is running.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTClient_setCallbacks(), which contains any application-specific context.
|
||||
* @param cause The reason for the disconnection.
|
||||
* Currently, <i>cause</i> is always set to NULL.
|
||||
*/
|
||||
typedef void MQTTClient_connectionLost(void* context, char* cause);
|
||||
|
||||
/**
|
||||
* This function sets the callback functions for a specific client.
|
||||
* If your client application doesn't use a particular callback, set the
|
||||
* relevant parameter to NULL (except for message arrived, which must be given).
|
||||
* Calling MQTTClient_setCallbacks() puts the
|
||||
* client into multi-threaded mode. Any necessary message acknowledgements and
|
||||
* status communications are handled in the background without any intervention
|
||||
* from the client application. See @ref async for more information.
|
||||
*
|
||||
* <b>Note:</b> The MQTT client must be disconnected when this function is
|
||||
* called.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to each of the callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
* @param cl A pointer to an MQTTClient_connectionLost() callback
|
||||
* function. You can set this to NULL if your application doesn't handle
|
||||
* disconnections.
|
||||
* @param ma A pointer to an MQTTClient_messageArrived() callback
|
||||
* function. This callback function must be set when you call
|
||||
* MQTTClient_setCallbacks(), as otherwise there would be nowhere to deliver
|
||||
* any incoming messages.
|
||||
* @param dc A pointer to an MQTTClient_deliveryComplete() callback
|
||||
* function. You can set this to NULL if your application publishes
|
||||
* synchronously or if you do not want to check for successful delivery.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTCLIENT_FAILURE if an error occurred.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl,
|
||||
MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc);
|
||||
|
||||
|
||||
/**
|
||||
* This is a callback function, which will be called when the a disconnect
|
||||
* packet is received from the server. This applies to MQTT V5 and above only.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* ::MQTTClient_setDisconnected(), which contains any application-specific context.
|
||||
* @param properties The MQTT V5 properties received with the disconnect, if any.
|
||||
* @param reasonCode The MQTT V5 reason code received with the disconnect.
|
||||
* Currently, <i>cause</i> is always set to NULL.
|
||||
*/
|
||||
typedef void MQTTClient_disconnected(void* context, MQTTProperties* properties,
|
||||
enum MQTTReasonCodes reasonCode);
|
||||
|
||||
/**
|
||||
* Sets the MQTTClient_disconnected() callback function for a client. This will be called
|
||||
* if a disconnect packet is received from the server. Only valid for MQTT V5 and above.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param context A pointer to any application-specific context. The
|
||||
* the <i>context</i> pointer is passed to each of the callback functions to
|
||||
* provide access to the context information in the callback.
|
||||
* @param co A pointer to an MQTTClient_disconnected() callback
|
||||
* function. NULL removes the callback setting.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the callbacks were correctly set,
|
||||
* ::MQTTCLIENT_FAILURE if an error occurred.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_setDisconnected(MQTTClient handle, void* context, MQTTClient_disconnected* co);
|
||||
|
||||
/**
|
||||
* This is a callback function, the MQTT V5 version of MQTTClient_deliveryComplete().
|
||||
* The client application
|
||||
* must provide an implementation of this function to enable asynchronous
|
||||
* notification of the completed delivery of messages.
|
||||
* It is called by the client library after the client application has
|
||||
* published a message to the server. It indicates that the necessary
|
||||
* handshaking and acknowledgements for the requested quality of service (see
|
||||
* MQTTClient_message.qos) have been completed. This function is executed on a
|
||||
* separate thread to the one on which the client application is running.
|
||||
* <b>Note:</b> It is not called when messages are published at QoS0.
|
||||
* @param context A pointer to the <i>context</i> value originally passed to
|
||||
* MQTTClient_setCallbacks(), which contains any application-specific context.
|
||||
* @param dt The ::MQTTClient_deliveryToken associated with
|
||||
* the published message. Applications can check that all messages have been
|
||||
* correctly published by matching the delivery tokens returned from calls to
|
||||
* MQTTClient_publish() and MQTTClient_publishMessage() with the tokens passed
|
||||
* to this callback.
|
||||
* @param packet_type the last received packet type for this completion. For QoS 1
|
||||
* always PUBACK. For QoS 2 could be PUBREC or PUBCOMP.
|
||||
* @param properties the MQTT V5 properties returned with the last packet from the server
|
||||
* @param reasonCode the reason code returned from the server
|
||||
*/
|
||||
typedef void MQTTClient_published(void* context, int dt, int packet_type, MQTTProperties* properties,
|
||||
enum MQTTReasonCodes reasonCode);
|
||||
|
||||
LIBMQTT_API int MQTTClient_setPublished(MQTTClient handle, void* context, MQTTClient_published* co);
|
||||
|
||||
/**
|
||||
* This function creates an MQTT client ready for connection to the
|
||||
* specified server and using the specified persistent storage (see
|
||||
* MQTTClient_persistence). See also MQTTClient_destroy().
|
||||
* @param handle A pointer to an ::MQTTClient handle. The handle is
|
||||
* populated with a valid client reference following a successful return from
|
||||
* this function.
|
||||
* @param serverURI A null-terminated string specifying the server to
|
||||
* which the client will connect. It takes the form <i>protocol://host:port</i>.
|
||||
* Currently, <i>protocol</i> must be:
|
||||
* <br>
|
||||
* @em tcp:// or @em mqtt:// - Insecure TCP
|
||||
* <br>
|
||||
* @em ssl:// or @em tls:// or @em mqtts:// - Encrypted SSL/TLS
|
||||
* <br>
|
||||
* @em ws:// - Insecure websockets
|
||||
* <br>
|
||||
* @em wss:// - Secure web sockets
|
||||
* <br>
|
||||
* The TLS enabled prefixes (ssl, tls, mqtts, wss) are only valid if a TLS
|
||||
* version of the library is linked with.
|
||||
* For <i>host</i>, you can specify either an IP address or a host name. For
|
||||
* instance, to connect to a server running on the local machines with the
|
||||
* default MQTT port, specify <i>tcp://localhost:1883</i>.
|
||||
* @param clientId The client identifier passed to the server when the
|
||||
* client connects to it. It is a null-terminated UTF-8 encoded string.
|
||||
* @param persistence_type The type of persistence to be used by the client:
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_NONE: Use in-memory persistence. If the device or
|
||||
* system on which the client is running fails or is switched off, the current
|
||||
* state of any in-flight messages is lost and some messages may not be
|
||||
* delivered even at QoS1 and QoS2.
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_DEFAULT: Use the default (file system-based)
|
||||
* persistence mechanism. Status about in-flight messages is held in persistent
|
||||
* storage and provides some protection against message loss in the case of
|
||||
* unexpected failure.
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_USER: Use an application-specific persistence
|
||||
* implementation. Using this type of persistence gives control of the
|
||||
* persistence mechanism to the application. The application has to implement
|
||||
* the MQTTClient_persistence interface.
|
||||
* @param persistence_context If the application uses
|
||||
* ::MQTTCLIENT_PERSISTENCE_NONE persistence, this argument is unused and should
|
||||
* be set to NULL. For ::MQTTCLIENT_PERSISTENCE_DEFAULT persistence, it
|
||||
* should be set to the location of the persistence directory (if set
|
||||
* to NULL, the persistence directory used is the working directory).
|
||||
* Applications that use ::MQTTCLIENT_PERSISTENCE_USER persistence set this
|
||||
* argument to point to a valid MQTTClient_persistence structure.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the client is successfully created, otherwise
|
||||
* an error code is returned.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_create(MQTTClient* handle, const char* serverURI, const char* clientId,
|
||||
int persistence_type, void* persistence_context);
|
||||
|
||||
/** Options for the ::MQTTClient_createWithOptions call */
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQCO. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** Whether the MQTT version is 3.1, 3.1.1, or 5. To use V5, this must be set.
|
||||
* MQTT V5 has to be chosen here, because during the create call the message persistence
|
||||
* is initialized, and we want to know whether the format of any persisted messages
|
||||
* is appropriate for the MQTT version we are going to connect with. Selecting 3.1 or
|
||||
* 3.1.1 and attempting to read 5.0 persisted messages will result in an error on create. */
|
||||
int MQTTVersion;
|
||||
} MQTTClient_createOptions;
|
||||
|
||||
#define MQTTClient_createOptions_initializer { {'M', 'Q', 'C', 'O'}, 0, MQTTVERSION_DEFAULT }
|
||||
|
||||
/**
|
||||
* A version of :MQTTClient_create() with additional options.
|
||||
* This function creates an MQTT client ready for connection to the
|
||||
* specified server and using the specified persistent storage (see
|
||||
* MQTTClient_persistence). See also MQTTClient_destroy().
|
||||
* @param handle A pointer to an ::MQTTClient handle. The handle is
|
||||
* populated with a valid client reference following a successful return from
|
||||
* this function.
|
||||
* @param serverURI A null-terminated string specifying the server to
|
||||
* which the client will connect. It takes the form <i>protocol://host:port</i>.
|
||||
* Currently, <i>protocol</i> must be <i>tcp</i> or <i>ssl</i>.
|
||||
* For <i>host</i>, you can
|
||||
* specify either an IP address or a host name. For instance, to connect to
|
||||
* a server running on the local machines with the default MQTT port, specify
|
||||
* <i>tcp://localhost:1883</i>.
|
||||
* @param clientId The client identifier passed to the server when the
|
||||
* client connects to it. It is a null-terminated UTF-8 encoded string.
|
||||
* @param persistence_type The type of persistence to be used by the client:
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_NONE: Use in-memory persistence. If the device or
|
||||
* system on which the client is running fails or is switched off, the current
|
||||
* state of any in-flight messages is lost and some messages may not be
|
||||
* delivered even at QoS1 and QoS2.
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_DEFAULT: Use the default (file system-based)
|
||||
* persistence mechanism. Status about in-flight messages is held in persistent
|
||||
* storage and provides some protection against message loss in the case of
|
||||
* unexpected failure.
|
||||
* <br>
|
||||
* ::MQTTCLIENT_PERSISTENCE_USER: Use an application-specific persistence
|
||||
* implementation. Using this type of persistence gives control of the
|
||||
* persistence mechanism to the application. The application has to implement
|
||||
* the MQTTClient_persistence interface.
|
||||
* @param persistence_context If the application uses
|
||||
* ::MQTTCLIENT_PERSISTENCE_NONE persistence, this argument is unused and should
|
||||
* be set to NULL. For ::MQTTCLIENT_PERSISTENCE_DEFAULT persistence, it
|
||||
* should be set to the location of the persistence directory (if set
|
||||
* to NULL, the persistence directory used is the working directory).
|
||||
* Applications that use ::MQTTCLIENT_PERSISTENCE_USER persistence set this
|
||||
* argument to point to a valid MQTTClient_persistence structure.
|
||||
* @param options additional options for the create.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the client is successfully created, otherwise
|
||||
* an error code is returned.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_createWithOptions(MQTTClient* handle, const char* serverURI, const char* clientId,
|
||||
int persistence_type, void* persistence_context, MQTTClient_createOptions* options);
|
||||
|
||||
/**
|
||||
* MQTTClient_willOptions defines the MQTT "Last Will and Testament" (LWT) settings for
|
||||
* the client. In the event that a client unexpectedly loses its connection to
|
||||
* the server, the server publishes the LWT message to the LWT topic on
|
||||
* behalf of the client. This allows other clients (subscribed to the LWT topic)
|
||||
* to be made aware that the client has disconnected. To enable the LWT
|
||||
* function for a specific client, a valid pointer to an MQTTClient_willOptions
|
||||
* structure is passed in the MQTTClient_connectOptions structure used in the
|
||||
* MQTTClient_connect() call that connects the client to the server. The pointer
|
||||
* to MQTTClient_willOptions can be set to NULL if the LWT function is not
|
||||
* required.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTW. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 or 1
|
||||
0 means there is no binary payload option
|
||||
*/
|
||||
int struct_version;
|
||||
/** The LWT topic to which the LWT message will be published. */
|
||||
const char* topicName;
|
||||
/** The LWT payload in string form. */
|
||||
const char* message;
|
||||
/**
|
||||
* The retained flag for the LWT message (see MQTTClient_message.retained).
|
||||
*/
|
||||
int retained;
|
||||
/**
|
||||
* The quality of service setting for the LWT message (see
|
||||
* MQTTClient_message.qos and @ref qos).
|
||||
*/
|
||||
int qos;
|
||||
/** The LWT payload in binary form. This is only checked and used if the message option is NULL */
|
||||
struct
|
||||
{
|
||||
int len; /**< binary payload length */
|
||||
const void* data; /**< binary payload data */
|
||||
} payload;
|
||||
} MQTTClient_willOptions;
|
||||
|
||||
#define MQTTClient_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 1, NULL, NULL, 0, 0, {0, NULL} }
|
||||
|
||||
#define MQTT_SSL_VERSION_DEFAULT 0
|
||||
#define MQTT_SSL_VERSION_TLS_1_0 1
|
||||
#define MQTT_SSL_VERSION_TLS_1_1 2
|
||||
#define MQTT_SSL_VERSION_TLS_1_2 3
|
||||
|
||||
/**
|
||||
* MQTTClient_sslProperties defines the settings to establish an SSL/TLS connection using the
|
||||
* OpenSSL library. It covers the following scenarios:
|
||||
* - Server authentication: The client needs the digital certificate of the server. It is included
|
||||
* in a store containting trusted material (also known as "trust store").
|
||||
* - Mutual authentication: Both client and server are authenticated during the SSL handshake. In
|
||||
* addition to the digital certificate of the server in a trust store, the client will need its own
|
||||
* digital certificate and the private key used to sign its digital certificate stored in a "key store".
|
||||
* - Anonymous connection: Both client and server do not get authenticated and no credentials are needed
|
||||
* to establish an SSL connection. Note that this scenario is not fully secure since it is subject to
|
||||
* man-in-the-middle attacks.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. Must be MQTS */
|
||||
char struct_id[4];
|
||||
|
||||
/** The version number of this structure. Must be 0, 1, 2, 3, 4 or 5.
|
||||
* 0 means no sslVersion
|
||||
* 1 means no verify, CApath
|
||||
* 2 means no ssl_error_context, ssl_error_cb
|
||||
* 3 means no ssl_psk_cb, ssl_psk_context, disableDefaultTrustStore
|
||||
* 4 means no protos, protos_len
|
||||
*/
|
||||
int struct_version;
|
||||
|
||||
/** The file in PEM format containing the public digital certificates trusted by the client. */
|
||||
const char* trustStore;
|
||||
|
||||
/** The file in PEM format containing the public certificate chain of the client. It may also include
|
||||
* the client's private key.
|
||||
*/
|
||||
const char* keyStore;
|
||||
|
||||
/** If not included in the sslKeyStore, this setting points to the file in PEM format containing
|
||||
* the client's private key.
|
||||
*/
|
||||
const char* privateKey;
|
||||
|
||||
/** The password to load the client's privateKey if encrypted. */
|
||||
const char* privateKeyPassword;
|
||||
|
||||
/**
|
||||
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
|
||||
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
|
||||
* http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
|
||||
* If this setting is ommitted, its default value will be "ALL", that is, all the cipher suites -excluding
|
||||
* those offering no encryption- will be considered.
|
||||
* This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance).
|
||||
*/
|
||||
const char* enabledCipherSuites;
|
||||
|
||||
/** True/False option to enable verification of the server certificate **/
|
||||
int enableServerCertAuth;
|
||||
|
||||
/** The SSL/TLS version to use. Specify one of MQTT_SSL_VERSION_DEFAULT (0),
|
||||
* MQTT_SSL_VERSION_TLS_1_0 (1), MQTT_SSL_VERSION_TLS_1_1 (2) or MQTT_SSL_VERSION_TLS_1_2 (3).
|
||||
* Only used if struct_version is >= 1.
|
||||
*/
|
||||
int sslVersion;
|
||||
|
||||
/**
|
||||
* Whether to carry out post-connect checks, including that a certificate
|
||||
* matches the given host name.
|
||||
* Exists only if struct_version >= 2
|
||||
*/
|
||||
int verify;
|
||||
|
||||
/**
|
||||
* From the OpenSSL documentation:
|
||||
* If CApath is not NULL, it points to a directory containing CA certificates in PEM format.
|
||||
* Exists only if struct_version >= 2
|
||||
*/
|
||||
const char* CApath;
|
||||
|
||||
/**
|
||||
* Callback function for OpenSSL error handler ERR_print_errors_cb
|
||||
* Exists only if struct_version >= 3
|
||||
*/
|
||||
int (*ssl_error_cb) (const char *str, size_t len, void *u);
|
||||
|
||||
/**
|
||||
* Application-specific contex for OpenSSL error handler ERR_print_errors_cb
|
||||
* Exists only if struct_version >= 3
|
||||
*/
|
||||
void* ssl_error_context;
|
||||
|
||||
/**
|
||||
* Callback function for setting TLS-PSK options. Parameters correspond to that of
|
||||
* SSL_CTX_set_psk_client_callback, except for u which is the pointer ssl_psk_context.
|
||||
* Exists only if struct_version >= 4
|
||||
*/
|
||||
unsigned int (*ssl_psk_cb) (const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len, void *u);
|
||||
|
||||
/**
|
||||
* Application-specific contex for ssl_psk_cb
|
||||
* Exists only if struct_version >= 4
|
||||
*/
|
||||
void* ssl_psk_context;
|
||||
|
||||
/**
|
||||
* Don't load default SSL CA. Should be used together with PSK to make sure
|
||||
* regular servers with certificate in place is not accepted.
|
||||
* Exists only if struct_version >= 4
|
||||
*/
|
||||
int disableDefaultTrustStore;
|
||||
|
||||
/**
|
||||
* The protocol-lists must be in wire-format, which is defined as a vector of non-empty, 8-bit length-prefixed, byte strings.
|
||||
* The length-prefix byte is not included in the length. Each string is limited to 255 bytes. A byte-string length of 0 is invalid.
|
||||
* A truncated byte-string is invalid.
|
||||
* Check documentation for SSL_CTX_set_alpn_protos
|
||||
* Exists only if struct_version >= 5
|
||||
*/
|
||||
const unsigned char *protos;
|
||||
|
||||
/**
|
||||
* The length of the vector protos vector
|
||||
* Exists only if struct_version >= 5
|
||||
*/
|
||||
unsigned int protos_len;
|
||||
} MQTTClient_SSLOptions;
|
||||
|
||||
#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }
|
||||
|
||||
/**
|
||||
* MQTTClient_libraryInfo is used to store details relating to the currently used
|
||||
* library such as the version in use, the time it was built and relevant openSSL
|
||||
* options.
|
||||
* There is one static instance of this struct in MQTTClient.c
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* value;
|
||||
} MQTTClient_nameValue;
|
||||
|
||||
/**
|
||||
* This function returns version information about the library.
|
||||
* no trace information will be returned.
|
||||
* @return an array of strings describing the library. The last entry is a NULL pointer.
|
||||
*/
|
||||
LIBMQTT_API MQTTClient_nameValue* MQTTClient_getVersionInfo(void);
|
||||
|
||||
/**
|
||||
* MQTTClient_connectOptions defines several settings that control the way the
|
||||
* client connects to an MQTT server.
|
||||
*
|
||||
* <b>Note:</b> Default values are not defined for members of
|
||||
* MQTTClient_connectOptions so it is good practice to specify all settings.
|
||||
* If the MQTTClient_connectOptions structure is defined as an automatic
|
||||
* variable, all members are set to random values and thus must be set by the
|
||||
* client application. If the MQTTClient_connectOptions structure is defined
|
||||
* as a static variable, initialization (in compliant compilers) sets all
|
||||
* values to 0 (NULL for pointers). A #keepAliveInterval setting of 0 prevents
|
||||
* correct operation of the client and so you <b>must</b> at least set a value
|
||||
* for #keepAliveInterval.
|
||||
*
|
||||
* Suitable default values are set in the following initializers:
|
||||
* - MQTTClient_connectOptions_initializer: for MQTT 3.1.1 non-WebSockets
|
||||
* - MQTTClient_connectOptions_initializer5: for MQTT 5.0 non-WebSockets
|
||||
* - MQTTClient_connectOptions_initializer_ws: for MQTT 3.1.1 WebSockets
|
||||
* - MQTTClient_connectOptions_initializer5_ws: for MQTT 5.0 WebSockets
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTC. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0, 1, 2, 3, 4, 5, 6, 7 or 8.
|
||||
* 0 signifies no SSL options and no serverURIs
|
||||
* 1 signifies no serverURIs
|
||||
* 2 signifies no MQTTVersion
|
||||
* 3 signifies no returned values
|
||||
* 4 signifies no binary password option
|
||||
* 5 signifies no maxInflightMessages and cleanstart
|
||||
* 6 signifies no HTTP headers option
|
||||
* 7 signifies no HTTP proxy and HTTPS proxy options
|
||||
*/
|
||||
int struct_version;
|
||||
/** The "keep alive" interval, measured in seconds, defines the maximum time
|
||||
* that should pass without communication between the client and the server
|
||||
* The client will ensure that at least one message travels across the
|
||||
* network within each keep alive period. In the absence of a data-related
|
||||
* message during the time period, the client sends a very small MQTT
|
||||
* "ping" message, which the server will acknowledge. The keep alive
|
||||
* interval enables the client to detect when the server is no longer
|
||||
* available without having to wait for the long TCP/IP timeout.
|
||||
*/
|
||||
int keepAliveInterval;
|
||||
/**
|
||||
* This is a boolean value. The cleansession setting controls the behaviour
|
||||
* of both the client and the server at connection and disconnection time.
|
||||
* The client and server both maintain session state information. This
|
||||
* information is used to ensure "at least once" and "exactly once"
|
||||
* delivery, and "exactly once" receipt of messages. Session state also
|
||||
* includes subscriptions created by an MQTT client. You can choose to
|
||||
* maintain or discard state information between sessions.
|
||||
*
|
||||
* When cleansession is true, the state information is discarded at
|
||||
* connect and disconnect. Setting cleansession to false keeps the state
|
||||
* information. When you connect an MQTT client application with
|
||||
* MQTTClient_connect(), the client identifies the connection using the
|
||||
* client identifier and the address of the server. The server checks
|
||||
* whether session information for this client
|
||||
* has been saved from a previous connection to the server. If a previous
|
||||
* session still exists, and cleansession=true, then the previous session
|
||||
* information at the client and server is cleared. If cleansession=false,
|
||||
* the previous session is resumed. If no previous session exists, a new
|
||||
* session is started.
|
||||
*/
|
||||
int cleansession;
|
||||
/**
|
||||
* This is a boolean value that controls how many messages can be in-flight
|
||||
* simultaneously. Setting <i>reliable</i> to true means that a published
|
||||
* message must be completed (acknowledgements received) before another
|
||||
* can be sent. Attempts to publish additional messages receive an
|
||||
* ::MQTTCLIENT_MAX_MESSAGES_INFLIGHT return code. Setting this flag to
|
||||
* false allows up to 10 messages to be in-flight. This can increase
|
||||
* overall throughput in some circumstances.
|
||||
*/
|
||||
int reliable;
|
||||
/**
|
||||
* This is a pointer to an MQTTClient_willOptions structure. If your
|
||||
* application does not make use of the Last Will and Testament feature,
|
||||
* set this pointer to NULL.
|
||||
*/
|
||||
MQTTClient_willOptions* will;
|
||||
/**
|
||||
* MQTT servers that support the MQTT v3.1.1 protocol provide authentication
|
||||
* and authorisation by user name and password. This is the user name
|
||||
* parameter.
|
||||
*/
|
||||
const char* username;
|
||||
/**
|
||||
* MQTT servers that support the MQTT v3.1.1 protocol provide authentication
|
||||
* and authorisation by user name and password. This is the password
|
||||
* parameter.
|
||||
*/
|
||||
const char* password;
|
||||
/**
|
||||
* The time interval in seconds to allow a connect to complete.
|
||||
*/
|
||||
int connectTimeout;
|
||||
/**
|
||||
* The time interval in seconds after which unacknowledged publish requests are
|
||||
* retried during a TCP session. With MQTT 3.1.1 and later, retries are
|
||||
* not required except on reconnect. 0 turns off in-session retries, and is the
|
||||
* recommended setting. Adding retries to an already overloaded network only
|
||||
* exacerbates the problem.
|
||||
*/
|
||||
int retryInterval;
|
||||
/**
|
||||
* This is a pointer to an MQTTClient_SSLOptions structure. If your
|
||||
* application does not make use of SSL, set this pointer to NULL.
|
||||
*/
|
||||
MQTTClient_SSLOptions* ssl;
|
||||
/**
|
||||
* The number of entries in the optional serverURIs array. Defaults to 0.
|
||||
*/
|
||||
int serverURIcount;
|
||||
/**
|
||||
* An optional array of null-terminated strings specifying the servers to
|
||||
* which the client will connect. Each string takes the form <i>protocol://host:port</i>.
|
||||
* <i>protocol</i> must be <i>tcp</i>, <i>ssl</i>, <i>ws</i> or <i>wss</i>.
|
||||
* The TLS enabled prefixes (ssl, tls, wss) are only valid if a TLS version of the library
|
||||
* is linked with.
|
||||
* For <i>host</i>, you can
|
||||
* specify either an IP address or a host name. For instance, to connect to
|
||||
* a server running on the local machines with the default MQTT port, specify
|
||||
* <i>tcp://localhost:1883</i>.
|
||||
* If this list is empty (the default), the server URI specified on MQTTClient_create()
|
||||
* is used.
|
||||
*/
|
||||
char* const* serverURIs;
|
||||
/**
|
||||
* Sets the version of MQTT to be used on the connect.
|
||||
* MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if that fails, fall back to 3.1
|
||||
* MQTTVERSION_3_1 (3) = only try version 3.1
|
||||
* MQTTVERSION_3_1_1 (4) = only try version 3.1.1
|
||||
* MQTTVERSION_5 (5) = only try version 5.0
|
||||
*/
|
||||
int MQTTVersion;
|
||||
/**
|
||||
* Returned from the connect when the MQTT version used to connect is 3.1.1
|
||||
*/
|
||||
struct
|
||||
{
|
||||
const char* serverURI; /**< the serverURI connected to */
|
||||
int MQTTVersion; /**< the MQTT version used to connect with */
|
||||
int sessionPresent; /**< if the MQTT version is 3.1.1, the value of sessionPresent returned in the connack */
|
||||
} returned;
|
||||
/**
|
||||
* Optional binary password. Only checked and used if the password option is NULL
|
||||
*/
|
||||
struct
|
||||
{
|
||||
int len; /**< binary password length */
|
||||
const void* data; /**< binary password data */
|
||||
} binarypwd;
|
||||
/**
|
||||
* The maximum number of messages in flight
|
||||
*/
|
||||
int maxInflightMessages;
|
||||
/*
|
||||
* MQTT V5 clean start flag. Only clears state at the beginning of the session.
|
||||
*/
|
||||
int cleanstart;
|
||||
/**
|
||||
* HTTP headers for websockets
|
||||
*/
|
||||
const MQTTClient_nameValue* httpHeaders;
|
||||
/**
|
||||
* The string value of the HTTP proxy. Examples:
|
||||
* - http://your.proxy.server:8080/
|
||||
* - http://user:pass@my.proxy.server:8080/
|
||||
*/
|
||||
const char* httpProxy;
|
||||
/**
|
||||
* HTTPS proxy setting. See ::MQTTClient_connectOptions.httpProxy and the section @ref HTTP_proxies.
|
||||
*/
|
||||
const char* httpsProxy;
|
||||
} MQTTClient_connectOptions;
|
||||
|
||||
/** Initializer for connect options for MQTT 3.1.1 non-WebSocket connections */
|
||||
#define MQTTClient_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 8, 60, 1, 1, NULL, NULL, NULL, 30, 0, NULL,\
|
||||
0, NULL, MQTTVERSION_DEFAULT, {NULL, 0, 0}, {0, NULL}, -1, 0, NULL, NULL, NULL}
|
||||
|
||||
/** Initializer for connect options for MQTT 5.0 non-WebSocket connections */
|
||||
#define MQTTClient_connectOptions_initializer5 { {'M', 'Q', 'T', 'C'}, 8, 60, 0, 1, NULL, NULL, NULL, 30, 0, NULL,\
|
||||
0, NULL, MQTTVERSION_5, {NULL, 0, 0}, {0, NULL}, -1, 1, NULL, NULL, NULL}
|
||||
|
||||
/** Initializer for connect options for MQTT 3.1.1 WebSockets connections.
|
||||
* The keepalive interval is set to 45 seconds to avoid webserver 60 second inactivity timeouts.
|
||||
*/
|
||||
#define MQTTClient_connectOptions_initializer_ws { {'M', 'Q', 'T', 'C'}, 8, 45, 1, 1, NULL, NULL, NULL, 30, 0, NULL,\
|
||||
0, NULL, MQTTVERSION_DEFAULT, {NULL, 0, 0}, {0, NULL}, -1, 0, NULL, NULL, NULL}
|
||||
|
||||
/** Initializer for connect options for MQTT 5.0 WebSockets connections.
|
||||
* The keepalive interval is set to 45 seconds to avoid webserver 60 second inactivity timeouts.
|
||||
*/
|
||||
#define MQTTClient_connectOptions_initializer5_ws { {'M', 'Q', 'T', 'C'}, 8, 45, 0, 1, NULL, NULL, NULL, 30, 0, NULL,\
|
||||
0, NULL, MQTTVERSION_5, {NULL, 0, 0}, {0, NULL}, -1, 1, NULL, NULL, NULL}
|
||||
|
||||
/**
|
||||
* This function attempts to connect a previously-created client (see
|
||||
* MQTTClient_create()) to an MQTT server using the specified options. If you
|
||||
* want to enable asynchronous message and status notifications, you must call
|
||||
* MQTTClient_setCallbacks() prior to MQTTClient_connect().
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param options A pointer to a valid MQTTClient_connectOptions
|
||||
* structure.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the client successfully connects to the
|
||||
* server. An error code is returned if the client was unable to connect to
|
||||
* the server.
|
||||
* Error codes greater than 0 are returned by the MQTT protocol:<br><br>
|
||||
* <b>1</b>: Connection refused: Unacceptable protocol version<br>
|
||||
* <b>2</b>: Connection refused: Identifier rejected<br>
|
||||
* <b>3</b>: Connection refused: Server unavailable<br>
|
||||
* <b>4</b>: Connection refused: Bad user name or password<br>
|
||||
* <b>5</b>: Connection refused: Not authorized<br>
|
||||
* <b>6-255</b>: Reserved for future use<br>
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options);
|
||||
|
||||
/** MQTT version 5.0 response information */
|
||||
typedef struct MQTTResponse
|
||||
{
|
||||
int version; /* the version number of this structure */
|
||||
enum MQTTReasonCodes reasonCode; /* the MQTT 5.0 reason code returned */
|
||||
int reasonCodeCount; /* the number of reason codes. Used for subscribeMany5 and unsubscribeMany5 */
|
||||
enum MQTTReasonCodes* reasonCodes; /* a list of reason codes. Used for subscribeMany5 and unsubscribeMany5 */
|
||||
MQTTProperties* properties; /* optionally, the MQTT 5.0 properties returned */
|
||||
} MQTTResponse;
|
||||
|
||||
#define MQTTResponse_initializer {1, MQTTREASONCODE_SUCCESS, 0, NULL, NULL}
|
||||
|
||||
/**
|
||||
* Frees the storage associated with the MQTT response.
|
||||
* @param response the response structure to be freed
|
||||
*/
|
||||
LIBMQTT_API void MQTTResponse_free(MQTTResponse response);
|
||||
|
||||
/**
|
||||
* Attempts to connect a previously-created client (see
|
||||
* MQTTClient_create()) to an MQTT server using MQTT version 5.0 and the specified options. If you
|
||||
* want to enable asynchronous message and status notifications, you must call
|
||||
* MQTTClient_setCallbacks() prior to MQTTClient_connect().
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param options A pointer to a valid MQTTClient_connectOptions
|
||||
* structure.
|
||||
* @param connectProperties the MQTT 5.0 connect properties to use
|
||||
* @param willProperties the MQTT 5.0 properties to set on the will message
|
||||
* @return the MQTT 5.0 response information: error codes and properties.
|
||||
*/
|
||||
LIBMQTT_API MQTTResponse MQTTClient_connect5(MQTTClient handle, MQTTClient_connectOptions* options,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties);
|
||||
|
||||
/**
|
||||
* This function attempts to disconnect the client from the MQTT
|
||||
* server. In order to allow the client time to complete handling of messages
|
||||
* that are in-flight when this function is called, a timeout period is
|
||||
* specified. When the timeout period has expired, the client disconnects even
|
||||
* if there are still outstanding message acknowledgements.
|
||||
* The next time the client connects to the same server, any QoS 1 or 2
|
||||
* messages which have not completed will be retried depending on the
|
||||
* cleansession settings for both the previous and the new connection (see
|
||||
* MQTTClient_connectOptions.cleansession and MQTTClient_connect()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param timeout The client delays disconnection for up to this time (in
|
||||
* milliseconds) in order to allow in-flight message transfers to complete.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the client successfully disconnects from
|
||||
* the server. An error code is returned if the client was unable to disconnect
|
||||
* from the server
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_disconnect(MQTTClient handle, int timeout);
|
||||
|
||||
LIBMQTT_API int MQTTClient_disconnect5(MQTTClient handle, int timeout, enum MQTTReasonCodes reason, MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* This function allows the client application to test whether or not a
|
||||
* client is currently connected to the MQTT server.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @return Boolean true if the client is connected, otherwise false.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_isConnected(MQTTClient handle);
|
||||
|
||||
|
||||
/* Subscribe is synchronous. QoS list parameter is changed on return to granted QoSs.
|
||||
Returns return code, MQTTCLIENT_SUCCESS == success, non-zero some sort of error (TBD) */
|
||||
|
||||
/**
|
||||
* This function attempts to subscribe a client to a single topic, which may
|
||||
* contain wildcards (see @ref wildcard). This call also specifies the
|
||||
* @ref qos requested for the subscription
|
||||
* (see also MQTTClient_subscribeMany()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topic The subscription topic, which may include wildcards.
|
||||
* @param qos The requested quality of service for the subscription.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the subscription request is successful.
|
||||
* An error code is returned if there was a problem registering the
|
||||
* subscription.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_subscribe(MQTTClient handle, const char* topic, int qos);
|
||||
|
||||
/**
|
||||
* This function attempts to subscribe an MQTT version 5.0 client to a single topic, which may
|
||||
* contain wildcards (see @ref wildcard). This call also specifies the
|
||||
* @ref qos requested for the subscription
|
||||
* (see also MQTTClient_subscribeMany()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topic The subscription topic, which may include wildcards.
|
||||
* @param qos The requested quality of service for the subscription.
|
||||
* @param opts the MQTT 5.0 subscribe options to be used
|
||||
* @param props the MQTT 5.0 properties to be used
|
||||
* @return the MQTT 5.0 response information: error codes and properties.
|
||||
*/
|
||||
LIBMQTT_API MQTTResponse MQTTClient_subscribe5(MQTTClient handle, const char* topic, int qos,
|
||||
MQTTSubscribe_options* opts, MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* This function attempts to subscribe a client to a list of topics, which may
|
||||
* contain wildcards (see @ref wildcard). This call also specifies the
|
||||
* @ref qos requested for each topic (see also MQTTClient_subscribe()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param count The number of topics for which the client is requesting
|
||||
* subscriptions.
|
||||
* @param topic An array (of length <i>count</i>) of pointers to
|
||||
* topics, each of which may include wildcards.
|
||||
* @param qos An array (of length <i>count</i>) of @ref qos
|
||||
* values. qos[n] is the requested QoS for topic[n].
|
||||
* @return ::MQTTCLIENT_SUCCESS if the subscription request is successful.
|
||||
* An error code is returned if there was a problem registering the
|
||||
* subscriptions.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_subscribeMany(MQTTClient handle, int count, char* const* topic, int* qos);
|
||||
|
||||
/**
|
||||
* This function attempts to subscribe an MQTT version 5.0 client to a list of topics, which may
|
||||
* contain wildcards (see @ref wildcard). This call also specifies the
|
||||
* @ref qos requested for each topic (see also MQTTClient_subscribe()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param count The number of topics for which the client is requesting
|
||||
* subscriptions.
|
||||
* @param topic An array (of length <i>count</i>) of pointers to
|
||||
* topics, each of which may include wildcards.
|
||||
* @param qos An array (of length <i>count</i>) of @ref qos
|
||||
* values. qos[n] is the requested QoS for topic[n].
|
||||
* @param opts the MQTT 5.0 subscribe options to be used
|
||||
* @param props the MQTT 5.0 properties to be used
|
||||
* @return the MQTT 5.0 response information: error codes and properties.
|
||||
*/
|
||||
LIBMQTT_API MQTTResponse MQTTClient_subscribeMany5(MQTTClient handle, int count, char* const* topic,
|
||||
int* qos, MQTTSubscribe_options* opts, MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* This function attempts to remove an existing subscription made by the
|
||||
* specified client.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topic The topic for the subscription to be removed, which may
|
||||
* include wildcards (see @ref wildcard).
|
||||
* @return ::MQTTCLIENT_SUCCESS if the subscription is removed.
|
||||
* An error code is returned if there was a problem removing the
|
||||
* subscription.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_unsubscribe(MQTTClient handle, const char* topic);
|
||||
|
||||
/**
|
||||
* This function attempts to remove an existing subscription made by the
|
||||
* specified client using MQTT 5.0.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topic The topic for the subscription to be removed, which may
|
||||
* include wildcards (see @ref wildcard).
|
||||
* @param props the MQTT 5.0 properties to be used
|
||||
* @return the MQTT 5.0 response information: error codes and properties.
|
||||
*/
|
||||
LIBMQTT_API MQTTResponse MQTTClient_unsubscribe5(MQTTClient handle, const char* topic, MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* This function attempts to remove existing subscriptions to a list of topics
|
||||
* made by the specified client.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param count The number subscriptions to be removed.
|
||||
* @param topic An array (of length <i>count</i>) of pointers to the topics of
|
||||
* the subscriptions to be removed, each of which may include wildcards.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the subscriptions are removed.
|
||||
* An error code is returned if there was a problem removing the subscriptions.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char* const* topic);
|
||||
|
||||
/**
|
||||
* This function attempts to remove existing subscriptions to a list of topics
|
||||
* made by the specified client using MQTT version 5.0.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param count The number subscriptions to be removed.
|
||||
* @param topic An array (of length <i>count</i>) of pointers to the topics of
|
||||
* the subscriptions to be removed, each of which may include wildcards.
|
||||
* @param props the MQTT 5.0 properties to be used
|
||||
* @return the MQTT 5.0 response information: error codes and properties.
|
||||
*/
|
||||
LIBMQTT_API MQTTResponse MQTTClient_unsubscribeMany5(MQTTClient handle, int count, char* const* topic, MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* This function attempts to publish a message to a given topic (see also
|
||||
* MQTTClient_publishMessage()). An ::MQTTClient_deliveryToken is issued when
|
||||
* this function returns successfully. If the client application needs to
|
||||
* test for succesful delivery of QoS1 and QoS2 messages, this can be done
|
||||
* either asynchronously or synchronously (see @ref async,
|
||||
* ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topicName The topic associated with this message.
|
||||
* @param payloadlen The length of the payload in bytes.
|
||||
* @param payload A pointer to the byte array payload of the message.
|
||||
* @param qos The @ref qos of the message.
|
||||
* @param retained The retained flag for the message.
|
||||
* @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated
|
||||
* with a token representing the message when the function returns
|
||||
* successfully. If your application does not use delivery tokens, set this
|
||||
* argument to NULL.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the message is accepted for publication.
|
||||
* An error code is returned if there was a problem accepting the message.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_publish(MQTTClient handle, const char* topicName, int payloadlen, const void* payload, int qos, int retained,
|
||||
MQTTClient_deliveryToken* dt);
|
||||
|
||||
/**
|
||||
* Attempts to publish a message to a given topic using MQTT version 5.0 (see also
|
||||
* MQTTClient_publishMessage5()). An ::MQTTClient_deliveryToken is issued when
|
||||
* this function returns successfully. If the client application needs to
|
||||
* test for succesful delivery of QoS1 and QoS2 messages, this can be done
|
||||
* either asynchronously or synchronously (see @ref async,
|
||||
* ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topicName The topic associated with this message.
|
||||
* @param payloadlen The length of the payload in bytes.
|
||||
* @param payload A pointer to the byte array payload of the message.
|
||||
* @param qos The @ref qos of the message.
|
||||
* @param retained The retained flag for the message.
|
||||
* @param properties the MQTT 5.0 properties to be used
|
||||
* @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated
|
||||
* with a token representing the message when the function returns
|
||||
* successfully. If your application does not use delivery tokens, set this
|
||||
* argument to NULL.
|
||||
* @return the MQTT 5.0 response information: error codes and properties.
|
||||
*/
|
||||
LIBMQTT_API MQTTResponse MQTTClient_publish5(MQTTClient handle, const char* topicName, int payloadlen, const void* payload,
|
||||
int qos, int retained, MQTTProperties* properties, MQTTClient_deliveryToken* dt);
|
||||
/**
|
||||
* This function attempts to publish a message to a given topic (see also
|
||||
* MQTTClient_publish()). An ::MQTTClient_deliveryToken is issued when
|
||||
* this function returns successfully. If the client application needs to
|
||||
* test for succesful delivery of QoS1 and QoS2 messages, this can be done
|
||||
* either asynchronously or synchronously (see @ref async,
|
||||
* ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topicName The topic associated with this message.
|
||||
* @param msg A pointer to a valid MQTTClient_message structure containing
|
||||
* the payload and attributes of the message to be published.
|
||||
* @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated
|
||||
* with a token representing the message when the function returns
|
||||
* successfully. If your application does not use delivery tokens, set this
|
||||
* argument to NULL.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the message is accepted for publication.
|
||||
* An error code is returned if there was a problem accepting the message.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_publishMessage(MQTTClient handle, const char* topicName, MQTTClient_message* msg, MQTTClient_deliveryToken* dt);
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to publish a message to the given topic using MQTT version 5.0
|
||||
* (see also
|
||||
* MQTTClient_publish5()). An ::MQTTClient_deliveryToken is issued when
|
||||
* this function returns successfully. If the client application needs to
|
||||
* test for succesful delivery of QoS1 and QoS2 messages, this can be done
|
||||
* either asynchronously or synchronously (see @ref async,
|
||||
* ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topicName The topic associated with this message.
|
||||
* @param msg A pointer to a valid MQTTClient_message structure containing
|
||||
* the payload and attributes of the message to be published.
|
||||
* @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated
|
||||
* with a token representing the message when the function returns
|
||||
* successfully. If your application does not use delivery tokens, set this
|
||||
* argument to NULL.
|
||||
* @return the MQTT 5.0 response information: error codes and properties.
|
||||
*/
|
||||
LIBMQTT_API MQTTResponse MQTTClient_publishMessage5(MQTTClient handle, const char* topicName, MQTTClient_message* msg,
|
||||
MQTTClient_deliveryToken* dt);
|
||||
|
||||
/**
|
||||
* This function is called by the client application to synchronize execution
|
||||
* of the main thread with completed publication of a message. When called,
|
||||
* MQTTClient_waitForCompletion() blocks execution until the message has been
|
||||
* successful delivered or the specified timeout has expired. See @ref async.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param dt The ::MQTTClient_deliveryToken that represents the message being
|
||||
* tested for successful delivery. Delivery tokens are issued by the
|
||||
* publishing functions MQTTClient_publish() and MQTTClient_publishMessage().
|
||||
* @param timeout The maximum time to wait in milliseconds.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the message was successfully delivered.
|
||||
* An error code is returned if the timeout expires or there was a problem
|
||||
* checking the token.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_waitForCompletion(MQTTClient handle, MQTTClient_deliveryToken dt, unsigned long timeout);
|
||||
|
||||
|
||||
/**
|
||||
* This function sets a pointer to an array of delivery tokens for
|
||||
* messages that are currently in-flight (pending completion).
|
||||
*
|
||||
* <b>Important note:</b> The memory used to hold the array of tokens is
|
||||
* malloc()'d in this function. The client application is responsible for
|
||||
* freeing this memory when it is no longer required.
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param tokens The address of a pointer to an ::MQTTClient_deliveryToken.
|
||||
* When the function returns successfully, the pointer is set to point to an
|
||||
* array of tokens representing messages pending completion. The last member of
|
||||
* the array is set to -1 to indicate there are no more tokens. If no tokens
|
||||
* are pending, the pointer is set to NULL.
|
||||
* @return ::MQTTCLIENT_SUCCESS if the function returns successfully.
|
||||
* An error code is returned if there was a problem obtaining the list of
|
||||
* pending tokens.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_getPendingDeliveryTokens(MQTTClient handle, MQTTClient_deliveryToken **tokens);
|
||||
|
||||
/**
|
||||
* When implementing a single-threaded client, call this function periodically
|
||||
* to allow processing of message retries and to send MQTT keepalive pings.
|
||||
* If the application is calling MQTTClient_receive() regularly, then it is
|
||||
* not necessary to call this function.
|
||||
*/
|
||||
LIBMQTT_API void MQTTClient_yield(void);
|
||||
|
||||
/**
|
||||
* This function performs a synchronous receive of incoming messages. It should
|
||||
* be used only when the client application has not set callback methods to
|
||||
* support asynchronous receipt of messages (see @ref async and
|
||||
* MQTTClient_setCallbacks()). Using this function allows a single-threaded
|
||||
* client subscriber application to be written. When called, this function
|
||||
* blocks until the next message arrives or the specified timeout expires
|
||||
*(see also MQTTClient_yield()).
|
||||
*
|
||||
* <b>Important note:</b> The application must free() the memory allocated
|
||||
* to the topic and the message when processing is complete (see
|
||||
* MQTTClient_freeMessage()).
|
||||
* @param handle A valid client handle from a successful call to
|
||||
* MQTTClient_create().
|
||||
* @param topicName The address of a pointer to a topic. This function
|
||||
* allocates the memory for the topic and returns it to the application
|
||||
* by setting <i>topicName</i> to point to the topic.
|
||||
* @param topicLen The length of the topic. If the return code from this
|
||||
* function is ::MQTTCLIENT_TOPICNAME_TRUNCATED, the topic contains embedded
|
||||
* NULL characters and the full topic should be retrieved by using
|
||||
* <i>topicLen</i>.
|
||||
* @param message The address of a pointer to the received message. This
|
||||
* function allocates the memory for the message and returns it to the
|
||||
* application by setting <i>message</i> to point to the received message.
|
||||
* The pointer is set to NULL if the timeout expires.
|
||||
* @param timeout The length of time to wait for a message in milliseconds.
|
||||
* @return ::MQTTCLIENT_SUCCESS or ::MQTTCLIENT_TOPICNAME_TRUNCATED if a
|
||||
* message is received. ::MQTTCLIENT_SUCCESS can also indicate that the
|
||||
* timeout expired, in which case <i>message</i> is NULL. An error code is
|
||||
* returned if there was a problem trying to receive a message.
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_receive(MQTTClient handle, char** topicName, int* topicLen, MQTTClient_message** message,
|
||||
unsigned long timeout);
|
||||
|
||||
/**
|
||||
* This function frees memory allocated to an MQTT message, including the
|
||||
* additional memory allocated to the message payload. The client application
|
||||
* calls this function when the message has been fully processed. <b>Important
|
||||
* note:</b> This function does not free the memory allocated to a message
|
||||
* topic string. It is the responsibility of the client application to free
|
||||
* this memory using the MQTTClient_free() library function.
|
||||
* @param msg The address of a pointer to the ::MQTTClient_message structure
|
||||
* to be freed.
|
||||
*/
|
||||
LIBMQTT_API void MQTTClient_freeMessage(MQTTClient_message** msg);
|
||||
|
||||
/**
|
||||
* This function frees memory allocated by the MQTT C client library, especially the
|
||||
* topic name. This is needed on Windows when the client libary and application
|
||||
* program have been compiled with different versions of the C compiler. It is
|
||||
* thus good policy to always use this function when freeing any MQTT C client-
|
||||
* allocated memory.
|
||||
* @param ptr The pointer to the client library storage to be freed.
|
||||
*/
|
||||
LIBMQTT_API void MQTTClient_free(void* ptr);
|
||||
|
||||
/**
|
||||
* This function is used to allocate memory to be used or freed by the MQTT C client library,
|
||||
* especially the data in user persistence. This is needed on Windows when the client library
|
||||
* and application program have been compiled with different versions of the C compiler.
|
||||
* @param size The size of the memory to be allocated.
|
||||
*/
|
||||
LIBMQTT_API void* MQTTClient_malloc(size_t size);
|
||||
|
||||
/**
|
||||
* This function frees the memory allocated to an MQTT client (see
|
||||
* MQTTClient_create()). It should be called when the client is no longer
|
||||
* required.
|
||||
* @param handle A pointer to the handle referring to the ::MQTTClient
|
||||
* structure to be freed.
|
||||
*/
|
||||
LIBMQTT_API void MQTTClient_destroy(MQTTClient* handle);
|
||||
|
||||
|
||||
enum MQTTCLIENT_TRACE_LEVELS
|
||||
{
|
||||
MQTTCLIENT_TRACE_MAXIMUM = 1,
|
||||
MQTTCLIENT_TRACE_MEDIUM,
|
||||
MQTTCLIENT_TRACE_MINIMUM,
|
||||
MQTTCLIENT_TRACE_PROTOCOL,
|
||||
MQTTCLIENT_TRACE_ERROR,
|
||||
MQTTCLIENT_TRACE_SEVERE,
|
||||
MQTTCLIENT_TRACE_FATAL,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function sets the level of trace information which will be
|
||||
* returned in the trace callback.
|
||||
* @param level the trace level required
|
||||
*/
|
||||
LIBMQTT_API void MQTTClient_setTraceLevel(enum MQTTCLIENT_TRACE_LEVELS level);
|
||||
|
||||
|
||||
/**
|
||||
* This is a callback function prototype which must be implemented if you want
|
||||
* to receive trace information. Do not invoke any other Paho API calls in this
|
||||
* callback function - unpredictable behavior may result.
|
||||
* @param level the trace level of the message returned
|
||||
* @param message the trace message. This is a pointer to a static buffer which
|
||||
* will be overwritten on each call. You must copy the data if you want to keep
|
||||
* it for later.
|
||||
*/
|
||||
typedef void MQTTClient_traceCallback(enum MQTTCLIENT_TRACE_LEVELS level, char* message);
|
||||
|
||||
/**
|
||||
* This function sets the trace callback if needed. If set to NULL,
|
||||
* no trace information will be returned. The default trace level is
|
||||
* MQTTASYNC_TRACE_MINIMUM.
|
||||
* @param callback a pointer to the function which will handle the trace information
|
||||
*/
|
||||
LIBMQTT_API void MQTTClient_setTraceCallback(MQTTClient_traceCallback* callback);
|
||||
|
||||
/**
|
||||
* Sets the timeout value for un/subscribe commands when waiting for the un/suback response from
|
||||
* the server. Values less than 5000 are not allowed.
|
||||
* @param handle A valid client handle from a successful call to MQTTClient_create().
|
||||
* @param milliSeconds the maximum number of milliseconds to wait
|
||||
* @return MQTTCLIENT_SUCCESS or MQTTCLIENT_FAILURE
|
||||
*/
|
||||
LIBMQTT_API int MQTTClient_setCommandTimeout(MQTTClient handle, unsigned long milliSeconds);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the string representation of the error or NULL.
|
||||
*
|
||||
* Do not free after use. Returns NULL if the error code is unknown.
|
||||
*/
|
||||
LIBMQTT_API const char* MQTTClient_strerror(int code);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* @cond MQTTClient_main
|
||||
* @page async Asynchronous vs synchronous client applications
|
||||
* This client library supports two modes of operation. These are referred to
|
||||
* as <b>synchronous</b> and <b>asynchronous</b> modes. If your application
|
||||
* calls MQTTClient_setCallbacks(), this puts the client into asynchronous
|
||||
* mode, otherwise it operates in synchronous mode.
|
||||
*
|
||||
* In synchronous mode, the client application runs on a single thread.
|
||||
* Messages are published using the MQTTClient_publish() and
|
||||
* MQTTClient_publishMessage() functions. To determine that a QoS1 or QoS2
|
||||
* (see @ref qos) message has been successfully delivered, the application
|
||||
* must call the MQTTClient_waitForCompletion() function. An example showing
|
||||
* synchronous publication is shown in @ref pubsync. Receiving messages in
|
||||
* synchronous mode uses the MQTTClient_receive() function. Client applications
|
||||
* must call either MQTTClient_receive() or MQTTClient_yield() relatively
|
||||
* frequently in order to allow processing of acknowledgements and the MQTT
|
||||
* "pings" that keep the network connection to the server alive.
|
||||
*
|
||||
* In asynchronous mode, the client application runs on several threads. The
|
||||
* main program calls functions in the client library to publish and subscribe,
|
||||
* just as for the synchronous mode. Processing of handshaking and maintaining
|
||||
* the network connection is performed in the background, however.
|
||||
* Notifications of status and message reception are provided to the client
|
||||
* application using callbacks registered with the library by the call to
|
||||
* MQTTClient_setCallbacks() (see MQTTClient_messageArrived(),
|
||||
* MQTTClient_connectionLost() and MQTTClient_deliveryComplete()).
|
||||
* This API is not thread safe however - it is not possible to call it from multiple
|
||||
* threads without synchronization. You can use the MQTTAsync API for that.
|
||||
*
|
||||
* @page callbacks Callbacks
|
||||
* You must not call a function from this API from within a callback otherwise
|
||||
* a deadlock might result. The only exception to this is the ability to call
|
||||
* connect within the connection lost callback, to allow a reconnect.
|
||||
*
|
||||
* When using MQTT 5.0, you can also call connect from within the disconnected
|
||||
* callback, which is invoked when the MQTT server sends a disconnect packet.
|
||||
* This server behaviour is allowed in MQTT 5.0, but not in MQTT 3.1.1, so the
|
||||
* disconnected callback will never be invoked if you use MQTT 3.1.1.
|
||||
*
|
||||
* In particular, you must not make a publish call within the message arrived callback.
|
||||
* These restrictions are all lifted in the
|
||||
* <a href="../../MQTTAsync/html/index.html">MQTTAsync API</a>.
|
||||
*
|
||||
* If no callbacks are assigned, this will include the message arrived callback.
|
||||
* This could be done if the application is a pure publisher, and does
|
||||
* not subscribe to any topics. If however messages are received, and no message
|
||||
* arrived callback is set, or receive not called, then those messages will accumulate
|
||||
* and take up memory, as there is no place for them to be delivered.
|
||||
* It is up to the application to protect against this situation.
|
||||
*
|
||||
* @page wildcard Subscription wildcards
|
||||
* Every MQTT message includes a topic that classifies it. MQTT servers use
|
||||
* topics to determine which subscribers should receive messages published to
|
||||
* the server.
|
||||
*
|
||||
* Consider the server receiving messages from several environmental sensors.
|
||||
* Each sensor publishes its measurement data as a message with an associated
|
||||
* topic. Subscribing applications need to know which sensor originally
|
||||
* published each received message. A unique topic is thus used to identify
|
||||
* each sensor and measurement type. Topics such as SENSOR1TEMP,
|
||||
* SENSOR1HUMIDITY, SENSOR2TEMP and so on achieve this but are not very
|
||||
* flexible. If additional sensors are added to the system at a later date,
|
||||
* subscribing applications must be modified to receive them.
|
||||
*
|
||||
* To provide more flexibility, MQTT supports a hierarchical topic namespace.
|
||||
* This allows application designers to organize topics to simplify their
|
||||
* management. Levels in the hierarchy are delimited by the '/' character,
|
||||
* such as SENSOR/1/HUMIDITY. Publishers and subscribers use these
|
||||
* hierarchical topics as already described.
|
||||
*
|
||||
* For subscriptions, two wildcard characters are supported:
|
||||
* <ul>
|
||||
* <li>A '#' character represents a complete sub-tree of the hierarchy and
|
||||
* thus must be the last character in a subscription topic string, such as
|
||||
* SENSOR/#. This will match any topic starting with SENSOR/, such as
|
||||
* SENSOR/1/TEMP and SENSOR/2/HUMIDITY.</li>
|
||||
* <li> A '+' character represents a single level of the hierarchy and is
|
||||
* used between delimiters. For example, SENSOR/+/TEMP will match
|
||||
* SENSOR/1/TEMP and SENSOR/2/TEMP.</li>
|
||||
* </ul>
|
||||
* Publishers are not allowed to use the wildcard characters in their topic
|
||||
* names.
|
||||
*
|
||||
* Deciding on your topic hierarchy is an important step in your system design.
|
||||
*
|
||||
* @page qos Quality of service
|
||||
* The MQTT protocol provides three qualities of service for delivering
|
||||
* messages between clients and servers: "at most once", "at least once" and
|
||||
* "exactly once".
|
||||
*
|
||||
* Quality of service (QoS) is an attribute of an individual message being
|
||||
* published. An application sets the QoS for a specific message by setting the
|
||||
* MQTTClient_message.qos field to the required value.
|
||||
*
|
||||
* A subscribing client can set the maximum quality of service a server uses
|
||||
* to send messages that match the client subscriptions. The
|
||||
* MQTTClient_subscribe() and MQTTClient_subscribeMany() functions set this
|
||||
* maximum. The QoS of a message forwarded to a subscriber thus might be
|
||||
* different to the QoS given to the message by the original publisher.
|
||||
* The lower of the two values is used to forward a message.
|
||||
*
|
||||
* The three levels are:
|
||||
*
|
||||
* <b>QoS0, At most once:</b> The message is delivered at most once, or it
|
||||
* may not be delivered at all. Its delivery across the network is not
|
||||
* acknowledged. The message is not stored. The message could be lost if the
|
||||
* client is disconnected, or if the server fails. QoS0 is the fastest mode of
|
||||
* transfer. It is sometimes called "fire and forget".
|
||||
*
|
||||
* The MQTT protocol does not require servers to forward publications at QoS0
|
||||
* to a client. If the client is disconnected at the time the server receives
|
||||
* the publication, the publication might be discarded, depending on the
|
||||
* server implementation.
|
||||
*
|
||||
* <b>QoS1, At least once:</b> The message is always delivered at least once.
|
||||
* It might be delivered multiple times if there is a failure before an
|
||||
* acknowledgment is received by the sender. The message must be stored
|
||||
* locally at the sender, until the sender receives confirmation that the
|
||||
* message has been published by the receiver. The message is stored in case
|
||||
* the message must be sent again.
|
||||
*
|
||||
* <b>QoS2, Exactly once:</b> The message is always delivered exactly once.
|
||||
* The message must be stored locally at the sender, until the sender receives
|
||||
* confirmation that the message has been published by the receiver. The
|
||||
* message is stored in case the message must be sent again. QoS2 is the
|
||||
* safest, but slowest mode of transfer. A more sophisticated handshaking
|
||||
* and acknowledgement sequence is used than for QoS1 to ensure no duplication
|
||||
* of messages occurs.
|
||||
* @page pubsync Synchronous publication example
|
||||
@code
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "MQTTClient.h"
|
||||
|
||||
#define ADDRESS "tcp://test.mosquitto.org:1883"
|
||||
#define CLIENTID "ExampleClientPub"
|
||||
#define TOPIC "MQTT Examples"
|
||||
#define PAYLOAD "Hello World!"
|
||||
#define QOS 1
|
||||
#define TIMEOUT 10000L
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
MQTTClient client;
|
||||
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
|
||||
MQTTClient_message pubmsg = MQTTClient_message_initializer;
|
||||
MQTTClient_deliveryToken token;
|
||||
int rc;
|
||||
|
||||
if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID,
|
||||
MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to create client, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
conn_opts.keepAliveInterval = 20;
|
||||
conn_opts.cleansession = 1;
|
||||
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to connect, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pubmsg.payload = PAYLOAD;
|
||||
pubmsg.payloadlen = (int)strlen(PAYLOAD);
|
||||
pubmsg.qos = QOS;
|
||||
pubmsg.retained = 0;
|
||||
if ((rc = MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to publish message, return code %d\n", rc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Waiting for up to %d seconds for publication of %s\n"
|
||||
"on topic %s for client with ClientID: %s\n",
|
||||
(int)(TIMEOUT/1000), PAYLOAD, TOPIC, CLIENTID);
|
||||
rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
|
||||
printf("Message with delivery token %d delivered\n", token);
|
||||
|
||||
if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS)
|
||||
printf("Failed to disconnect, return code %d\n", rc);
|
||||
MQTTClient_destroy(&client);
|
||||
return rc;
|
||||
}
|
||||
|
||||
* @endcode
|
||||
*
|
||||
* @page pubasync Asynchronous publication example
|
||||
@code{.c}
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "MQTTClient.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#define ADDRESS "tcp://test.mosquitto.org:1883"
|
||||
#define CLIENTID "ExampleClientPub"
|
||||
#define TOPIC "MQTT Examples"
|
||||
#define PAYLOAD "Hello World!"
|
||||
#define QOS 1
|
||||
#define TIMEOUT 10000L
|
||||
|
||||
MQTTClient_deliveryToken deliveredtoken;
|
||||
|
||||
void delivered(void *context, MQTTClient_deliveryToken dt)
|
||||
{
|
||||
printf("Message with token value %d delivery confirmed\n", dt);
|
||||
deliveredtoken = dt;
|
||||
}
|
||||
|
||||
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
|
||||
{
|
||||
printf("Message arrived\n");
|
||||
printf(" topic: %s\n", topicName);
|
||||
printf(" message: %.*s\n", message->payloadlen, (char*)message->payload);
|
||||
MQTTClient_freeMessage(&message);
|
||||
MQTTClient_free(topicName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void connlost(void *context, char *cause)
|
||||
{
|
||||
printf("\nConnection lost\n");
|
||||
printf(" cause: %s\n", cause);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
MQTTClient client;
|
||||
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
|
||||
MQTTClient_message pubmsg = MQTTClient_message_initializer;
|
||||
MQTTClient_deliveryToken token;
|
||||
int rc;
|
||||
|
||||
if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID,
|
||||
MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to create client, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to set callbacks, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto destroy_exit;
|
||||
}
|
||||
|
||||
conn_opts.keepAliveInterval = 20;
|
||||
conn_opts.cleansession = 1;
|
||||
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to connect, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto destroy_exit;
|
||||
}
|
||||
|
||||
pubmsg.payload = PAYLOAD;
|
||||
pubmsg.payloadlen = (int)strlen(PAYLOAD);
|
||||
pubmsg.qos = QOS;
|
||||
pubmsg.retained = 0;
|
||||
deliveredtoken = 0;
|
||||
if ((rc = MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to publish message, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Waiting for publication of %s\n"
|
||||
"on topic %s for client with ClientID: %s\n",
|
||||
PAYLOAD, TOPIC, CLIENTID);
|
||||
while (deliveredtoken != token)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
Sleep(100);
|
||||
#else
|
||||
usleep(10000L);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to disconnect, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
destroy_exit:
|
||||
MQTTClient_destroy(&client);
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
* @endcode
|
||||
* @page subasync Asynchronous subscription example
|
||||
@code
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "MQTTClient.h"
|
||||
|
||||
#define ADDRESS "tcp://test.mosquitto.org:1883"
|
||||
#define CLIENTID "ExampleClientSub"
|
||||
#define TOPIC "MQTT Examples"
|
||||
#define PAYLOAD "Hello World!"
|
||||
#define QOS 1
|
||||
#define TIMEOUT 10000L
|
||||
|
||||
volatile MQTTClient_deliveryToken deliveredtoken;
|
||||
|
||||
void delivered(void *context, MQTTClient_deliveryToken dt)
|
||||
{
|
||||
printf("Message with token value %d delivery confirmed\n", dt);
|
||||
deliveredtoken = dt;
|
||||
}
|
||||
|
||||
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
|
||||
{
|
||||
printf("Message arrived\n");
|
||||
printf(" topic: %s\n", topicName);
|
||||
printf(" message: %.*s\n", message->payloadlen, (char*)message->payload);
|
||||
MQTTClient_freeMessage(&message);
|
||||
MQTTClient_free(topicName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void connlost(void *context, char *cause)
|
||||
{
|
||||
printf("\nConnection lost\n");
|
||||
printf(" cause: %s\n", cause);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
MQTTClient client;
|
||||
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
|
||||
int rc;
|
||||
|
||||
if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID,
|
||||
MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to create client, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to set callbacks, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto destroy_exit;
|
||||
}
|
||||
|
||||
conn_opts.keepAliveInterval = 20;
|
||||
conn_opts.cleansession = 1;
|
||||
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to connect, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
goto destroy_exit;
|
||||
}
|
||||
|
||||
printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
|
||||
"Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
|
||||
if ((rc = MQTTClient_subscribe(client, TOPIC, QOS)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to subscribe, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ch;
|
||||
do
|
||||
{
|
||||
ch = getchar();
|
||||
} while (ch!='Q' && ch != 'q');
|
||||
|
||||
if ((rc = MQTTClient_unsubscribe(client, TOPIC)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to unsubscribe, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
printf("Failed to disconnect, return code %d\n", rc);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
destroy_exit:
|
||||
MQTTClient_destroy(&client);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
* @endcode
|
||||
* @page tracing Tracing
|
||||
*
|
||||
* Runtime tracing is controlled by environment variables.
|
||||
*
|
||||
* Tracing is switched on by setting MQTT_C_CLIENT_TRACE. A value of ON, or stdout, prints to
|
||||
* stdout, any other value is interpreted as a file name to use.
|
||||
*
|
||||
* The amount of trace detail is controlled with the MQTT_C_CLIENT_TRACE_LEVEL environment
|
||||
* variable - valid values are ERROR, PROTOCOL, MINIMUM, MEDIUM and MAXIMUM
|
||||
* (from least to most verbose).
|
||||
*
|
||||
* The variable MQTT_C_CLIENT_TRACE_MAX_LINES limits the number of lines of trace that are output
|
||||
* to a file. Two files are used at most, when they are full, the last one is overwritten with the
|
||||
* new trace entries. The default size is 1000 lines.
|
||||
*
|
||||
* ### MQTT Packet Tracing
|
||||
*
|
||||
* A feature that can be very useful is printing the MQTT packets that are sent and received. To
|
||||
* achieve this, use the following environment variable settings:
|
||||
* @code
|
||||
MQTT_C_CLIENT_TRACE=ON
|
||||
MQTT_C_CLIENT_TRACE_LEVEL=PROTOCOL
|
||||
* @endcode
|
||||
* The output you should see looks like this:
|
||||
* @code
|
||||
20130528 155936.813 3 stdout-subscriber -> CONNECT cleansession: 1 (0)
|
||||
20130528 155936.813 3 stdout-subscriber <- CONNACK rc: 0
|
||||
20130528 155936.813 3 stdout-subscriber -> SUBSCRIBE msgid: 1 (0)
|
||||
20130528 155936.813 3 stdout-subscriber <- SUBACK msgid: 1
|
||||
20130528 155941.818 3 stdout-subscriber -> DISCONNECT (0)
|
||||
* @endcode
|
||||
* where the fields are:
|
||||
* 1. date
|
||||
* 2. time
|
||||
* 3. socket number
|
||||
* 4. client id
|
||||
* 5. direction (-> from client to server, <- from server to client)
|
||||
* 6. packet details
|
||||
*
|
||||
* ### Default Level Tracing
|
||||
*
|
||||
* This is an extract of a default level trace of a call to connect:
|
||||
* @code
|
||||
19700101 010000.000 (1152206656) (0)> MQTTClient_connect:893
|
||||
19700101 010000.000 (1152206656) (1)> MQTTClient_connectURI:716
|
||||
20130528 160447.479 Connecting to serverURI localhost:1883
|
||||
20130528 160447.479 (1152206656) (2)> MQTTProtocol_connect:98
|
||||
20130528 160447.479 (1152206656) (3)> MQTTProtocol_addressPort:48
|
||||
20130528 160447.479 (1152206656) (3)< MQTTProtocol_addressPort:73
|
||||
20130528 160447.479 (1152206656) (3)> Socket_new:599
|
||||
20130528 160447.479 New socket 4 for localhost, port 1883
|
||||
20130528 160447.479 (1152206656) (4)> Socket_addSocket:163
|
||||
20130528 160447.479 (1152206656) (5)> Socket_setnonblocking:73
|
||||
20130528 160447.479 (1152206656) (5)< Socket_setnonblocking:78 (0)
|
||||
20130528 160447.479 (1152206656) (4)< Socket_addSocket:176 (0)
|
||||
20130528 160447.479 (1152206656) (4)> Socket_error:95
|
||||
20130528 160447.479 (1152206656) (4)< Socket_error:104 (115)
|
||||
20130528 160447.479 Connect pending
|
||||
20130528 160447.479 (1152206656) (3)< Socket_new:683 (115)
|
||||
20130528 160447.479 (1152206656) (2)< MQTTProtocol_connect:131 (115)
|
||||
* @endcode
|
||||
* where the fields are:
|
||||
* 1. date
|
||||
* 2. time
|
||||
* 3. thread id
|
||||
* 4. function nesting level
|
||||
* 5. function entry (>) or exit (<)
|
||||
* 6. function name : line of source code file
|
||||
* 7. return value (if there is one)
|
||||
*
|
||||
* ### Memory Allocation Tracing
|
||||
*
|
||||
* Setting the trace level to maximum causes memory allocations and frees to be traced along with
|
||||
* the default trace entries, with messages like the following:
|
||||
* @code
|
||||
20130528 161819.657 Allocating 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 177 ptr 0x179f930
|
||||
|
||||
20130528 161819.657 Freeing 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 201, heap use now 896 bytes
|
||||
* @endcode
|
||||
* When the last MQTT client object is destroyed, if the trace is being recorded
|
||||
* and all memory allocated by the client library has not been freed, an error message will be
|
||||
* written to the trace. This can help with fixing memory leaks. The message will look like this:
|
||||
* @code
|
||||
20130528 163909.208 Some memory not freed at shutdown, possible memory leak
|
||||
20130528 163909.208 Heap scan start, total 880 bytes
|
||||
20130528 163909.208 Heap element size 32, line 354, file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c, ptr 0x260cb00
|
||||
20130528 163909.208 Content
|
||||
20130528 163909.209 Heap scan end
|
||||
* @endcode
|
||||
* @endcond
|
||||
*
|
||||
* √* @page HTTP_proxies HTTP Proxies
|
||||
* The use of HTTP proxies can be controlled by environment variables or API calls.
|
||||
*
|
||||
* The ::MQTTClient_connectOptions.httpProxy and ::MQTTClient_connectOptions.httpsProxy fields
|
||||
* of the ::MQTTClient_connectOptions structure override any settings in the environment.
|
||||
*
|
||||
* If the environment variable PAHO_C_CLIENT_USE_HTTP_PROXY is set to TRUE, then the
|
||||
* http_proxy or https_proxy (lower case only) environment variables are used, for plain
|
||||
* TCP and TLS-secured connections respectively.
|
||||
*
|
||||
* The no_proxy environment variable can be used to exclude certain hosts from using an
|
||||
* environment variable chosen proxy. This does not apply to a proxy selected through the API.
|
||||
* The no_proxy environment variable is lower case only, and is a list of comma-separated
|
||||
* hostname:port values. Suffixes are matched (e.g. example.com will match test.example.com).
|
||||
*
|
||||
*/
|
||||
277
3rd/paho.mqtt.c/src/MQTTClientPersistence.h
Normal file
277
3rd/paho.mqtt.c/src/MQTTClientPersistence.h
Normal file
@@ -0,0 +1,277 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2020 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief This structure represents a persistent data store, used to store
|
||||
* outbound and inbound messages, in order to achieve reliable messaging.
|
||||
*
|
||||
* The MQTT Client persists QoS1 and QoS2 messages in order to meet the
|
||||
* assurances of delivery associated with these @ref qos levels. The messages
|
||||
* are saved in persistent storage
|
||||
* The type and context of the persistence implementation are specified when
|
||||
* the MQTT client is created (see MQTTClient_create()). The default
|
||||
* persistence type (::MQTTCLIENT_PERSISTENCE_DEFAULT) uses a file system-based
|
||||
* persistence mechanism. The <i>persistence_context</i> argument passed to
|
||||
* MQTTClient_create() when using the default peristence is a string
|
||||
* representing the location of the persistence directory. If the context
|
||||
* argument is NULL, the working directory will be used.
|
||||
*
|
||||
* To use memory-based persistence, an application passes
|
||||
* ::MQTTCLIENT_PERSISTENCE_NONE as the <i>persistence_type</i> to
|
||||
* MQTTClient_create(). This can lead to message loss in certain situations,
|
||||
* but can be appropriate in some cases (see @ref qos).
|
||||
*
|
||||
* Client applications can provide their own persistence mechanism by passing
|
||||
* ::MQTTCLIENT_PERSISTENCE_USER as the <i>persistence_type</i>. To implement a
|
||||
* custom persistence mechanism, the application must pass an initialized
|
||||
* ::MQTTClient_persistence structure as the <i>persistence_context</i>
|
||||
* argument to MQTTClient_create().
|
||||
*
|
||||
* If the functions defined return an ::MQTTCLIENT_PERSISTENCE_ERROR then the
|
||||
* state of the persisted data should remain as it was prior to the function
|
||||
* being called. For example, if Persistence_put() returns
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR, then it is assumed tha tthe persistent store
|
||||
* does not contain the data that was passed to the function. Similarly, if
|
||||
* Persistence_remove() returns ::MQTTCLIENT_PERSISTENCE_ERROR then it is
|
||||
* assumed that the data to be removed is still held in the persistent store.
|
||||
*
|
||||
* It is up to the persistence implementation to log any error information that
|
||||
* may be required to diagnose a persistence mechanism failure.
|
||||
*/
|
||||
|
||||
/*
|
||||
/// @cond EXCLUDE
|
||||
*/
|
||||
#if !defined(MQTTCLIENTPERSISTENCE_H)
|
||||
#define MQTTCLIENTPERSISTENCE_H
|
||||
/*
|
||||
/// @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* This <i>persistence_type</i> value specifies the default file system-based
|
||||
* persistence mechanism (see MQTTClient_create()).
|
||||
*/
|
||||
#define MQTTCLIENT_PERSISTENCE_DEFAULT 0
|
||||
/**
|
||||
* This <i>persistence_type</i> value specifies a memory-based
|
||||
* persistence mechanism (see MQTTClient_create()).
|
||||
*/
|
||||
#define MQTTCLIENT_PERSISTENCE_NONE 1
|
||||
/**
|
||||
* This <i>persistence_type</i> value specifies an application-specific
|
||||
* persistence mechanism (see MQTTClient_create()).
|
||||
*/
|
||||
#define MQTTCLIENT_PERSISTENCE_USER 2
|
||||
|
||||
/**
|
||||
* Application-specific persistence functions must return this error code if
|
||||
* there is a problem executing the function.
|
||||
*/
|
||||
#define MQTTCLIENT_PERSISTENCE_ERROR -2
|
||||
|
||||
/**
|
||||
* @brief Initialize the persistent store.
|
||||
*
|
||||
* Either open the existing persistent store for this client ID or create a new
|
||||
* one if one doesn't exist. If the persistent store is already open, return
|
||||
* without taking any action.
|
||||
*
|
||||
* An application can use the same client identifier to connect to many
|
||||
* different servers. The <i>clientid</i> in conjunction with the
|
||||
* <i>serverURI</i> uniquely identifies the persistence store required.
|
||||
*
|
||||
* @param handle The address of a pointer to a handle for this persistence
|
||||
* implementation. This function must set handle to a valid reference to the
|
||||
* persistence following a successful return.
|
||||
* The handle pointer is passed as an argument to all the other
|
||||
* persistence functions. It may include the context parameter and/or any other
|
||||
* data for use by the persistence functions.
|
||||
* @param clientID The client identifier for which the persistent store should
|
||||
* be opened.
|
||||
* @param serverURI The connection string specified when the MQTT client was
|
||||
* created (see MQTTClient_create()).
|
||||
* @param context A pointer to any data required to initialize the persistent
|
||||
* store (see ::MQTTClient_persistence).
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_open)(void** handle, const char* clientID, const char* serverURI, void* context);
|
||||
|
||||
/**
|
||||
* @brief Close the persistent store referred to by the handle.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_close)(void* handle);
|
||||
|
||||
/**
|
||||
* @brief Put the specified data into the persistent store.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param key A string used as the key for the data to be put in the store. The
|
||||
* key is later used to retrieve data from the store with Persistence_get().
|
||||
* @param bufcount The number of buffers to write to the persistence store.
|
||||
* @param buffers An array of pointers to the data buffers associated with
|
||||
* this <i>key</i>.
|
||||
* @param buflens An array of lengths of the data buffers. <i>buflen[n]</i>
|
||||
* gives the length of <i>buffer[n]</i>.
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_put)(void* handle, char* key, int bufcount, char* buffers[], int buflens[]);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the specified data from the persistent store.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param key A string that is the key for the data to be retrieved. This is
|
||||
* the same key used to save the data to the store with Persistence_put().
|
||||
* @param buffer The address of a pointer to a buffer. This function sets the
|
||||
* pointer to point at the retrieved data, if successful.
|
||||
* @param buflen The address of an int that is set to the length of
|
||||
* <i>buffer</i> by this function if successful.
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_get)(void* handle, char* key, char** buffer, int* buflen);
|
||||
|
||||
/**
|
||||
* @brief Remove the data for the specified key from the store.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param key A string that is the key for the data to be removed from the
|
||||
* store. This is the same key used to save the data to the store with
|
||||
* Persistence_put().
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_remove)(void* handle, char* key);
|
||||
|
||||
/**
|
||||
* @brief Returns the keys in this persistent data store.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param keys The address of a pointer to pointers to strings. Assuming
|
||||
* successful execution, this function allocates memory to hold the returned
|
||||
* keys (strings used to store the data with Persistence_put()). It also
|
||||
* allocates memory to hold an array of pointers to these strings. <i>keys</i>
|
||||
* is set to point to the array of pointers to strings.
|
||||
* @param nkeys A pointer to the number of keys in this persistent data store.
|
||||
* This function sets the number of keys, if successful.
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_keys)(void* handle, char*** keys, int* nkeys);
|
||||
|
||||
/**
|
||||
* @brief Clears the persistence store, so that it no longer contains any
|
||||
* persisted data.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_clear)(void* handle);
|
||||
|
||||
/**
|
||||
* @brief Returns whether any data has been persisted using the specified key.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param key The string to be tested for existence in the store.
|
||||
* @return Return 0 if the key was found in the store, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_containskey)(void* handle, char* key);
|
||||
|
||||
/**
|
||||
* @brief A structure containing the function pointers to a persistence
|
||||
* implementation and the context or state that will be shared across all
|
||||
* the persistence functions.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* A pointer to any data required to initialize the persistent store.
|
||||
*/
|
||||
void* context;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_open().
|
||||
*/
|
||||
Persistence_open popen;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_close().
|
||||
*/
|
||||
Persistence_close pclose;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_put().
|
||||
*/
|
||||
Persistence_put pput;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_get().
|
||||
*/
|
||||
Persistence_get pget;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_remove().
|
||||
*/
|
||||
Persistence_remove premove;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_keys().
|
||||
*/
|
||||
Persistence_keys pkeys;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_clear().
|
||||
*/
|
||||
Persistence_clear pclear;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_containskey().
|
||||
*/
|
||||
Persistence_containskey pcontainskey;
|
||||
} MQTTClient_persistence;
|
||||
|
||||
|
||||
/**
|
||||
* A callback which is invoked just before a write to persistence. This can be
|
||||
* used to transform the data, for instance to encrypt it.
|
||||
* @param context The context as set in ::MQTTAsync_setBeforePersistenceWrite
|
||||
* @param bufcount The number of buffers to write to the persistence store.
|
||||
* @param buffers An array of pointers to the data buffers.
|
||||
* @param buflens An array of lengths of the data buffers.
|
||||
* @return Return 0 if the function completes successfully, otherwise non 0.
|
||||
*/
|
||||
typedef int MQTTPersistence_beforeWrite(void* context, int bufcount, char* buffers[], int buflens[]);
|
||||
|
||||
|
||||
/**
|
||||
* A callback which is invoked just after a read from persistence. This can be
|
||||
* used to transform the data, for instance to decrypt it.
|
||||
* @param context The context as set in ::MQTTAsync_setAfterPersistenceRead
|
||||
* @param buffer The address of a pointer to a buffer.
|
||||
* @param buflen The address of an int that is the length of the buffer.
|
||||
* @return Return 0 if the function completes successfully, otherwise non 0.
|
||||
*/
|
||||
typedef int MQTTPersistence_afterRead(void* context, char** buffer, int* buflen);
|
||||
|
||||
#endif
|
||||
36
3rd/paho.mqtt.c/src/MQTTExportDeclarations.h
Normal file
36
3rd/paho.mqtt.c/src/MQTTExportDeclarations.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020, 2020 Andreas Walter
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Andreas Walter - initially moved export declarations into separate fle
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(EXPORTDECLARATIONS_H)
|
||||
#define EXPORTDECLARATIONS_H
|
||||
|
||||
#if defined(_WIN32)
|
||||
# if defined(PAHO_MQTT_EXPORTS)
|
||||
# define LIBMQTT_API __declspec(dllexport)
|
||||
# elif defined(PAHO_MQTT_IMPORTS)
|
||||
# define LIBMQTT_API __declspec(dllimport)
|
||||
# else
|
||||
# define LIBMQTT_API
|
||||
# endif
|
||||
#else
|
||||
# if defined(PAHO_MQTT_EXPORTS)
|
||||
# define LIBMQTT_API __attribute__ ((visibility ("default")))
|
||||
# else
|
||||
# define LIBMQTT_API extern
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1126
3rd/paho.mqtt.c/src/MQTTPacket.c
Normal file
1126
3rd/paho.mqtt.c/src/MQTTPacket.c
Normal file
@@ -0,0 +1,1126 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2026 IBM Corp. and Ian Craggs
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - fix for issue 453
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions to deal with reading and writing of MQTT packets from and to sockets
|
||||
*
|
||||
* Some other related functions are in the MQTTPacketOut module
|
||||
*/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "Log.h"
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
#include "MQTTPersistence.h"
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
#include "Messages.h"
|
||||
#include "StackTrace.h"
|
||||
#include "WebSocket.h"
|
||||
#include "MQTTTime.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
#if !defined(min)
|
||||
#define min(A,B) ( (A) < (B) ? (A):(B))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* List of the predefined MQTT v3/v5 packet names.
|
||||
*/
|
||||
static const char *packet_names[] =
|
||||
{
|
||||
"RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL",
|
||||
"PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK",
|
||||
"PINGREQ", "PINGRESP", "DISCONNECT", "AUTH"
|
||||
};
|
||||
|
||||
const char** MQTTClient_packet_names = packet_names;
|
||||
|
||||
|
||||
/**
|
||||
* Converts an MQTT packet code into its name
|
||||
* @param ptype packet code
|
||||
* @return the corresponding string, or "UNKNOWN"
|
||||
*/
|
||||
const char* MQTTPacket_name(int ptype)
|
||||
{
|
||||
return (ptype >= 0 && ptype <= AUTH) ? packet_names[ptype] : "UNKNOWN";
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of functions to build packets, indexed according to packet code
|
||||
*/
|
||||
pf new_packets[] =
|
||||
{
|
||||
NULL, /**< reserved */
|
||||
NULL, /**< MQTTPacket_connect*/
|
||||
MQTTPacket_connack, /**< CONNACK */
|
||||
MQTTPacket_publish, /**< PUBLISH */
|
||||
MQTTPacket_ack, /**< PUBACK */
|
||||
MQTTPacket_ack, /**< PUBREC */
|
||||
MQTTPacket_ack, /**< PUBREL */
|
||||
MQTTPacket_ack, /**< PUBCOMP */
|
||||
NULL, /**< MQTTPacket_subscribe*/
|
||||
MQTTPacket_suback, /**< SUBACK */
|
||||
NULL, /**< MQTTPacket_unsubscribe*/
|
||||
MQTTPacket_unsuback, /**< UNSUBACK */
|
||||
MQTTPacket_header_only, /**< PINGREQ */
|
||||
MQTTPacket_header_only, /**< PINGRESP */
|
||||
MQTTPacket_ack, /**< DISCONNECT */
|
||||
MQTTPacket_ack /**< AUTH */
|
||||
};
|
||||
|
||||
|
||||
static char* readUTFlen(char** pptr, char* enddata, int* len);
|
||||
static int MQTTPacket_send_ack(int MQTTVersion, int type, int msgid, int dup, networkHandles *net);
|
||||
|
||||
/**
|
||||
* Reads one MQTT packet from a socket.
|
||||
* @param socket a socket from which to read an MQTT packet
|
||||
* @param error pointer to the error code which is completed if no packet is returned
|
||||
* @return the packet structure or NULL if there was an error
|
||||
*/
|
||||
void* MQTTPacket_Factory(int MQTTVersion, networkHandles* net, int* error)
|
||||
{
|
||||
char* data = NULL;
|
||||
static Header header;
|
||||
size_t remaining_length;
|
||||
int ptype;
|
||||
void* pack = NULL;
|
||||
size_t actual_len = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
*error = SOCKET_ERROR; /* indicate whether an error occurred, or not */
|
||||
|
||||
const size_t headerWsFramePos = WebSocket_framePos();
|
||||
|
||||
/* read the packet data from the socket */
|
||||
*error = WebSocket_getch(net, &header.byte);
|
||||
if (*error != TCPSOCKET_COMPLETE) /* first byte is the header byte */
|
||||
goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
|
||||
|
||||
/* now read the remaining length, so we know how much more to read */
|
||||
if ((*error = MQTTPacket_decode(net, &remaining_length)) != TCPSOCKET_COMPLETE)
|
||||
goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
|
||||
|
||||
/* now read the rest, the variable header and payload */
|
||||
data = WebSocket_getdata(net, remaining_length, &actual_len);
|
||||
if (remaining_length && data == NULL)
|
||||
{
|
||||
*error = SOCKET_ERROR;
|
||||
goto exit; /* socket error */
|
||||
}
|
||||
|
||||
if (actual_len < remaining_length)
|
||||
{
|
||||
*error = TCPSOCKET_INTERRUPTED;
|
||||
net->lastReceived = MQTTTime_now();
|
||||
}
|
||||
else
|
||||
{
|
||||
ptype = header.bits.type;
|
||||
if (ptype < CONNECT || (MQTTVersion < MQTTVERSION_5 && ptype >= DISCONNECT) ||
|
||||
(MQTTVersion >= MQTTVERSION_5 && ptype > AUTH) ||
|
||||
new_packets[ptype] == NULL)
|
||||
Log(TRACE_MIN, 2, NULL, ptype);
|
||||
else
|
||||
{
|
||||
if ((pack = (*new_packets[ptype])(MQTTVersion, header.byte, data, remaining_length)) == NULL)
|
||||
{
|
||||
*error = SOCKET_ERROR; // was BAD_MQTT_PACKET;
|
||||
Log(LOG_ERROR, -1, "Bad MQTT packet, type %d", ptype);
|
||||
}
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
else if (header.bits.type == PUBLISH && header.bits.qos == 2)
|
||||
{
|
||||
int buf0len;
|
||||
char *buf = malloc(10);
|
||||
|
||||
if (buf == NULL)
|
||||
{
|
||||
*error = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
buf[0] = header.byte;
|
||||
buf0len = 1 + MQTTPacket_encode(&buf[1], remaining_length);
|
||||
*error = MQTTPersistence_putPacket(net->socket, buf, buf0len, 1,
|
||||
&data, &remaining_length, header.bits.type, ((Publish *)pack)->msgId, 1, MQTTVersion);
|
||||
free(buf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (pack)
|
||||
net->lastReceived = MQTTTime_now();
|
||||
exit:
|
||||
if (*error == TCPSOCKET_INTERRUPTED)
|
||||
WebSocket_framePosSeekTo(headerWsFramePos);
|
||||
|
||||
FUNC_EXIT_RC(*error);
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends an MQTT packet in one system call write
|
||||
* @param socket the socket to which to write the data
|
||||
* @param header the one-byte MQTT header
|
||||
* @param buffer the rest of the buffer to write (not including remaining length)
|
||||
* @param buflen the length of the data in buffer to be written
|
||||
* @param MQTTVersion the version of MQTT being used
|
||||
* @return the completion code (TCPSOCKET_COMPLETE etc)
|
||||
*/
|
||||
int MQTTPacket_send(networkHandles* net, Header header, char* buffer, size_t buflen, int freeData,
|
||||
int MQTTVersion)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
size_t buf0len;
|
||||
char *buf;
|
||||
PacketBuffers packetbufs;
|
||||
|
||||
FUNC_ENTRY;
|
||||
buf0len = 1 + MQTTPacket_encode(NULL, buflen);
|
||||
buf = malloc(buf0len);
|
||||
if (buf == NULL)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
buf[0] = header.byte;
|
||||
MQTTPacket_encode(&buf[1], buflen);
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (header.bits.type == PUBREL)
|
||||
{
|
||||
char* ptraux = buffer;
|
||||
int msgId = readInt(&ptraux);
|
||||
|
||||
rc = MQTTPersistence_putPacket(net->socket, buf, buf0len, 1, &buffer, &buflen,
|
||||
header.bits.type, msgId, 0, MQTTVersion);
|
||||
}
|
||||
#endif
|
||||
packetbufs.count = 1;
|
||||
packetbufs.buffers = &buffer;
|
||||
packetbufs.buflens = &buflen;
|
||||
packetbufs.frees = &freeData;
|
||||
memset(packetbufs.mask, '\0', sizeof(packetbufs.mask));
|
||||
rc = WebSocket_putdatas(net, &buf, &buf0len, &packetbufs);
|
||||
|
||||
if (rc == TCPSOCKET_COMPLETE)
|
||||
net->lastSent = MQTTTime_now();
|
||||
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sends an MQTT packet from multiple buffers in one system call write
|
||||
* @param socket the socket to which to write the data
|
||||
* @param header the one-byte MQTT header
|
||||
* @param count the number of buffers
|
||||
* @param buffers the rest of the buffers to write (not including remaining length)
|
||||
* @param buflens the lengths of the data in the array of buffers to be written
|
||||
* @param the MQTT version being used
|
||||
* @return the completion code (TCPSOCKET_COMPLETE etc)
|
||||
*/
|
||||
int MQTTPacket_sends(networkHandles* net, Header header, PacketBuffers* bufs, int MQTTVersion)
|
||||
{
|
||||
int i, rc = SOCKET_ERROR;
|
||||
size_t buf0len, total = 0;
|
||||
char *buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
for (i = 0; i < bufs->count; i++)
|
||||
total += bufs->buflens[i];
|
||||
buf0len = 1 + MQTTPacket_encode(NULL, total);
|
||||
buf = malloc(buf0len);
|
||||
if (buf == NULL)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
buf[0] = header.byte;
|
||||
MQTTPacket_encode(&buf[1], total);
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (header.bits.type == PUBLISH && header.bits.qos != 0)
|
||||
{ /* persist PUBLISH QoS1 and Qo2 */
|
||||
char *ptraux = bufs->buffers[2];
|
||||
int msgId = readInt(&ptraux);
|
||||
rc = MQTTPersistence_putPacket(net->socket, buf, buf0len, bufs->count, bufs->buffers, bufs->buflens,
|
||||
header.bits.type, msgId, 0, MQTTVersion);
|
||||
}
|
||||
#endif
|
||||
rc = WebSocket_putdatas(net, &buf, &buf0len, bufs);
|
||||
|
||||
if (rc == TCPSOCKET_COMPLETE)
|
||||
net->lastSent = MQTTTime_now();
|
||||
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes the message length according to the MQTT algorithm
|
||||
* @param buf the buffer into which the encoded data is written
|
||||
* @param length the length to be encoded
|
||||
* @return the number of bytes written to buffer
|
||||
*/
|
||||
int MQTTPacket_encode(char* buf, size_t length)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
do
|
||||
{
|
||||
char d = length % 128;
|
||||
length /= 128;
|
||||
/* if there are more digits to encode, set the top bit of this digit */
|
||||
if (length > 0)
|
||||
d |= 0x80;
|
||||
if (buf)
|
||||
buf[rc++] = d;
|
||||
else
|
||||
rc++;
|
||||
} while (length > 0);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm
|
||||
* @param socket the socket from which to read the bytes
|
||||
* @param value the decoded length returned
|
||||
* @return the number of bytes read from the socket
|
||||
*/
|
||||
int MQTTPacket_decode(networkHandles* net, size_t* value)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
char c;
|
||||
int multiplier = 1;
|
||||
int len = 0;
|
||||
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
|
||||
|
||||
FUNC_ENTRY;
|
||||
*value = 0;
|
||||
do
|
||||
{
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
{
|
||||
rc = SOCKET_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
rc = WebSocket_getch(net, &c);
|
||||
if (rc != TCPSOCKET_COMPLETE)
|
||||
goto exit;
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates an integer from two bytes read from the input buffer
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the integer value calculated
|
||||
*/
|
||||
int readInt(char** pptr)
|
||||
{
|
||||
char* ptr = *pptr;
|
||||
int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
|
||||
*pptr += 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means
|
||||
* a length delimited string. So it reads the two byte length then the data according to
|
||||
* that length. The end of the buffer is provided too, so we can prevent buffer overruns caused
|
||||
* by an incorrect length.
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @param enddata pointer to the end of the buffer not to be read beyond
|
||||
* @param len returns the calculcated value of the length bytes read
|
||||
* @return an allocated C string holding the characters read, or NULL if the length read would
|
||||
* have caused an overrun.
|
||||
*
|
||||
*/
|
||||
static char* readUTFlen(char** pptr, char* enddata, int* len)
|
||||
{
|
||||
char* string = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (enddata - (*pptr) > 1) /* enough length to read the integer? */
|
||||
{
|
||||
*len = readInt(pptr);
|
||||
if (&(*pptr)[*len] <= enddata)
|
||||
{
|
||||
if ((string = malloc(*len+1)) == NULL)
|
||||
goto exit;
|
||||
memcpy(string, *pptr, *len);
|
||||
string[*len] = '\0';
|
||||
*pptr += *len;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means
|
||||
* a length delimited string. So it reads the two byte length then the data according to
|
||||
* that length. The end of the buffer is provided too, so we can prevent buffer overruns caused
|
||||
* by an incorrect length.
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @param enddata pointer to the end of the buffer not to be read beyond
|
||||
* @return an allocated C string holding the characters read, or NULL if the length read would
|
||||
* have caused an overrun.
|
||||
*/
|
||||
char* readUTF(char** pptr, char* enddata)
|
||||
{
|
||||
int len;
|
||||
return readUTFlen(pptr, enddata, &len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads one character from the input buffer.
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the character read
|
||||
*/
|
||||
unsigned char readChar(char** pptr)
|
||||
{
|
||||
unsigned char c = **pptr;
|
||||
(*pptr)++;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes one character to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param c the character to write
|
||||
*/
|
||||
void writeChar(char** pptr, char c)
|
||||
{
|
||||
**pptr = c;
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes an integer as 2 bytes to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param anInt the integer to write
|
||||
*/
|
||||
void writeInt(char** pptr, int anInt)
|
||||
{
|
||||
**pptr = (char)(anInt / 256);
|
||||
(*pptr)++;
|
||||
**pptr = (char)(anInt % 256);
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes a "UTF" string to an output buffer. Converts C string to length-delimited.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param string the C string to write
|
||||
*/
|
||||
void writeUTF(char** pptr, const char* string)
|
||||
{
|
||||
size_t len = strlen(string);
|
||||
writeInt(pptr, (int)len);
|
||||
memcpy(*pptr, string, len);
|
||||
*pptr += len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes length delimited data to an output buffer
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param data the data to write
|
||||
* @param datalen the length of the data to write
|
||||
*/
|
||||
void writeData(char** pptr, const void* data, int datalen)
|
||||
{
|
||||
writeInt(pptr, datalen);
|
||||
memcpy(*pptr, data, datalen);
|
||||
*pptr += datalen;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create packets which have only a header.
|
||||
* @param MQTTVersion the version of MQTT
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_header_only(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
static unsigned char header = 0;
|
||||
header = aHeader;
|
||||
return &header;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT disconnect packet down a socket.
|
||||
* @param socket the open socket to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_disconnect(Clients* client, enum MQTTReasonCodes reason, MQTTProperties* props)
|
||||
{
|
||||
Header header;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = 0;
|
||||
header.bits.type = DISCONNECT;
|
||||
|
||||
if (client->MQTTVersion >= 5 && (props || reason != MQTTREASONCODE_SUCCESS))
|
||||
{
|
||||
size_t buflen = 1 + ((props == NULL) ? 0 : MQTTProperties_len(props));
|
||||
char *buf = NULL;
|
||||
char *ptr = NULL;
|
||||
|
||||
if ((buf = malloc(buflen)) == NULL)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
ptr = buf;
|
||||
writeChar(&ptr, reason);
|
||||
if (props)
|
||||
MQTTProperties_write(&ptr, props);
|
||||
if ((rc = MQTTPacket_send(&client->net, header, buf, buflen, 1,
|
||||
client->MQTTVersion)) != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
}
|
||||
else
|
||||
rc = MQTTPacket_send(&client->net, header, NULL, 0, 0, client->MQTTVersion);
|
||||
exit:
|
||||
Log(LOG_PROTOCOL, 28, NULL, client->net.socket, client->clientID, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create publish packets.
|
||||
* @param MQTTVersion
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_publish(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
Publish* pack = NULL;
|
||||
char* curdata = data;
|
||||
char* enddata = &data[datalen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((pack = malloc(sizeof(Publish))) == NULL)
|
||||
goto exit;
|
||||
memset(pack, '\0', sizeof(Publish));
|
||||
pack->MQTTVersion = MQTTVersion;
|
||||
pack->header.byte = aHeader;
|
||||
if ((pack->topic = readUTFlen(&curdata, enddata, &pack->topiclen)) == NULL) /* Topic name on which to publish */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
if (pack->header.bits.qos > 0) /* Msgid only exists for QoS 1 or 2 */
|
||||
{
|
||||
if (enddata - curdata < 2) /* Is there enough data for the msgid? */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
pack->msgId = readInt(&curdata);
|
||||
}
|
||||
else
|
||||
pack->msgId = 0;
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
pack->properties = props;
|
||||
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
pack->payload = curdata;
|
||||
pack->payloadlen = (int)(datalen-(curdata-data));
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free allocated storage for a publish packet.
|
||||
* @param pack pointer to the publish packet structure
|
||||
*/
|
||||
void MQTTPacket_freePublish(Publish* pack)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (pack->topic != NULL)
|
||||
free(pack->topic);
|
||||
if (pack->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_free(&pack->properties);
|
||||
free(pack);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free allocated storage for an ack packet.
|
||||
* @param pack pointer to the publish packet structure
|
||||
*/
|
||||
void MQTTPacket_freeAck(Ack* pack)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (pack->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_free(&pack->properties);
|
||||
free(pack);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT acknowledgement packet down a socket.
|
||||
* @param MQTTVersion the version of MQTT being used
|
||||
* @param type the MQTT packet type e.g. SUBACK
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param dup boolean - whether to set the MQTT DUP flag
|
||||
* @param net the network handle to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
static int MQTTPacket_send_ack(int MQTTVersion, int type, int msgid, int dup, networkHandles *net)
|
||||
{
|
||||
Header header;
|
||||
int rc = SOCKET_ERROR;
|
||||
char *buf = NULL;
|
||||
char *ptr = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((ptr = buf = malloc(2)) == NULL)
|
||||
goto exit;
|
||||
header.byte = 0;
|
||||
header.bits.type = type;
|
||||
header.bits.dup = dup;
|
||||
if (type == PUBREL)
|
||||
header.bits.qos = 1;
|
||||
writeInt(&ptr, msgid);
|
||||
if ((rc = MQTTPacket_send(net, header, buf, 2, 1, MQTTVersion)) != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT PUBACK packet down a socket.
|
||||
* @param MQTTVersion the version of MQTT being used
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_puback(int MQTTVersion, int msgid, networkHandles* net, const char* clientID)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_ack(MQTTVersion, PUBACK, msgid, 0, net);
|
||||
Log(LOG_PROTOCOL, 12, NULL, net->socket, clientID, msgid, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free allocated storage for a suback packet.
|
||||
* @param pack pointer to the suback packet structure
|
||||
*/
|
||||
void MQTTPacket_freeSuback(Suback* pack)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (pack->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_free(&pack->properties);
|
||||
if (pack->qoss != NULL)
|
||||
ListFree(pack->qoss);
|
||||
free(pack);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free allocated storage for a suback packet.
|
||||
* @param pack pointer to the suback packet structure
|
||||
*/
|
||||
void MQTTPacket_freeUnsuback(Unsuback* pack)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (pack->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
MQTTProperties_free(&pack->properties);
|
||||
if (pack->reasonCodes != NULL)
|
||||
ListFree(pack->reasonCodes);
|
||||
}
|
||||
free(pack);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT PUBREC packet down a socket.
|
||||
* @param MQTTVersion the version of MQTT being used
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pubrec(int MQTTVersion, int msgid, networkHandles* net, const char* clientID)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_ack(MQTTVersion, PUBREC, msgid, 0, net);
|
||||
Log(LOG_PROTOCOL, 13, NULL, net->socket, clientID, msgid, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT PUBREL packet down a socket.
|
||||
* @param MQTTVersion the version of MQTT being used
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param dup boolean - whether to set the MQTT DUP flag
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pubrel(int MQTTVersion, int msgid, int dup, networkHandles* net, const char* clientID)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_ack(MQTTVersion, PUBREL, msgid, dup, net);
|
||||
Log(LOG_PROTOCOL, 16, NULL, net->socket, clientID, msgid, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT PUBCOMP packet down a socket.
|
||||
* @param MQTTVersion the version of MQTT being used
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pubcomp(int MQTTVersion, int msgid, networkHandles* net, const char* clientID)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_ack(MQTTVersion, PUBCOMP, msgid, 0, net);
|
||||
Log(LOG_PROTOCOL, 18, NULL, net->socket, clientID, msgid, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create acknowledgement packets.
|
||||
* @param MQTTVersion the version of MQTT being used
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_ack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
Ack* pack = NULL;
|
||||
char* curdata = data;
|
||||
char* enddata = &data[datalen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((pack = malloc(sizeof(Ack))) == NULL)
|
||||
goto exit;
|
||||
pack->MQTTVersion = MQTTVersion;
|
||||
pack->header.byte = aHeader;
|
||||
if (pack->header.bits.type != DISCONNECT)
|
||||
{
|
||||
if (enddata - curdata < 2) /* Is there enough data for the msgid? */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
pack->msgId = readInt(&curdata);
|
||||
}
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
|
||||
pack->rc = MQTTREASONCODE_SUCCESS;
|
||||
pack->properties = props;
|
||||
|
||||
/* disconnect has no msgid */
|
||||
if (datalen > 2 || (pack->header.bits.type == DISCONNECT && datalen > 0))
|
||||
pack->rc = readChar(&curdata); /* reason code */
|
||||
|
||||
if (datalen > 3 || (pack->header.bits.type == DISCONNECT && datalen > 1))
|
||||
{
|
||||
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format the payload for printing in the trace.
|
||||
* Any unprintable characters output as hex.
|
||||
* @param buflen the length of the supplied print buffer
|
||||
* @param buf the supplied print buffer
|
||||
* @param payloadlen the length of the payload to be printed
|
||||
* @param payload the payload data to be printed
|
||||
* @return the length of the data output to the print buffer
|
||||
*/
|
||||
int MQTTPacket_formatPayload(int buflen, char* buf, int payloadlen, char* payload)
|
||||
{
|
||||
int pos = 0;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < payloadlen; i++)
|
||||
{
|
||||
if (isprint((unsigned char)payload[i]))
|
||||
{
|
||||
if (pos >= buflen)
|
||||
break;
|
||||
buf[pos++] = payload[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
static char *hexdigit = "0123456789ABCDEF";
|
||||
unsigned char curchar = (unsigned char)payload[i];
|
||||
|
||||
if (pos >= buflen - 3)
|
||||
break;
|
||||
buf[pos++] = '\\';
|
||||
buf[pos++] = 'x';
|
||||
buf[pos++] = hexdigit[(curchar & 0xF0) >> 4];
|
||||
buf[pos++] = hexdigit[curchar & 0x0F];
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT PUBLISH packet down a socket.
|
||||
* @param pack a structure from which to get some values to use, e.g topic, payload
|
||||
* @param dup boolean - whether to set the MQTT DUP flag
|
||||
* @param qos the value to use for the MQTT QoS setting
|
||||
* @param retained boolean - whether to set the MQTT retained flag
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, const char* clientID)
|
||||
{
|
||||
Header header;
|
||||
char *topiclen;
|
||||
int rc = SOCKET_ERROR;
|
||||
|
||||
FUNC_ENTRY;
|
||||
topiclen = malloc(2);
|
||||
if (topiclen == NULL)
|
||||
goto exit;
|
||||
|
||||
header.bits.type = PUBLISH;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = qos;
|
||||
header.bits.retain = retained;
|
||||
if (qos > 0 || pack->MQTTVersion >= 5)
|
||||
{
|
||||
int buflen = ((qos > 0) ? 2 : 0) + ((pack->MQTTVersion >= 5) ? MQTTProperties_len(&pack->properties) : 0);
|
||||
char *ptr = NULL;
|
||||
char* bufs[4] = {topiclen, pack->topic, NULL, pack->payload};
|
||||
size_t lens[4] = {2, strlen(pack->topic), buflen, pack->payloadlen};
|
||||
int frees[4] = {1, 0, 1, 0};
|
||||
PacketBuffers packetbufs = {4, bufs, lens, frees, {pack->mask[0], pack->mask[1], pack->mask[2], pack->mask[3]}};
|
||||
|
||||
bufs[2] = ptr = malloc(buflen);
|
||||
if (ptr == NULL)
|
||||
goto exit_free;
|
||||
if (qos > 0)
|
||||
writeInt(&ptr, pack->msgId);
|
||||
if (pack->MQTTVersion >= 5)
|
||||
MQTTProperties_write(&ptr, &pack->properties);
|
||||
|
||||
ptr = topiclen;
|
||||
writeInt(&ptr, (int)lens[1]);
|
||||
rc = MQTTPacket_sends(net, header, &packetbufs, pack->MQTTVersion);
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(bufs[2]);
|
||||
memcpy(pack->mask, packetbufs.mask, sizeof(pack->mask));
|
||||
}
|
||||
else
|
||||
{
|
||||
char* ptr = topiclen;
|
||||
char* bufs[3] = {topiclen, pack->topic, pack->payload};
|
||||
size_t lens[3] = {2, strlen(pack->topic), pack->payloadlen};
|
||||
int frees[3] = {1, 0, 0};
|
||||
PacketBuffers packetbufs = {3, bufs, lens, frees, {pack->mask[0], pack->mask[1], pack->mask[2], pack->mask[3]}};
|
||||
|
||||
writeInt(&ptr, (int)lens[1]);
|
||||
rc = MQTTPacket_sends(net, header, &packetbufs, pack->MQTTVersion);
|
||||
memcpy(pack->mask, packetbufs.mask, sizeof(pack->mask));
|
||||
}
|
||||
{
|
||||
#define buflen 30
|
||||
char buf[buflen];
|
||||
int len = 0;
|
||||
|
||||
len = MQTTPacket_formatPayload(buflen, buf, pack->payloadlen, pack->payload);
|
||||
|
||||
if (qos == 0)
|
||||
Log(LOG_PROTOCOL, 27, NULL, net->socket, clientID, retained, rc, pack->payloadlen, len, buf);
|
||||
else
|
||||
Log(LOG_PROTOCOL, 10, NULL, net->socket, clientID, pack->msgId, qos, retained, rc, pack->payloadlen,
|
||||
len, buf);
|
||||
}
|
||||
exit_free:
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(topiclen);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free allocated storage for a various packet tyoes
|
||||
* @param pack pointer to the suback packet structure
|
||||
*/
|
||||
void MQTTPacket_free_packet(MQTTPacket* pack)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (pack->header.bits.type == PUBLISH)
|
||||
MQTTPacket_freePublish((Publish*)pack);
|
||||
/*else if (pack->header.type == SUBSCRIBE)
|
||||
MQTTPacket_freeSubscribe((Subscribe*)pack, 1);
|
||||
else if (pack->header.type == UNSUBSCRIBE)
|
||||
MQTTPacket_freeUnsubscribe((Unsubscribe*)pack);*/
|
||||
else
|
||||
free(pack);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes an integer as 4 bytes to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param anInt the integer to write
|
||||
*/
|
||||
void writeInt4(char** pptr, unsigned int anInt)
|
||||
{
|
||||
**pptr = (char)(anInt / 16777216);
|
||||
(*pptr)++;
|
||||
anInt %= 16777216;
|
||||
**pptr = (char)(anInt / 65536);
|
||||
(*pptr)++;
|
||||
anInt %= 65536;
|
||||
**pptr = (char)(anInt / 256);
|
||||
(*pptr)++;
|
||||
**pptr = (char)(anInt % 256);
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates an integer from two bytes read from the input buffer
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the integer value calculated
|
||||
*/
|
||||
unsigned int readInt4(char** pptr)
|
||||
{
|
||||
unsigned char* ptr = (unsigned char*)*pptr;
|
||||
unsigned int value = 16777216*(*ptr) + 65536*(*(ptr+1)) + 256*(*(ptr+2)) + (*(ptr+3));
|
||||
*pptr += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
void writeMQTTLenString(char** pptr, MQTTLenString lenstring)
|
||||
{
|
||||
writeInt(pptr, lenstring.len);
|
||||
memcpy(*pptr, lenstring.data, lenstring.len);
|
||||
*pptr += lenstring.len;
|
||||
}
|
||||
|
||||
|
||||
int MQTTLenStringRead(MQTTLenString* lenstring, char** pptr, char* enddata)
|
||||
{
|
||||
int len = -1;
|
||||
|
||||
/* the first two bytes are the length of the string */
|
||||
if (enddata - (*pptr) > 1) /* enough length to read the integer? */
|
||||
{
|
||||
lenstring->len = readInt(pptr); /* increments pptr to point past length */
|
||||
if (&(*pptr)[lenstring->len] <= enddata)
|
||||
{
|
||||
lenstring->data = (char*)*pptr;
|
||||
*pptr += lenstring->len;
|
||||
len = 2 + lenstring->len;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
if (prop->value.integer4 >= 0 && prop->value.integer4 <= 127)
|
||||
len = 1;
|
||||
else if (prop->value.integer4 >= 128 && prop->value.integer4 <= 16383)
|
||||
len = 2;
|
||||
else if (prop->value.integer4 >= 16384 && prop->value.integer4 < 2097151)
|
||||
len = 3;
|
||||
else if (prop->value.integer4 >= 2097152 && prop->value.integer4 < 268435455)
|
||||
len = 4;
|
||||
*/
|
||||
int MQTTPacket_VBIlen(int rem_len)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (rem_len < 128)
|
||||
rc = 1;
|
||||
else if (rem_len < 16384)
|
||||
rc = 2;
|
||||
else if (rem_len < 2097152)
|
||||
rc = 3;
|
||||
else
|
||||
rc = 4;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm
|
||||
* @param getcharfn pointer to function to read the next character from the data source
|
||||
* @param value the decoded length returned
|
||||
* @return the number of bytes read from the socket
|
||||
*/
|
||||
int MQTTPacket_VBIdecode(int (*getcharfn)(char*, int), unsigned int* value)
|
||||
{
|
||||
char c;
|
||||
int multiplier = 1;
|
||||
int len = 0;
|
||||
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
|
||||
|
||||
*value = 0;
|
||||
do
|
||||
{
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
{
|
||||
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
rc = (*getcharfn)(&c, 1);
|
||||
if (rc != 1)
|
||||
goto exit;
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
exit:
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static char* bufptr;
|
||||
|
||||
int bufchar(char* c, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
*c = *bufptr++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int MQTTPacket_decodeBuf(char* buf, unsigned int* value)
|
||||
{
|
||||
bufptr = buf;
|
||||
return MQTTPacket_VBIdecode(bufchar, value);
|
||||
}
|
||||
|
||||
272
3rd/paho.mqtt.c/src/MQTTPacket.h
Normal file
272
3rd/paho.mqtt.c/src/MQTTPacket.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2024 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - big endian Linux reversed definition
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPACKET_H)
|
||||
#define MQTTPACKET_H
|
||||
|
||||
#include "Socket.h"
|
||||
#if defined(OPENSSL)
|
||||
#include "SSLSocket.h"
|
||||
#endif
|
||||
#include "LinkedList.h"
|
||||
#include "Clients.h"
|
||||
|
||||
typedef unsigned int bit;
|
||||
typedef void* (*pf)(int, unsigned char, char*, size_t);
|
||||
|
||||
#include "MQTTProperties.h"
|
||||
#include "MQTTReasonCodes.h"
|
||||
|
||||
enum errors
|
||||
{
|
||||
MQTTPACKET_BAD = -4,
|
||||
MQTTPACKET_BUFFER_TOO_SHORT = -2,
|
||||
MQTTPACKET_READ_ERROR = -1,
|
||||
MQTTPACKET_READ_COMPLETE
|
||||
};
|
||||
|
||||
|
||||
enum msgTypes
|
||||
{
|
||||
CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
|
||||
PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
|
||||
PINGREQ, PINGRESP, DISCONNECT, AUTH
|
||||
};
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <endian.h>
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define REVERSED 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Bitfields for the MQTT header byte.
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
/*unsigned*/ char byte; /**< the whole byte */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
bit dup : 1; /**< DUP flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
bit retain : 1; /**< retained flag bit */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
bit retain : 1; /**< retained flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
bit dup : 1; /**< DUP flag bit */
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
} bits;
|
||||
#endif
|
||||
} Header;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a connect packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
union
|
||||
{
|
||||
unsigned char all; /**< all connect flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
bit username : 1; /**< 3.1 user name */
|
||||
bit password : 1; /**< 3.1 password */
|
||||
bit willRetain : 1; /**< will retain setting */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
bit will : 1; /**< will flag */
|
||||
bit cleanstart : 1; /**< cleansession flag */
|
||||
int : 1; /**< unused */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
int : 1; /**< unused */
|
||||
bit cleanstart : 1; /**< cleansession flag */
|
||||
bit will : 1; /**< will flag */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
bit willRetain : 1; /**< will retain setting */
|
||||
bit password : 1; /**< 3.1 password */
|
||||
bit username : 1; /**< 3.1 user name */
|
||||
} bits;
|
||||
#endif
|
||||
} flags; /**< connect flags byte */
|
||||
|
||||
char *Protocol, /**< MQTT protocol name */
|
||||
*clientID, /**< string client id */
|
||||
*willTopic, /**< will topic */
|
||||
*willMsg; /**< will payload */
|
||||
|
||||
int keepAliveTimer; /**< keepalive timeout value in seconds */
|
||||
unsigned char version; /**< MQTT version number */
|
||||
} Connect;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a connack packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
union
|
||||
{
|
||||
unsigned char all; /**< all connack flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int reserved : 7; /**< message type nibble */
|
||||
bit sessionPresent : 1; /**< was a session found on the server? */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
bit sessionPresent : 1; /**< was a session found on the server? */
|
||||
unsigned int reserved : 7; /**< message type nibble */
|
||||
} bits;
|
||||
#endif
|
||||
} flags; /**< connack flags byte */
|
||||
unsigned char rc; /**< connack reason code */
|
||||
unsigned int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
} Connack;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a packet with header only.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
} MQTTPacket;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a suback packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
int msgId; /**< MQTT message id */
|
||||
int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
List* qoss; /**< list of granted QoSs (MQTT 3/4) / reason codes (MQTT 5) */
|
||||
} Suback;
|
||||
|
||||
|
||||
/**
|
||||
* Data for an MQTT V5 unsuback packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
int msgId; /**< MQTT message id */
|
||||
int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
List* reasonCodes; /**< list of reason codes */
|
||||
} Unsuback;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a publish packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
char* topic; /**< topic string */
|
||||
int topiclen;
|
||||
int msgId; /**< MQTT message id */
|
||||
char* payload; /**< binary payload, length delimited */
|
||||
int payloadlen; /**< payload length */
|
||||
int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
uint8_t mask[4]; /**< the websockets mask the payload is masked with, if any */
|
||||
} Publish;
|
||||
|
||||
|
||||
/**
|
||||
* Data for one of the ack packets.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
int msgId; /**< MQTT message id */
|
||||
unsigned char rc; /**< MQTT 5 reason code */
|
||||
int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
} Ack;
|
||||
|
||||
typedef Ack Puback;
|
||||
typedef Ack Pubrec;
|
||||
typedef Ack Pubrel;
|
||||
typedef Ack Pubcomp;
|
||||
|
||||
int MQTTPacket_encode(char* buf, size_t length);
|
||||
int MQTTPacket_decode(networkHandles* net, size_t* value);
|
||||
int readInt(char** pptr);
|
||||
char* readUTF(char** pptr, char* enddata);
|
||||
unsigned char readChar(char** pptr);
|
||||
void writeChar(char** pptr, char c);
|
||||
void writeInt(char** pptr, int anInt);
|
||||
void writeUTF(char** pptr, const char* string);
|
||||
void writeData(char** pptr, const void* data, int datalen);
|
||||
|
||||
const char* MQTTPacket_name(int ptype);
|
||||
|
||||
void* MQTTPacket_Factory(int MQTTVersion, networkHandles* net, int* error);
|
||||
int MQTTPacket_send(networkHandles* net, Header header, char* buffer, size_t buflen, int free, int MQTTVersion);
|
||||
int MQTTPacket_sends(networkHandles* net, Header header, PacketBuffers* buffers, int MQTTVersion);
|
||||
|
||||
void* MQTTPacket_header_only(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
int MQTTPacket_send_disconnect(Clients* client, enum MQTTReasonCodes reason, MQTTProperties* props);
|
||||
|
||||
void* MQTTPacket_publish(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
void MQTTPacket_freePublish(Publish* pack);
|
||||
int MQTTPacket_formatPayload(int buflen, char* buf, int payloadlen, char* payload);
|
||||
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, const char* clientID);
|
||||
int MQTTPacket_send_puback(int MQTTVersion, int msgid, networkHandles* net, const char* clientID);
|
||||
void* MQTTPacket_ack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
|
||||
void MQTTPacket_freeAck(Ack* pack);
|
||||
void MQTTPacket_freeSuback(Suback* pack);
|
||||
void MQTTPacket_freeUnsuback(Unsuback* pack);
|
||||
int MQTTPacket_send_pubrec(int MQTTVersion, int msgid, networkHandles* net, const char* clientID);
|
||||
int MQTTPacket_send_pubrel(int MQTTVersion, int msgid, int dup, networkHandles* net, const char* clientID);
|
||||
int MQTTPacket_send_pubcomp(int MQTTVersion, int msgid, networkHandles* net, const char* clientID);
|
||||
|
||||
void MQTTPacket_free_packet(MQTTPacket* pack);
|
||||
|
||||
void writeInt4(char** pptr, unsigned int anInt);
|
||||
unsigned int readInt4(char** pptr);
|
||||
void writeMQTTLenString(char** pptr, MQTTLenString lenstring);
|
||||
int MQTTLenStringRead(MQTTLenString* lenstring, char** pptr, char* enddata);
|
||||
int MQTTPacket_VBIlen(int rem_len);
|
||||
int MQTTPacket_decodeBuf(char* buf, unsigned int* value);
|
||||
|
||||
#include "MQTTPacketOut.h"
|
||||
|
||||
#endif /* MQTTPACKET_H */
|
||||
476
3rd/paho.mqtt.c/src/MQTTPacketOut.c
Normal file
476
3rd/paho.mqtt.c/src/MQTTPacketOut.c
Normal file
@@ -0,0 +1,476 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2024 IBM Corp. and Ian Craggs
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Rong Xiang, Ian Craggs - C++ compatibility
|
||||
* Ian Craggs - binary password and will payload
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions to deal with reading and writing of MQTT packets from and to sockets
|
||||
*
|
||||
* Some other related functions are in the MQTTPacket module
|
||||
*/
|
||||
|
||||
|
||||
#include "MQTTPacketOut.h"
|
||||
#include "Log.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT CONNECT packet down a socket for V5 or later
|
||||
* @param client a structure from which to get all the required values
|
||||
* @param MQTTVersion the MQTT version to connect with
|
||||
* @param connectProperties MQTT V5 properties for the connect packet
|
||||
* @param willProperties MQTT V5 properties for the will message, if any
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_connect(Clients* client, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties)
|
||||
{
|
||||
char *buf, *ptr;
|
||||
Connect packet;
|
||||
int rc = SOCKET_ERROR, len;
|
||||
|
||||
FUNC_ENTRY;
|
||||
packet.header.byte = 0;
|
||||
packet.header.bits.type = CONNECT;
|
||||
|
||||
len = ((MQTTVersion == MQTTVERSION_3_1) ? 12 : 10) + (int)strlen(client->clientID)+2;
|
||||
if (client->will)
|
||||
len += (int)strlen(client->will->topic)+2 + client->will->payloadlen+2;
|
||||
if (client->username)
|
||||
len += (int)strlen(client->username)+2;
|
||||
if (client->password)
|
||||
len += client->passwordlen+2;
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
len += MQTTProperties_len(connectProperties);
|
||||
if (client->will)
|
||||
len += MQTTProperties_len(willProperties);
|
||||
}
|
||||
|
||||
ptr = buf = malloc(len);
|
||||
if (ptr == NULL)
|
||||
goto exit_nofree;
|
||||
if (MQTTVersion == MQTTVERSION_3_1)
|
||||
{
|
||||
writeUTF(&ptr, "MQIsdp");
|
||||
writeChar(&ptr, (char)MQTTVERSION_3_1);
|
||||
}
|
||||
else if (MQTTVersion == MQTTVERSION_3_1_1 || MQTTVersion == MQTTVERSION_5)
|
||||
{
|
||||
writeUTF(&ptr, "MQTT");
|
||||
writeChar(&ptr, (char)MQTTVersion);
|
||||
}
|
||||
else
|
||||
goto exit;
|
||||
|
||||
packet.flags.all = 0;
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
packet.flags.bits.cleanstart = client->cleanstart;
|
||||
else
|
||||
packet.flags.bits.cleanstart = client->cleansession;
|
||||
packet.flags.bits.will = (client->will) ? 1 : 0;
|
||||
if (packet.flags.bits.will)
|
||||
{
|
||||
packet.flags.bits.willQoS = client->will->qos;
|
||||
packet.flags.bits.willRetain = client->will->retained;
|
||||
}
|
||||
if (client->username)
|
||||
packet.flags.bits.username = 1;
|
||||
if (client->password)
|
||||
packet.flags.bits.password = 1;
|
||||
|
||||
writeChar(&ptr, packet.flags.all);
|
||||
writeInt(&ptr, client->keepAliveInterval);
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_write(&ptr, connectProperties);
|
||||
writeUTF(&ptr, client->clientID);
|
||||
if (client->will)
|
||||
{
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_write(&ptr, willProperties);
|
||||
writeUTF(&ptr, client->will->topic);
|
||||
writeData(&ptr, client->will->payload, client->will->payloadlen);
|
||||
}
|
||||
if (client->username)
|
||||
writeUTF(&ptr, client->username);
|
||||
if (client->password)
|
||||
writeData(&ptr, client->password, client->passwordlen);
|
||||
|
||||
rc = MQTTPacket_send(&client->net, packet.header, buf, len, 1, MQTTVersion);
|
||||
Log(LOG_PROTOCOL, 0, NULL, client->net.socket, client->clientID,
|
||||
MQTTVersion, client->cleansession, rc);
|
||||
exit:
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
exit_nofree:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create connack packets.
|
||||
* @param MQTTVersion MQTT 5 or less?
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_connack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
Connack* pack = NULL;
|
||||
char* curdata = data;
|
||||
char* enddata = &data[datalen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((pack = malloc(sizeof(Connack))) == NULL)
|
||||
goto exit;
|
||||
pack->MQTTVersion = MQTTVersion;
|
||||
pack->header.byte = aHeader;
|
||||
if (datalen < 2) /* enough data for connect flags and reason code? */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
pack->flags.all = readChar(&curdata); /* connect flags */
|
||||
pack->rc = readChar(&curdata); /* reason code */
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
pack->properties = props;
|
||||
if (datalen > 2)
|
||||
{
|
||||
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free allocated storage for a connack packet.
|
||||
* @param pack pointer to the connack packet structure
|
||||
*/
|
||||
void MQTTPacket_freeConnack(Connack* pack)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (pack->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_free(&pack->properties);
|
||||
free(pack);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT PINGREQ packet down a socket.
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pingreq(networkHandles* net, const char* clientID)
|
||||
{
|
||||
Header header;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = 0;
|
||||
header.bits.type = PINGREQ;
|
||||
rc = MQTTPacket_send(net, header, NULL, 0, 0, MQTTVERSION_3_1_1);
|
||||
Log(LOG_PROTOCOL, 20, NULL, net->socket, clientID, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT subscribe packet down a socket.
|
||||
* @param topics list of topics
|
||||
* @param qoss list of corresponding QoSs
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param dup boolean - whether to set the MQTT DUP flag
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_subscribe(List* topics, List* qoss, MQTTSubscribe_options* opts, MQTTProperties* props,
|
||||
int msgid, int dup, Clients* client)
|
||||
{
|
||||
Header header;
|
||||
char *data, *ptr;
|
||||
int rc = -1;
|
||||
ListElement *elem = NULL, *qosElem = NULL;
|
||||
int datalen, i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.bits.type = SUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
header.bits.retain = 0;
|
||||
|
||||
datalen = 2 + topics->count * 3; /* utf length + char qos == 3 */
|
||||
while (ListNextElement(topics, &elem))
|
||||
datalen += (int)strlen((char*)(elem->content));
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
datalen += MQTTProperties_len(props);
|
||||
|
||||
ptr = data = malloc(datalen);
|
||||
if (ptr == NULL)
|
||||
goto exit;
|
||||
writeInt(&ptr, msgid);
|
||||
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_write(&ptr, props);
|
||||
|
||||
elem = NULL;
|
||||
while (ListNextElement(topics, &elem))
|
||||
{
|
||||
char subopts = 0;
|
||||
|
||||
ListNextElement(qoss, &qosElem);
|
||||
writeUTF(&ptr, (char*)(elem->content));
|
||||
subopts = *(int*)(qosElem->content);
|
||||
if (client->MQTTVersion >= MQTTVERSION_5 && opts != NULL)
|
||||
{
|
||||
subopts |= (opts[i].noLocal << 2); /* 1 bit */
|
||||
subopts |= (opts[i].retainAsPublished << 3); /* 1 bit */
|
||||
subopts |= (opts[i].retainHandling << 4); /* 2 bits */
|
||||
}
|
||||
writeChar(&ptr, subopts);
|
||||
++i;
|
||||
}
|
||||
rc = MQTTPacket_send(&client->net, header, data, datalen, 1, client->MQTTVersion);
|
||||
Log(LOG_PROTOCOL, 22, NULL, client->net.socket, client->clientID, msgid, rc);
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(data);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create suback packets.
|
||||
* @param MQTTVersion the version of MQTT
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_suback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
Suback* pack = NULL;
|
||||
char* curdata = data;
|
||||
char* enddata = &data[datalen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((pack = malloc(sizeof(Suback))) == NULL)
|
||||
goto exit;
|
||||
pack->MQTTVersion = MQTTVersion;
|
||||
pack->header.byte = aHeader;
|
||||
if (enddata - curdata < 2) /* Is there enough data to read the msgid? */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
pack->msgId = readInt(&curdata);
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
pack->properties = props;
|
||||
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
pack->qoss = ListInitialize();
|
||||
while ((size_t)(curdata - data) < datalen)
|
||||
{
|
||||
unsigned int* newint;
|
||||
newint = malloc(sizeof(unsigned int));
|
||||
if (newint == NULL)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
*newint = (unsigned int)readChar(&curdata);
|
||||
ListAppend(pack->qoss, newint, sizeof(unsigned int));
|
||||
}
|
||||
if (pack->qoss->count == 0)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
ListFree(pack->qoss);
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT unsubscribe packet down a socket.
|
||||
* @param topics list of topics
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param dup boolean - whether to set the MQTT DUP flag
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_unsubscribe(List* topics, MQTTProperties* props, int msgid, int dup, Clients* client)
|
||||
{
|
||||
Header header;
|
||||
char *data, *ptr;
|
||||
int rc = SOCKET_ERROR;
|
||||
ListElement *elem = NULL;
|
||||
int datalen;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.bits.type = UNSUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
header.bits.retain = 0;
|
||||
|
||||
datalen = 2 + topics->count * 2; /* utf length == 2 */
|
||||
while (ListNextElement(topics, &elem))
|
||||
datalen += (int)strlen((char*)(elem->content));
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
datalen += MQTTProperties_len(props);
|
||||
ptr = data = malloc(datalen);
|
||||
if (ptr == NULL)
|
||||
goto exit;
|
||||
|
||||
writeInt(&ptr, msgid);
|
||||
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_write(&ptr, props);
|
||||
|
||||
elem = NULL;
|
||||
while (ListNextElement(topics, &elem))
|
||||
writeUTF(&ptr, (char*)(elem->content));
|
||||
rc = MQTTPacket_send(&client->net, header, data, datalen, 1, client->MQTTVersion);
|
||||
Log(LOG_PROTOCOL, 25, NULL, client->net.socket, client->clientID, msgid, rc);
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(data);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create unsuback packets.
|
||||
* @param MQTTVersion the version of MQTT
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_unsuback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
Unsuback* pack = NULL;
|
||||
char* curdata = data;
|
||||
char* enddata = &data[datalen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((pack = malloc(sizeof(Unsuback))) == NULL)
|
||||
goto exit;
|
||||
pack->MQTTVersion = MQTTVersion;
|
||||
pack->header.byte = aHeader;
|
||||
if (enddata - curdata < 2) /* Is there enough data? */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
pack->msgId = readInt(&curdata);
|
||||
pack->reasonCodes = NULL;
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
pack->properties = props;
|
||||
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
pack->reasonCodes = ListInitialize();
|
||||
while ((size_t)(curdata - data) < datalen)
|
||||
{
|
||||
enum MQTTReasonCodes* newrc;
|
||||
newrc = malloc(sizeof(enum MQTTReasonCodes));
|
||||
if (newrc == NULL)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
*newrc = (enum MQTTReasonCodes)readChar(&curdata);
|
||||
ListAppend(pack->reasonCodes, newrc, sizeof(enum MQTTReasonCodes));
|
||||
}
|
||||
if (pack->reasonCodes->count == 0)
|
||||
{
|
||||
ListFree(pack->reasonCodes);
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
39
3rd/paho.mqtt.c/src/MQTTPacketOut.h
Normal file
39
3rd/paho.mqtt.c/src/MQTTPacketOut.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2018 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPACKETOUT_H)
|
||||
#define MQTTPACKETOUT_H
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
int MQTTPacket_send_connect(Clients* client, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties);
|
||||
void* MQTTPacket_connack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
void MQTTPacket_freeConnack(Connack* pack);
|
||||
|
||||
int MQTTPacket_send_pingreq(networkHandles* net, const char* clientID);
|
||||
|
||||
int MQTTPacket_send_subscribe(List* topics, List* qoss, MQTTSubscribe_options* opts, MQTTProperties* props,
|
||||
int msgid, int dup, Clients* client);
|
||||
void* MQTTPacket_suback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
|
||||
int MQTTPacket_send_unsubscribe(List* topics, MQTTProperties* props, int msgid, int dup, Clients* client);
|
||||
void* MQTTPacket_unsuback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
|
||||
#endif
|
||||
913
3rd/paho.mqtt.c/src/MQTTPersistence.c
Normal file
913
3rd/paho.mqtt.c/src/MQTTPersistence.c
Normal file
@@ -0,0 +1,913 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2026 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - async client updates
|
||||
* Ian Craggs - fix for bug 432903 - queue persistence
|
||||
* Ian Craggs - MQTT V5 updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief Functions that apply to persistence operations.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MQTTPersistence.h"
|
||||
#include "MQTTPersistenceDefault.h"
|
||||
#include "MQTTProtocolClient.h"
|
||||
#include "Heap.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
|
||||
static MQTTPersistence_qEntry* MQTTPersistence_restoreQueueEntry(char* buffer, size_t buflen, int MQTTVersion);
|
||||
static void MQTTPersistence_insertInSeqOrder(List* list, MQTTPersistence_qEntry* qEntry, size_t size);
|
||||
|
||||
/**
|
||||
* Creates a ::MQTTClient_persistence structure representing a persistence implementation.
|
||||
* @param persistence the ::MQTTClient_persistence structure.
|
||||
* @param type the type of the persistence implementation. See ::MQTTClient_create.
|
||||
* @param pcontext the context for this persistence implementation. See ::MQTTClient_create.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
#include "StackTrace.h"
|
||||
|
||||
int MQTTPersistence_create(MQTTClient_persistence** persistence, int type, void* pcontext)
|
||||
{
|
||||
int rc = 0;
|
||||
MQTTClient_persistence* per = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
switch (type)
|
||||
{
|
||||
case MQTTCLIENT_PERSISTENCE_NONE :
|
||||
per = NULL;
|
||||
break;
|
||||
case MQTTCLIENT_PERSISTENCE_DEFAULT :
|
||||
per = malloc(sizeof(MQTTClient_persistence));
|
||||
if ( per != NULL )
|
||||
{
|
||||
if ( pcontext == NULL )
|
||||
pcontext = "."; /* working directory */
|
||||
if ((per->context = malloc(strlen(pcontext) + 1)) == NULL)
|
||||
{
|
||||
free(per);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(per->context, pcontext);
|
||||
/* file system functions */
|
||||
per->popen = pstopen;
|
||||
per->pclose = pstclose;
|
||||
per->pput = pstput;
|
||||
per->pget = pstget;
|
||||
per->premove = pstremove;
|
||||
per->pkeys = pstkeys;
|
||||
per->pclear = pstclear;
|
||||
per->pcontainskey = pstcontainskey;
|
||||
}
|
||||
else
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
break;
|
||||
case MQTTCLIENT_PERSISTENCE_USER :
|
||||
per = (MQTTClient_persistence *)pcontext;
|
||||
if ( per == NULL || (per != NULL && (per->context == NULL || per->pclear == NULL ||
|
||||
per->pclose == NULL || per->pcontainskey == NULL || per->pget == NULL || per->pkeys == NULL ||
|
||||
per->popen == NULL || per->pput == NULL || per->premove == NULL)) )
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
break;
|
||||
default:
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
*persistence = per;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open persistent store and restore any persisted messages.
|
||||
* @param client the client as ::Clients.
|
||||
* @param serverURI the URI of the remote end.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_initialize(Clients *c, const char *serverURI)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ( c->persistence != NULL )
|
||||
{
|
||||
rc = c->persistence->popen(&(c->phandle), c->clientID, serverURI, c->persistence->context);
|
||||
if ( rc == 0 )
|
||||
rc = MQTTPersistence_restorePackets(c);
|
||||
}
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close persistent store.
|
||||
* @param client the client as ::Clients.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_close(Clients *c)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (c->persistence != NULL)
|
||||
{
|
||||
rc = c->persistence->pclose(c->phandle);
|
||||
|
||||
if (c->persistence->popen == pstopen) {
|
||||
if (c->persistence->context)
|
||||
free(c->persistence->context);
|
||||
free(c->persistence);
|
||||
}
|
||||
|
||||
c->phandle = NULL;
|
||||
c->persistence = NULL;
|
||||
}
|
||||
#endif
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the persistent store.
|
||||
* @param client the client as ::Clients.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_clear(Clients *c)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence != NULL)
|
||||
rc = c->persistence->pclear(c->phandle);
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restores the persisted records to the outbound and inbound message queues of the
|
||||
* client.
|
||||
* @param client the client as ::Clients.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_restorePackets(Clients *c)
|
||||
{
|
||||
int rc = 0;
|
||||
char **msgkeys = NULL,
|
||||
*buffer = NULL;
|
||||
int nkeys = 0, buflen;
|
||||
int i = 0;
|
||||
int msgs_sent = 0;
|
||||
int msgs_rcvd = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
|
||||
{
|
||||
while (rc == 0 && i < nkeys)
|
||||
{
|
||||
if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_COMMAND_KEY, strlen(PERSISTENCE_V5_COMMAND_KEY)) == 0)
|
||||
{
|
||||
;
|
||||
}
|
||||
else if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) == 0)
|
||||
{
|
||||
;
|
||||
}
|
||||
else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0 &&
|
||||
(c->afterRead == NULL || (rc = c->afterRead(c->afterRead_context, &buffer, &buflen)) == 0))
|
||||
{
|
||||
int data_MQTTVersion = MQTTVERSION_3_1_1;
|
||||
char* cur_key = msgkeys[i];
|
||||
MQTTPacket* pack = NULL;
|
||||
|
||||
if (strncmp(cur_key, PERSISTENCE_V5_PUBLISH_RECEIVED,
|
||||
strlen(PERSISTENCE_V5_PUBLISH_RECEIVED)) == 0)
|
||||
{
|
||||
data_MQTTVersion = MQTTVERSION_5;
|
||||
cur_key = PERSISTENCE_PUBLISH_RECEIVED;
|
||||
}
|
||||
else if (strncmp(cur_key, PERSISTENCE_V5_PUBLISH_SENT,
|
||||
strlen(PERSISTENCE_V5_PUBLISH_SENT)) == 0)
|
||||
{
|
||||
data_MQTTVersion = MQTTVERSION_5;
|
||||
cur_key = PERSISTENCE_PUBLISH_SENT;
|
||||
}
|
||||
else if (strncmp(cur_key, PERSISTENCE_V5_PUBREL,
|
||||
strlen(PERSISTENCE_V5_PUBREL)) == 0)
|
||||
{
|
||||
data_MQTTVersion = MQTTVERSION_5;
|
||||
cur_key = PERSISTENCE_PUBREL;
|
||||
}
|
||||
|
||||
if (data_MQTTVersion == MQTTVERSION_5 && c->MQTTVersion < MQTTVERSION_5)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR; /* can't restore version 5 data with a version 3 client */
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pack = MQTTPersistence_restorePacket(data_MQTTVersion, buffer, buflen);
|
||||
if ( pack != NULL )
|
||||
{
|
||||
if (strncmp(cur_key, PERSISTENCE_PUBLISH_RECEIVED,
|
||||
strlen(PERSISTENCE_PUBLISH_RECEIVED)) == 0)
|
||||
{
|
||||
Publish* publish = (Publish*)pack;
|
||||
Messages* msg = NULL;
|
||||
publish->MQTTVersion = c->MQTTVersion;
|
||||
msg = MQTTProtocol_createMessage(publish, &msg, publish->header.bits.qos, publish->header.bits.retain, 1);
|
||||
msg->nextMessageType = PUBREL;
|
||||
/* order does not matter for persisted received messages */
|
||||
ListAppend(c->inboundMsgs, msg, msg->len);
|
||||
if (c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
free(msg->publish->payload);
|
||||
free(msg->publish->topic);
|
||||
msg->publish->payload = msg->publish->topic = NULL;
|
||||
}
|
||||
publish->topic = NULL;
|
||||
MQTTPacket_freePublish(publish);
|
||||
msgs_rcvd++;
|
||||
}
|
||||
else if (strncmp(cur_key, PERSISTENCE_PUBLISH_SENT,
|
||||
strlen(PERSISTENCE_PUBLISH_SENT)) == 0)
|
||||
{
|
||||
Publish* publish = (Publish*)pack;
|
||||
Messages* msg = NULL;
|
||||
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
char *key = malloc(keysize);
|
||||
int chars = 0;
|
||||
|
||||
if (!key)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
publish->MQTTVersion = c->MQTTVersion;
|
||||
if (publish->MQTTVersion >= MQTTVERSION_5)
|
||||
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBREL, publish->msgId);
|
||||
else
|
||||
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBREL, publish->msgId);
|
||||
if (chars >= keysize)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = MQTTProtocol_createMessage(publish, &msg, publish->header.bits.qos, publish->header.bits.retain, 1);
|
||||
if (c->persistence->pcontainskey(c->phandle, key) == 0)
|
||||
/* PUBLISH Qo2 and PUBREL sent */
|
||||
msg->nextMessageType = PUBCOMP;
|
||||
/* else: PUBLISH QoS1, or PUBLISH QoS2 and PUBREL not sent */
|
||||
/* retry at the first opportunity */
|
||||
memset(&msg->lastTouch, '\0', sizeof(msg->lastTouch));
|
||||
MQTTPersistence_insertInOrder(c->outboundMsgs, msg, msg->len);
|
||||
publish->topic = NULL;
|
||||
MQTTPacket_freePublish(publish);
|
||||
msgs_sent++;
|
||||
}
|
||||
free(key);
|
||||
}
|
||||
else if (strncmp(cur_key, PERSISTENCE_PUBREL, strlen(PERSISTENCE_PUBREL)) == 0)
|
||||
{
|
||||
/* orphaned PUBRELs ? */
|
||||
Pubrel* pubrel = (Pubrel*)pack;
|
||||
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
char *key = malloc(keysize);
|
||||
int chars = 0;
|
||||
|
||||
if (!key)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
pubrel->MQTTVersion = c->MQTTVersion;
|
||||
if (pubrel->MQTTVersion >= MQTTVERSION_5)
|
||||
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_SENT, pubrel->msgId);
|
||||
else
|
||||
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_SENT, pubrel->msgId);
|
||||
if (chars >= keysize)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
}
|
||||
else if (c->persistence->pcontainskey(c->phandle, key) != 0)
|
||||
rc = c->persistence->premove(c->phandle, msgkeys[i]);
|
||||
free(pubrel);
|
||||
free(key);
|
||||
}
|
||||
}
|
||||
else /* pack == NULL -> bad persisted record */
|
||||
rc = c->persistence->premove(c->phandle, msgkeys[i]);
|
||||
}
|
||||
if (buffer)
|
||||
{
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
if (msgkeys[i])
|
||||
{
|
||||
free(msgkeys[i]);
|
||||
msgkeys[i] = NULL;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d sent messages and %d received messages restored for client %s\n",
|
||||
msgs_sent, msgs_rcvd, c->clientID);
|
||||
MQTTPersistence_wrapMsgID(c);
|
||||
exit:
|
||||
if (msgkeys)
|
||||
{
|
||||
for (i = 0; i < nkeys; ++i)
|
||||
{
|
||||
if (msgkeys[i])
|
||||
free(msgkeys[i]);
|
||||
}
|
||||
free(msgkeys);
|
||||
}
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a MQTT packet restored from persisted data.
|
||||
* @param buffer the persisted data.
|
||||
* @param buflen the number of bytes of the data buffer.
|
||||
*/
|
||||
void* MQTTPersistence_restorePacket(int MQTTVersion, char* buffer, size_t buflen)
|
||||
{
|
||||
void* pack = NULL;
|
||||
Header header;
|
||||
int fixed_header_length = 1, ptype, remaining_length = 0;
|
||||
char c;
|
||||
int multiplier = 1;
|
||||
extern pf new_packets[];
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = buffer[0];
|
||||
/* decode the message length according to the MQTT algorithm */
|
||||
do
|
||||
{
|
||||
c = *(++buffer);
|
||||
remaining_length += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
fixed_header_length++;
|
||||
} while ((c & 128) != 0);
|
||||
|
||||
if ( (fixed_header_length + remaining_length) == buflen )
|
||||
{
|
||||
ptype = header.bits.type;
|
||||
if (ptype >= CONNECT && ptype <= DISCONNECT && new_packets[ptype] != NULL)
|
||||
pack = (*new_packets[ptype])(MQTTVersion, header.byte, ++buffer, remaining_length);
|
||||
}
|
||||
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts the specified message into the list, maintaining message ID order.
|
||||
* @param list the list to insert the message into.
|
||||
* @param content the message to add.
|
||||
* @param size size of the message.
|
||||
*/
|
||||
void MQTTPersistence_insertInOrder(List* list, void* content, size_t size)
|
||||
{
|
||||
ListElement* index = NULL;
|
||||
ListElement* current = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
while(ListNextElement(list, ¤t) != NULL && index == NULL)
|
||||
{
|
||||
if ( ((Messages*)content)->msgid < ((Messages*)current->content)->msgid )
|
||||
index = current;
|
||||
}
|
||||
|
||||
ListInsert(list, content, size, index);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a record to the persistent store. This function must not be called for QoS0
|
||||
* messages.
|
||||
* @param socket the socket of the client.
|
||||
* @param buf0 fixed header.
|
||||
* @param buf0len length of the fixed header.
|
||||
* @param count number of buffers representing the variable header and/or the payload.
|
||||
* @param buffers the buffers representing the variable header and/or the payload.
|
||||
* @param buflens length of the buffers representing the variable header and/or the payload.
|
||||
* @param htype MQTT packet type - PUBLISH or PUBREL
|
||||
* @param msgId the message ID.
|
||||
* @param scr 0 indicates message in the sending direction; 1 indicates message in the
|
||||
* receiving direction.
|
||||
* @param the MQTT version being used (>= MQTTVERSION_5 means properties included)
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_putPacket(SOCKET socket, char* buf0, size_t buf0len, int count,
|
||||
char** buffers, size_t* buflens, int htype, int msgId, int scr, int MQTTVersion)
|
||||
{
|
||||
int rc = 0;
|
||||
extern ClientStates* bstate;
|
||||
int nbufs, i;
|
||||
int* lens = NULL;
|
||||
char** bufs = NULL;
|
||||
char *key;
|
||||
Clients* client = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
client = (Clients*)(ListFindItem(bstate->clients, &socket, clientSocketCompare)->content);
|
||||
if (client->persistence != NULL)
|
||||
{
|
||||
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
if ((key = malloc(keysize)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
nbufs = 1 + count;
|
||||
if ((lens = (int *)malloc(nbufs * sizeof(int))) == NULL)
|
||||
{
|
||||
free(key);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if ((bufs = (char **)malloc(nbufs * sizeof(char *))) == NULL)
|
||||
{
|
||||
free(key);
|
||||
free(lens);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
lens[0] = (int)buf0len;
|
||||
bufs[0] = buf0;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
lens[i+1] = (int)buflens[i];
|
||||
bufs[i+1] = buffers[i];
|
||||
}
|
||||
|
||||
/* key */
|
||||
if (scr == 0)
|
||||
{ /* sending */
|
||||
char* key_id = PERSISTENCE_PUBLISH_SENT;
|
||||
|
||||
if (htype == PUBLISH) /* PUBLISH QoS1 and QoS2*/
|
||||
{
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
key_id = PERSISTENCE_V5_PUBLISH_SENT;
|
||||
}
|
||||
else if (htype == PUBREL) /* PUBREL */
|
||||
{
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
key_id = PERSISTENCE_V5_PUBREL;
|
||||
else
|
||||
key_id = PERSISTENCE_PUBREL;
|
||||
}
|
||||
if (snprintf(key, keysize, "%s%d", key_id, msgId) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
else if (scr == 1) /* receiving PUBLISH QoS2 */
|
||||
{
|
||||
char* key_id = PERSISTENCE_PUBLISH_RECEIVED;
|
||||
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
key_id = PERSISTENCE_V5_PUBLISH_RECEIVED;
|
||||
if (snprintf(key, keysize, "%s%d", key_id, msgId) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
|
||||
if (rc == 0 && client->beforeWrite)
|
||||
rc = client->beforeWrite(client->beforeWrite_context, nbufs, bufs, lens);
|
||||
|
||||
if (rc == 0)
|
||||
rc = client->persistence->pput(client->phandle, key, nbufs, bufs, lens);
|
||||
|
||||
free(key);
|
||||
free(lens);
|
||||
free(bufs);
|
||||
}
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a record from the persistent store.
|
||||
* @param client the client as ::Clients.
|
||||
* @param type the type of the persisted record: #PERSISTENCE_PUBLISH_SENT, #PERSISTENCE_PUBREL
|
||||
* or #PERSISTENCE_PUBLISH_RECEIVED.
|
||||
* @param qos the qos field of the message.
|
||||
* @param msgId the message ID.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_remove(Clients* c, char *type, int qos, int msgId)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence != NULL)
|
||||
{
|
||||
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
char *key = malloc(keysize);
|
||||
int chars = 0;
|
||||
|
||||
if (!key)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (strcmp(type, PERSISTENCE_PUBLISH_SENT) == 0 ||
|
||||
strcmp(type, PERSISTENCE_V5_PUBLISH_SENT) == 0)
|
||||
{
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_SENT, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
rc = c->persistence->premove(c->phandle, key);
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBREL, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
rc += c->persistence->premove(c->phandle, key);
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_SENT, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
rc += c->persistence->premove(c->phandle, key);
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBREL, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
rc += c->persistence->premove(c->phandle, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* PERSISTENCE_PUBLISH_SENT && qos == 1 */
|
||||
{ /* or PERSISTENCE_PUBLISH_RECEIVED */
|
||||
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_RECEIVED, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
rc = c->persistence->premove(c->phandle, key);
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_RECEIVED, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
rc += c->persistence->premove(c->phandle, key);
|
||||
}
|
||||
}
|
||||
if (rc == MQTTCLIENT_PERSISTENCE_ERROR)
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
free(key);
|
||||
}
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the message IDs wrapped by looking for the largest gap between two consecutive
|
||||
* message IDs in the outboundMsgs queue.
|
||||
* @param client the client as ::Clients.
|
||||
*/
|
||||
void MQTTPersistence_wrapMsgID(Clients *client)
|
||||
{
|
||||
ListElement* wrapel = NULL;
|
||||
ListElement* current = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ( client->outboundMsgs->count > 0 )
|
||||
{
|
||||
int firstMsgID = ((Messages*)client->outboundMsgs->first->content)->msgid;
|
||||
int lastMsgID = ((Messages*)client->outboundMsgs->last->content)->msgid;
|
||||
int gap = MAX_MSG_ID - lastMsgID + firstMsgID;
|
||||
current = ListNextElement(client->outboundMsgs, ¤t);
|
||||
|
||||
while(ListNextElement(client->outboundMsgs, ¤t) != NULL)
|
||||
{
|
||||
int curMsgID = ((Messages*)current->content)->msgid;
|
||||
int curPrevMsgID = ((Messages*)current->prev->content)->msgid;
|
||||
int curgap = curMsgID - curPrevMsgID;
|
||||
if ( curgap > gap )
|
||||
{
|
||||
gap = curgap;
|
||||
wrapel = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( wrapel != NULL )
|
||||
{
|
||||
/* put wrapel at the beginning of the queue */
|
||||
client->outboundMsgs->first->prev = client->outboundMsgs->last;
|
||||
client->outboundMsgs->last->next = client->outboundMsgs->first;
|
||||
client->outboundMsgs->first = wrapel;
|
||||
client->outboundMsgs->last = wrapel->prev;
|
||||
client->outboundMsgs->first->prev = NULL;
|
||||
client->outboundMsgs->last->next = NULL;
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
#define KEYSIZE PERSISTENCE_MAX_KEY_LENGTH + 1
|
||||
int MQTTPersistence_unpersistQueueEntry(Clients* client, MQTTPersistence_qEntry* qe)
|
||||
{
|
||||
int rc = 0;
|
||||
char key[KEYSIZE];
|
||||
int chars = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_V5_QUEUE_KEY, qe->seqno);
|
||||
else
|
||||
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_QUEUE_KEY, qe->seqno);
|
||||
if (chars >= KEYSIZE)
|
||||
{
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
else if ((rc = client->persistence->premove(client->phandle, key)) != 0)
|
||||
Log(LOG_ERROR, 0, "Error %d removing qEntry from persistence", rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#define MAX_NO_OF_BUFFERS 9
|
||||
int MQTTPersistence_persistQueueEntry(Clients* aclient, MQTTPersistence_qEntry* qe)
|
||||
{
|
||||
int rc = 0;
|
||||
int bufindex = 0;
|
||||
char key[KEYSIZE];
|
||||
int chars = 0;
|
||||
int lens[MAX_NO_OF_BUFFERS];
|
||||
void* bufs[MAX_NO_OF_BUFFERS];
|
||||
int props_allocated = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
bufs[bufindex] = &qe->msg->payloadlen;
|
||||
lens[bufindex++] = sizeof(qe->msg->payloadlen);
|
||||
|
||||
bufs[bufindex] = qe->msg->payload;
|
||||
lens[bufindex++] = qe->msg->payloadlen;
|
||||
|
||||
bufs[bufindex] = &qe->msg->qos;
|
||||
lens[bufindex++] = sizeof(qe->msg->qos);
|
||||
|
||||
bufs[bufindex] = &qe->msg->retained;
|
||||
lens[bufindex++] = sizeof(qe->msg->retained);
|
||||
|
||||
bufs[bufindex] = &qe->msg->dup;
|
||||
lens[bufindex++] = sizeof(qe->msg->dup);
|
||||
|
||||
bufs[bufindex] = &qe->msg->msgid;
|
||||
lens[bufindex++] = sizeof(qe->msg->msgid);
|
||||
|
||||
bufs[bufindex] = qe->topicName;
|
||||
lens[bufindex++] = (int)strlen(qe->topicName) + 1;
|
||||
|
||||
bufs[bufindex] = &qe->topicLen;
|
||||
lens[bufindex++] = sizeof(qe->topicLen);
|
||||
|
||||
if (++aclient->qentry_seqno == PERSISTENCE_SEQNO_LIMIT)
|
||||
aclient->qentry_seqno = 0;
|
||||
|
||||
if (aclient->MQTTVersion >= MQTTVERSION_5) /* persist properties */
|
||||
{
|
||||
MQTTProperties no_props = MQTTProperties_initializer;
|
||||
MQTTProperties* props = &no_props;
|
||||
int temp_len = 0;
|
||||
char* ptr = NULL;
|
||||
|
||||
if (qe->msg->struct_version >= 1)
|
||||
props = &qe->msg->properties;
|
||||
|
||||
temp_len = MQTTProperties_len(props);
|
||||
ptr = bufs[bufindex] = malloc(temp_len);
|
||||
if (!ptr)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
props_allocated = bufindex;
|
||||
rc = MQTTProperties_write(&ptr, props);
|
||||
lens[bufindex++] = temp_len;
|
||||
|
||||
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_V5_QUEUE_KEY, aclient->qentry_seqno);
|
||||
}
|
||||
else
|
||||
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_QUEUE_KEY, aclient->qentry_seqno);
|
||||
|
||||
if (chars >= KEYSIZE)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
qe->seqno = aclient->qentry_seqno;
|
||||
|
||||
if (aclient->beforeWrite)
|
||||
rc = aclient->beforeWrite(aclient->beforeWrite_context, bufindex, (char**)bufs, lens);
|
||||
|
||||
if (rc == 0 && (rc = aclient->persistence->pput(aclient->phandle, key, bufindex, (char**)bufs, lens)) != 0)
|
||||
Log(LOG_ERROR, 0, "Error persisting queue entry, rc %d", rc);
|
||||
}
|
||||
if (props_allocated != 0)
|
||||
free(bufs[props_allocated]);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static MQTTPersistence_qEntry* MQTTPersistence_restoreQueueEntry(char* buffer, size_t buflen, int MQTTVersion)
|
||||
{
|
||||
MQTTPersistence_qEntry* qe = NULL;
|
||||
char* ptr = buffer;
|
||||
int data_size;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((qe = malloc(sizeof(MQTTPersistence_qEntry))) == NULL)
|
||||
goto exit;
|
||||
memset(qe, '\0', sizeof(MQTTPersistence_qEntry));
|
||||
|
||||
if ((qe->msg = malloc(sizeof(MQTTPersistence_message))) == NULL)
|
||||
{
|
||||
free(qe);
|
||||
qe = NULL;
|
||||
goto exit;
|
||||
}
|
||||
memset(qe->msg, '\0', sizeof(MQTTPersistence_message));
|
||||
|
||||
qe->msg->struct_version = 1;
|
||||
|
||||
qe->msg->payloadlen = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
data_size = qe->msg->payloadlen;
|
||||
if ((qe->msg->payload = malloc(data_size)) == NULL)
|
||||
{
|
||||
free(qe->msg);
|
||||
free(qe);
|
||||
qe = NULL;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(qe->msg->payload, ptr, data_size);
|
||||
ptr += data_size;
|
||||
|
||||
qe->msg->qos = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
qe->msg->retained = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
qe->msg->dup = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
qe->msg->msgid = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
data_size = (int)strlen(ptr) + 1;
|
||||
if ((qe->topicName = malloc(data_size)) == NULL)
|
||||
{
|
||||
free(qe->msg->payload);
|
||||
free(qe->msg);
|
||||
free(qe);
|
||||
qe = NULL;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(qe->topicName, ptr);
|
||||
ptr += data_size;
|
||||
|
||||
qe->topicLen = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
if (MQTTVersion >= MQTTVERSION_5 &&
|
||||
MQTTProperties_read(&qe->msg->properties, &ptr, buffer + buflen) != 1)
|
||||
Log(LOG_ERROR, -1, "Error restoring properties from persistence");
|
||||
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return qe;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTPersistence_insertInSeqOrder(List* list, MQTTPersistence_qEntry* qEntry, size_t size)
|
||||
{
|
||||
ListElement* index = NULL;
|
||||
ListElement* current = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
while (ListNextElement(list, ¤t) != NULL && index == NULL)
|
||||
{
|
||||
if (qEntry->seqno < ((MQTTPersistence_qEntry*)current->content)->seqno)
|
||||
index = current;
|
||||
}
|
||||
ListInsert(list, qEntry, size, index);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restores a queue of messages from persistence to memory
|
||||
* @param c the client as ::Clients - the client object to restore the messages to
|
||||
* @return return code, 0 if successful
|
||||
*/
|
||||
int MQTTPersistence_restoreMessageQueue(Clients* c)
|
||||
{
|
||||
int rc = 0;
|
||||
char **msgkeys;
|
||||
int nkeys;
|
||||
int i = 0;
|
||||
int entries_restored = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
|
||||
{
|
||||
while (rc == 0 && i < nkeys)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
int buflen;
|
||||
|
||||
if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) != 0 &&
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) != 0)
|
||||
{
|
||||
; /* ignore if not a queue entry key */
|
||||
}
|
||||
else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0 &&
|
||||
(c->afterRead == NULL || (rc = c->afterRead(c->afterRead_context, &buffer, &buflen)) == 0))
|
||||
{
|
||||
int MQTTVersion =
|
||||
(strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) == 0)
|
||||
? MQTTVERSION_5 : MQTTVERSION_3_1_1;
|
||||
MQTTPersistence_qEntry* qe = MQTTPersistence_restoreQueueEntry(buffer, buflen, MQTTVersion);
|
||||
|
||||
if (qe)
|
||||
{
|
||||
qe->seqno = atoi(strchr(msgkeys[i], '-')+1); /* key format is tag'-'seqno */
|
||||
MQTTPersistence_insertInSeqOrder(c->messageQueue, qe, sizeof(MQTTPersistence_qEntry));
|
||||
c->qentry_seqno = max(c->qentry_seqno, qe->seqno);
|
||||
entries_restored++;
|
||||
}
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
}
|
||||
if (msgkeys[i])
|
||||
{
|
||||
free(msgkeys[i]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (msgkeys != NULL)
|
||||
free(msgkeys);
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d queued messages restored for client %s", entries_restored, c->clientID);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
99
3rd/paho.mqtt.c/src/MQTTPersistence.h
Normal file
99
3rd/paho.mqtt.c/src/MQTTPersistence.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - async client updates
|
||||
* Ian Craggs - fix for bug 432903 - queue persistence
|
||||
* Ian Craggs - MQTT V5 updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPERSISTENCE_H)
|
||||
#define MQTTPERSISTENCE_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "Clients.h"
|
||||
#include "MQTTProperties.h"
|
||||
|
||||
/** Stem of the key for a sent PUBLISH QoS1 or QoS2 */
|
||||
#define PERSISTENCE_PUBLISH_SENT "s-"
|
||||
/** Stem of the key for a sent PUBREL */
|
||||
#define PERSISTENCE_PUBREL "sc-"
|
||||
/** Stem of the key for a received PUBLISH QoS2 */
|
||||
#define PERSISTENCE_PUBLISH_RECEIVED "r-"
|
||||
|
||||
/** Stem of the key for a sent MQTT V5 PUBLISH QoS1 or QoS2 */
|
||||
#define PERSISTENCE_V5_PUBLISH_SENT "s5-"
|
||||
/** Stem of the key for a sent MQTT V5 PUBREL */
|
||||
#define PERSISTENCE_V5_PUBREL "sc5-"
|
||||
/** Stem of the key for a received MQTT V5 PUBLISH QoS2 */
|
||||
#define PERSISTENCE_V5_PUBLISH_RECEIVED "r5-"
|
||||
|
||||
/** Stem of the key for an async client command */
|
||||
#define PERSISTENCE_COMMAND_KEY "c-"
|
||||
/** Stem of the key for an MQTT V5 async client command */
|
||||
#define PERSISTENCE_V5_COMMAND_KEY "c5-"
|
||||
/** Stem of the key for an client incoming message queue */
|
||||
#define PERSISTENCE_QUEUE_KEY "q-"
|
||||
/** Stem of the key for an MQTT V5 incoming message queue */
|
||||
#define PERSISTENCE_V5_QUEUE_KEY "q5-"
|
||||
/** Maximum length of a stem for a persistence key */
|
||||
#define PERSISTENCE_MAX_STEM_LENGTH 4
|
||||
/** Maximum allowed length of a persistence key */
|
||||
#define PERSISTENCE_MAX_KEY_LENGTH 10
|
||||
/** Maximum size of an integer sequence number appended to a persistence key */
|
||||
#define PERSISTENCE_SEQNO_LIMIT 1000000 /*10^(PERSISTENCE_MAX_KEY_LENGTH - PERSISTENCE_MAX_STEM_LENGTH)*/
|
||||
|
||||
int MQTTPersistence_create(MQTTClient_persistence** per, int type, void* pcontext);
|
||||
int MQTTPersistence_initialize(Clients* c, const char* serverURI);
|
||||
int MQTTPersistence_close(Clients* c);
|
||||
int MQTTPersistence_clear(Clients* c);
|
||||
int MQTTPersistence_restorePackets(Clients* c);
|
||||
void* MQTTPersistence_restorePacket(int MQTTVersion, char* buffer, size_t buflen);
|
||||
void MQTTPersistence_insertInOrder(List* list, void* content, size_t size);
|
||||
int MQTTPersistence_putPacket(SOCKET socket, char* buf0, size_t buf0len, int count,
|
||||
char** buffers, size_t* buflens, int htype, int msgId, int scr, int MQTTVersion);
|
||||
int MQTTPersistence_remove(Clients* c, char* type, int qos, int msgId);
|
||||
void MQTTPersistence_wrapMsgID(Clients *c);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char struct_id[4];
|
||||
int struct_version;
|
||||
int payloadlen;
|
||||
void* payload;
|
||||
int qos;
|
||||
int retained;
|
||||
int dup;
|
||||
int msgid;
|
||||
MQTTProperties properties;
|
||||
} MQTTPersistence_message;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MQTTPersistence_message* msg;
|
||||
char* topicName;
|
||||
int topicLen;
|
||||
unsigned int seqno; /* only used on restore */
|
||||
} MQTTPersistence_qEntry;
|
||||
|
||||
int MQTTPersistence_unpersistQueueEntry(Clients* client, MQTTPersistence_qEntry* qe);
|
||||
int MQTTPersistence_persistQueueEntry(Clients* aclient, MQTTPersistence_qEntry* qe);
|
||||
int MQTTPersistence_restoreMessageQueue(Clients* c);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1022
3rd/paho.mqtt.c/src/MQTTPersistenceDefault.c
Normal file
1022
3rd/paho.mqtt.c/src/MQTTPersistenceDefault.c
Normal file
@@ -0,0 +1,1022 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2024 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - async client updates
|
||||
* Ian Craggs - fix for bug 484496
|
||||
* Ian Craggs - fix for issue 285
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief A file system based persistence implementation.
|
||||
*
|
||||
* A directory is specified when the MQTT client is created. When the persistence is then
|
||||
* opened (see ::Persistence_open), a sub-directory is made beneath the base for this
|
||||
* particular client ID and connection key. This allows one persistence base directory to
|
||||
* be shared by multiple clients.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
|
||||
#include "OsWrapper.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <direct.h>
|
||||
/* Windows doesn't have strtok_r, so remap it to strtok_s */
|
||||
#define strtok_r strtok_s
|
||||
#define snprintf _snprintf
|
||||
int keysWin32(char *, char ***, int *);
|
||||
int clearWin32(char *);
|
||||
int containskeyWin32(char *, char *);
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
int keysUnix(char *, char ***, int *);
|
||||
int clearUnix(char *);
|
||||
int containskeyUnix(char *, char *);
|
||||
#endif
|
||||
|
||||
#include "MQTTClientPersistence.h"
|
||||
#include "MQTTPersistenceDefault.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Heap.h"
|
||||
|
||||
/** Create persistence directory for the client: context/clientID-serverURI.
|
||||
* See ::Persistence_open
|
||||
*/
|
||||
|
||||
int pstopen(void **handle, const char* clientID, const char* serverURI, void* context)
|
||||
{
|
||||
int rc = 0;
|
||||
char *dataDir = context;
|
||||
char *clientDir;
|
||||
char *pToken = NULL;
|
||||
char *save_ptr = NULL;
|
||||
char *pCrtDirName = NULL;
|
||||
char *pTokDirName = NULL;
|
||||
char *perserverURI = NULL, *ptraux;
|
||||
size_t alloclen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* Note that serverURI=address:port, but ":" not allowed in Windows directories */
|
||||
if ((perserverURI = malloc(strlen(serverURI) + 1)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(perserverURI, serverURI);
|
||||
while ((ptraux = strstr(perserverURI, ":")) != NULL)
|
||||
*ptraux = '-' ;
|
||||
|
||||
/* consider '/' + '-' + '\0' */
|
||||
alloclen = strlen(dataDir) + strlen(clientID) + strlen(perserverURI) + 3;
|
||||
clientDir = malloc(alloclen);
|
||||
if (!clientDir)
|
||||
{
|
||||
free(perserverURI);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(clientDir, alloclen, "%s/%s-%s", dataDir, clientID, perserverURI) >= alloclen)
|
||||
{
|
||||
free(clientDir);
|
||||
free(perserverURI);
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* create clientDir directory */
|
||||
|
||||
/* pCrtDirName - holds the directory name we are currently trying to create. */
|
||||
/* This gets built up level by level untipwdl the full path name is created.*/
|
||||
/* pTokDirName - holds the directory name that gets used by strtok. */
|
||||
if ((pCrtDirName = (char*)malloc(strlen(clientDir) + 1)) == NULL)
|
||||
{
|
||||
free(clientDir);
|
||||
free(perserverURI);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if ((pTokDirName = (char*)malloc( strlen(clientDir) + 1 )) == NULL)
|
||||
{
|
||||
free(pCrtDirName);
|
||||
free(clientDir);
|
||||
free(perserverURI);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
strcpy( pTokDirName, clientDir );
|
||||
|
||||
/* If first character is directory separator, make sure it's in the created directory name #285 */
|
||||
if (*pTokDirName == '/' || *pTokDirName == '\\')
|
||||
{
|
||||
*pCrtDirName = *pTokDirName;
|
||||
pToken = strtok_r( pTokDirName + 1, "\\/", &save_ptr );
|
||||
strcpy( pCrtDirName + 1, pToken );
|
||||
}
|
||||
else
|
||||
{
|
||||
pToken = strtok_r( pTokDirName, "\\/", &save_ptr );
|
||||
strcpy( pCrtDirName, pToken );
|
||||
}
|
||||
|
||||
rc = pstmkdir( pCrtDirName );
|
||||
pToken = strtok_r( NULL, "\\/", &save_ptr );
|
||||
while ( (pToken != NULL) && (rc == 0) )
|
||||
{
|
||||
/* Append the next directory level and try to create it */
|
||||
strcat( pCrtDirName, "/" );
|
||||
strcat( pCrtDirName, pToken );
|
||||
rc = pstmkdir( pCrtDirName );
|
||||
pToken = strtok_r( NULL, "\\/", &save_ptr );
|
||||
}
|
||||
|
||||
*handle = clientDir;
|
||||
|
||||
free(pTokDirName);
|
||||
free(pCrtDirName);
|
||||
free(perserverURI);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** Function to create a directory.
|
||||
* Returns 0 on success or if the directory already exists.
|
||||
*/
|
||||
int pstmkdir( char *pPathname )
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if defined(_WIN32)
|
||||
if ( _mkdir( pPathname ) != 0 )
|
||||
{
|
||||
#else
|
||||
/* Create a directory with read, write and execute access for the owner and read access for the group */
|
||||
#if !defined(_WRS_KERNEL)
|
||||
if ( mkdir( pPathname, S_IRWXU | S_IRGRP ) != 0 )
|
||||
#else
|
||||
if ( mkdir( pPathname ) != 0 )
|
||||
#endif /* !defined(_WRS_KERNEL) */
|
||||
{
|
||||
#endif
|
||||
if ( errno != EEXIST )
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Write wire message to the client persistence directory.
|
||||
* See ::Persistence_put
|
||||
*/
|
||||
int pstput(void* handle, char* key, int bufcount, char* buffers[], int buflens[])
|
||||
{
|
||||
int rc = 0;
|
||||
char *clientDir = handle;
|
||||
char *file;
|
||||
FILE *fp;
|
||||
size_t bytesWritten = 0,
|
||||
bytesTotal = 0;
|
||||
int i;
|
||||
size_t alloclen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (clientDir == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* consider '/' + '\0' */
|
||||
alloclen = strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2;
|
||||
file = malloc(alloclen);
|
||||
if (!file)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(file, alloclen, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION) >= alloclen)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
fp = fopen(file, "wb");
|
||||
if ( fp != NULL )
|
||||
{
|
||||
for(i=0; i<bufcount; i++)
|
||||
{
|
||||
bytesTotal += buflens[i];
|
||||
bytesWritten += fwrite(buffers[i], sizeof(char), buflens[i], fp );
|
||||
}
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
} else
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
|
||||
if (bytesWritten != bytesTotal)
|
||||
{
|
||||
pstremove(handle, key);
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
|
||||
free_exit:
|
||||
free(file);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/** Retrieve a wire message from the client persistence directory.
|
||||
* See ::Persistence_get
|
||||
*/
|
||||
int pstget(void* handle, char* key, char** buffer, int* buflen)
|
||||
{
|
||||
int rc = 0;
|
||||
FILE *fp = NULL;
|
||||
char *clientDir = handle;
|
||||
char *filename = NULL;
|
||||
char *buf = NULL;
|
||||
unsigned long fileLen = 0;
|
||||
unsigned long bytesRead = 0;
|
||||
size_t alloclen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (clientDir == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* consider '/' + '\0' */
|
||||
alloclen = strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2;
|
||||
filename = malloc(alloclen);
|
||||
if (!filename)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(filename, alloclen, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION) >= alloclen)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
free(filename);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fp = fopen(filename, "rb");
|
||||
free(filename);
|
||||
if (fp != NULL)
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
fileLen = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
if ((buf = (char *)malloc(fileLen)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
bytesRead = (int)fread(buf, sizeof(char), fileLen, fp);
|
||||
*buffer = buf;
|
||||
*buflen = bytesRead;
|
||||
if ( bytesRead != fileLen )
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
fclose(fp);
|
||||
} else
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
|
||||
/* the caller must free buf */
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Delete a persisted message from the client persistence directory.
|
||||
* See ::Persistence_remove
|
||||
*/
|
||||
int pstremove(void* handle, char* key)
|
||||
{
|
||||
int rc = 0;
|
||||
char *clientDir = handle;
|
||||
char *file;
|
||||
size_t alloclen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (clientDir == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* consider '/' + '\0' */
|
||||
/* consider '/' + '\0' */
|
||||
alloclen = strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2;
|
||||
file = malloc(alloclen);
|
||||
if (!file)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(file, alloclen, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION) >= alloclen)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
if ( _unlink(file) != 0 )
|
||||
{
|
||||
#else
|
||||
if ( unlink(file) != 0 )
|
||||
{
|
||||
#endif
|
||||
if ( errno != ENOENT )
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
free(file);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/** Delete client persistence directory (if empty).
|
||||
* See ::Persistence_close
|
||||
*/
|
||||
int pstclose(void* handle)
|
||||
{
|
||||
int rc = 0;
|
||||
char *clientDir = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (clientDir == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
if ( _rmdir(clientDir) != 0 )
|
||||
{
|
||||
#else
|
||||
if ( rmdir(clientDir) != 0 )
|
||||
{
|
||||
#endif
|
||||
if ( errno != ENOENT && errno != ENOTEMPTY )
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
|
||||
free(clientDir);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/** Returns whether if a wire message is persisted in the client persistence directory.
|
||||
* See ::Persistence_containskey
|
||||
*/
|
||||
int pstcontainskey(void *handle, char *key)
|
||||
{
|
||||
int rc = 0;
|
||||
char *clientDir = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (clientDir == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
rc = containskeyWin32(clientDir, key);
|
||||
#else
|
||||
rc = containskeyUnix(clientDir, key);
|
||||
#endif
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
int containskeyWin32(char *dirname, char *key)
|
||||
{
|
||||
int notFound = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
int fFinished = 0;
|
||||
char *filekey, *ptraux;
|
||||
#if defined(_WIN32)
|
||||
#define DIRSIZE MAX_PATH+1
|
||||
#else
|
||||
const size_t DIRSIZE = MAX_PATH+1;
|
||||
#endif
|
||||
char dir[DIRSIZE];
|
||||
WIN32_FIND_DATAA FileData;
|
||||
HANDLE hDir;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (snprintf(dir, DIRSIZE, "%s/*", dirname) >= DIRSIZE)
|
||||
goto exit;
|
||||
|
||||
hDir = FindFirstFileA(dir, &FileData);
|
||||
if (hDir != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
while (!fFinished)
|
||||
{
|
||||
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
|
||||
{
|
||||
if ((filekey = malloc(strlen(FileData.cFileName) + 1)) == NULL)
|
||||
{
|
||||
notFound = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(filekey, FileData.cFileName);
|
||||
ptraux = strstr(filekey, MESSAGE_FILENAME_EXTENSION);
|
||||
if ( ptraux != NULL )
|
||||
*ptraux = '\0' ;
|
||||
if(strcmp(filekey, key) == 0)
|
||||
{
|
||||
notFound = 0;
|
||||
fFinished = 1;
|
||||
}
|
||||
free(filekey);
|
||||
}
|
||||
if (!FindNextFileA(hDir, &FileData))
|
||||
{
|
||||
if (GetLastError() == ERROR_NO_MORE_FILES)
|
||||
fFinished = 1;
|
||||
}
|
||||
}
|
||||
FindClose(hDir);
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT_RC(notFound);
|
||||
return notFound;
|
||||
}
|
||||
#else
|
||||
int containskeyUnix(char *dirname, char *key)
|
||||
{
|
||||
int notFound = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
char *filekey, *ptraux;
|
||||
DIR *dp = NULL;
|
||||
struct dirent *dir_entry;
|
||||
struct stat stat_info;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if((dp = opendir(dirname)) != NULL)
|
||||
{
|
||||
while((dir_entry = readdir(dp)) != NULL && notFound)
|
||||
{
|
||||
const size_t allocsize = strlen(dirname) + strlen(dir_entry->d_name) + 2;
|
||||
char* filename = malloc(allocsize);
|
||||
|
||||
if (!filename)
|
||||
{
|
||||
notFound = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(filename, allocsize, "%s/%s", dirname, dir_entry->d_name) >= allocsize)
|
||||
{
|
||||
free(filename);
|
||||
notFound = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
lstat(filename, &stat_info);
|
||||
free(filename);
|
||||
if(S_ISREG(stat_info.st_mode))
|
||||
{
|
||||
if ((filekey = malloc(strlen(dir_entry->d_name) + 1)) == NULL)
|
||||
{
|
||||
notFound = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(filekey, dir_entry->d_name);
|
||||
ptraux = strstr(filekey, MESSAGE_FILENAME_EXTENSION);
|
||||
if ( ptraux != NULL )
|
||||
*ptraux = '\0' ;
|
||||
if(strcmp(filekey, key) == 0)
|
||||
notFound = 0;
|
||||
free(filekey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (dp)
|
||||
closedir(dp);
|
||||
FUNC_EXIT_RC(notFound);
|
||||
return notFound;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/** Delete all the persisted message in the client persistence directory.
|
||||
* See ::Persistence_clear
|
||||
*/
|
||||
int pstclear(void *handle)
|
||||
{
|
||||
int rc = 0;
|
||||
char *clientDir = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (clientDir == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
rc = clearWin32(clientDir);
|
||||
#else
|
||||
rc = clearUnix(clientDir);
|
||||
#endif
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
int clearWin32(char *dirname)
|
||||
{
|
||||
int rc = 0;
|
||||
int fFinished = 0;
|
||||
char *file;
|
||||
char dir[DIRSIZE];
|
||||
WIN32_FIND_DATAA FileData;
|
||||
HANDLE hDir;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (snprintf(dir, DIRSIZE, "%s/*", dirname) >= DIRSIZE)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hDir = FindFirstFileA(dir, &FileData);
|
||||
if (hDir != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
while (!fFinished)
|
||||
{
|
||||
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
|
||||
{
|
||||
size_t allocsize = strlen(dirname) + strlen(FileData.cFileName) + 2;
|
||||
|
||||
file = malloc(allocsize);
|
||||
if (!file)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(file, allocsize, "%s/%s", dirname, FileData.cFileName) >= allocsize)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
free(file);
|
||||
goto exit;
|
||||
}
|
||||
rc = remove(file);
|
||||
free(file);
|
||||
if ( rc != 0 )
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!FindNextFileA(hDir, &FileData))
|
||||
{
|
||||
if (GetLastError() == ERROR_NO_MORE_FILES)
|
||||
fFinished = 1;
|
||||
}
|
||||
}
|
||||
FindClose(hDir);
|
||||
} else
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
int clearUnix(char *dirname)
|
||||
{
|
||||
int rc = 0;
|
||||
DIR *dp;
|
||||
struct dirent *dir_entry;
|
||||
struct stat stat_info;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if((dp = opendir(dirname)) != NULL)
|
||||
{
|
||||
while((dir_entry = readdir(dp)) != NULL && rc == 0)
|
||||
{
|
||||
if (lstat(dir_entry->d_name, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
|
||||
{
|
||||
if (remove(dir_entry->d_name) != 0 && errno != ENOENT)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
} else
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/** Returns the keys (file names w/o the extension) in the client persistence directory.
|
||||
* See ::Persistence_keys
|
||||
*/
|
||||
int pstkeys(void *handle, char ***keys, int *nkeys)
|
||||
{
|
||||
int rc = 0;
|
||||
char *clientDir = handle;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (clientDir == NULL)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
rc = keysWin32(clientDir, keys, nkeys);
|
||||
#else
|
||||
rc = keysUnix(clientDir, keys, nkeys);
|
||||
#endif
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
int keysWin32(char *dirname, char ***keys, int *nkeys)
|
||||
{
|
||||
int rc = 0;
|
||||
char **fkeys = NULL;
|
||||
int nfkeys = 0;
|
||||
char dir[DIRSIZE];
|
||||
WIN32_FIND_DATAA FileData;
|
||||
HANDLE hDir;
|
||||
int fFinished = 0;
|
||||
char *ptraux;
|
||||
int i;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (snprintf(dir, DIRSIZE, "%s/*", dirname) >= DIRSIZE)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* get number of keys */
|
||||
hDir = FindFirstFileA(dir, &FileData);
|
||||
if (hDir != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
while (!fFinished)
|
||||
{
|
||||
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
|
||||
nfkeys++;
|
||||
if (!FindNextFileA(hDir, &FileData))
|
||||
{
|
||||
if (GetLastError() == ERROR_NO_MORE_FILES)
|
||||
fFinished = 1;
|
||||
}
|
||||
}
|
||||
FindClose(hDir);
|
||||
} else
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (nfkeys != 0)
|
||||
{
|
||||
if ((fkeys = (char **)malloc(nfkeys * sizeof(char *))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy the keys */
|
||||
hDir = FindFirstFileA(dir, &FileData);
|
||||
if (hDir != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
fFinished = 0;
|
||||
i = 0;
|
||||
while (!fFinished)
|
||||
{
|
||||
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
|
||||
{
|
||||
if ((fkeys[i] = malloc(strlen(FileData.cFileName) + 1)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(fkeys[i], FileData.cFileName);
|
||||
ptraux = strstr(fkeys[i], MESSAGE_FILENAME_EXTENSION);
|
||||
if ( ptraux != NULL )
|
||||
*ptraux = '\0' ;
|
||||
i++;
|
||||
}
|
||||
if (!FindNextFileA(hDir, &FileData))
|
||||
{
|
||||
if (GetLastError() == ERROR_NO_MORE_FILES)
|
||||
fFinished = 1;
|
||||
}
|
||||
}
|
||||
FindClose(hDir);
|
||||
} else
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*nkeys = nfkeys;
|
||||
*keys = fkeys;
|
||||
/* the caller must free keys */
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
int keysUnix(char *dirname, char ***keys, int *nkeys)
|
||||
{
|
||||
int rc = 0;
|
||||
char **fkeys = NULL;
|
||||
int nfkeys = 0;
|
||||
char *ptraux;
|
||||
int i;
|
||||
DIR *dp = NULL;
|
||||
struct dirent *dir_entry;
|
||||
struct stat stat_info;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* get number of keys */
|
||||
if((dp = opendir(dirname)) != NULL)
|
||||
{
|
||||
while((dir_entry = readdir(dp)) != NULL)
|
||||
{
|
||||
size_t allocsize = strlen(dirname)+strlen(dir_entry->d_name)+2;
|
||||
char* temp = malloc(allocsize);
|
||||
|
||||
if (!temp)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(temp, allocsize, "%s/%s", dirname, dir_entry->d_name) >= allocsize)
|
||||
{
|
||||
free(temp);
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (lstat(temp, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
|
||||
nfkeys++;
|
||||
free(temp);
|
||||
}
|
||||
closedir(dp);
|
||||
dp = NULL;
|
||||
} else
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (nfkeys != 0)
|
||||
{
|
||||
if ((fkeys = (char **)malloc(nfkeys * sizeof(char *))) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* copy the keys */
|
||||
if((dp = opendir(dirname)) != NULL)
|
||||
{
|
||||
i = 0;
|
||||
while((dir_entry = readdir(dp)) != NULL)
|
||||
{
|
||||
size_t allocsize = strlen(dirname)+strlen(dir_entry->d_name)+2;
|
||||
char* temp = malloc(allocsize);
|
||||
|
||||
if (!temp)
|
||||
{
|
||||
int n = 0;
|
||||
for (n = 0; n < i; n++)
|
||||
free(fkeys[n]);
|
||||
free(fkeys);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(temp, allocsize, "%s/%s", dirname, dir_entry->d_name) >= allocsize)
|
||||
{
|
||||
int n = 0;
|
||||
for (n = 0; n < i; n++)
|
||||
free(fkeys[n]);
|
||||
free(temp);
|
||||
free(fkeys);
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (lstat(temp, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
|
||||
{
|
||||
if ((fkeys[i] = malloc(strlen(dir_entry->d_name) + 1)) == NULL)
|
||||
{
|
||||
int n = 0;
|
||||
for (n = 0; n < i; n++)
|
||||
free(fkeys[n]);
|
||||
free(temp);
|
||||
free(fkeys);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(fkeys[i], dir_entry->d_name);
|
||||
ptraux = strstr(fkeys[i], MESSAGE_FILENAME_EXTENSION);
|
||||
if ( ptraux != NULL )
|
||||
*ptraux = '\0' ;
|
||||
i++;
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
} else
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
*nkeys = nfkeys;
|
||||
*keys = fkeys;
|
||||
/* the caller must free keys */
|
||||
|
||||
exit:
|
||||
if (dp)
|
||||
closedir(dp);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
#define MSTEM "m-"
|
||||
#define NMSGS 10
|
||||
#define NBUFS 4
|
||||
#define NDEL 2
|
||||
#define RC !rc ? "(Success)" : "(Failed) "
|
||||
|
||||
int rc;
|
||||
char *handle;
|
||||
char *perdir = ".";
|
||||
const char *clientID = "TheUTClient";
|
||||
const char *serverURI = "127.0.0.1:1883";
|
||||
|
||||
char *stem = MSTEM;
|
||||
int msgId, i;
|
||||
int nm[NDEL] = {5 , 8}; /* msgIds to get and remove */
|
||||
|
||||
char *key;
|
||||
char **keys;
|
||||
int nkeys;
|
||||
char *buffer, *buff;
|
||||
int buflen;
|
||||
|
||||
int nbufs = NBUFS;
|
||||
char *bufs[NBUFS] = {"m0", "mm1", "mmm2" , "mmmm3"}; /* message content */
|
||||
int buflens[NBUFS];
|
||||
for(i=0;i<nbufs;i++)
|
||||
buflens[i]=strlen(bufs[i]);
|
||||
|
||||
/* open */
|
||||
/* printf("Persistence directory : %s\n", perdir); */
|
||||
rc = pstopen((void**)&handle, clientID, serverURI, perdir);
|
||||
printf("%s Persistence directory for client %s : %s\n", RC, clientID, handle);
|
||||
|
||||
/* put */
|
||||
for(msgId=0;msgId<NMSGS;msgId++)
|
||||
{
|
||||
key = malloc(PERSISTENCE_MAX_KEY_LENGTH + 1);
|
||||
sprintf(key, "%s%d", stem, msgId);
|
||||
rc = pstput(handle, key, nbufs, bufs, buflens);
|
||||
printf("%s Adding message %s\n", RC, key);
|
||||
free(key);
|
||||
}
|
||||
|
||||
/* keys ,ie, list keys added */
|
||||
rc = pstkeys(handle, &keys, &nkeys);
|
||||
printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
|
||||
for(i=0;i<nkeys;i++)
|
||||
printf("%13s\n", keys[i]);
|
||||
|
||||
if (keys !=NULL)
|
||||
free(keys);
|
||||
|
||||
/* containskey */
|
||||
for(i=0;i<NDEL;i++)
|
||||
{
|
||||
key = malloc(PERSISTENCE_MAX_KEY_LENGTH + 1);
|
||||
sprintf(key, "%s%d", stem, nm[i]);
|
||||
rc = pstcontainskey(handle, key);
|
||||
printf("%s Message %s is persisted ?\n", RC, key);
|
||||
free(key);
|
||||
}
|
||||
|
||||
/* get && remove*/
|
||||
for(i=0;i<NDEL;i++)
|
||||
{
|
||||
key = malloc(PERSISTENCE_MAX_KEY_LENGTH + 1);
|
||||
sprintf(key, "%s%d", stem, nm[i]);
|
||||
rc = pstget(handle, key, &buffer, &buflen);
|
||||
buff = malloc(buflen+1);
|
||||
memcpy(buff, buffer, buflen);
|
||||
buff[buflen] = '\0';
|
||||
printf("%s Retrieving message %s : %s\n", RC, key, buff);
|
||||
rc = pstremove(handle, key);
|
||||
printf("%s Removing message %s\n", RC, key);
|
||||
free(key);
|
||||
free(buff);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/* containskey */
|
||||
for(i=0;i<NDEL;i++)
|
||||
{
|
||||
key = malloc(PERSISTENCE_MAX_KEY_LENGTH + 1);
|
||||
sprintf(key, "%s%d", stem, nm[i]);
|
||||
rc = pstcontainskey(handle, key);
|
||||
printf("%s Message %s is persisted ?\n", RC, key);
|
||||
free(key);
|
||||
}
|
||||
|
||||
/* keys ,ie, list keys added */
|
||||
rc = pstkeys(handle, &keys, &nkeys);
|
||||
printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
|
||||
for(i=0;i<nkeys;i++)
|
||||
printf("%13s\n", keys[i]);
|
||||
|
||||
if (keys != NULL)
|
||||
free(keys);
|
||||
|
||||
|
||||
/* close -> it will fail, since client persistence directory is not empty */
|
||||
rc = pstclose(&handle);
|
||||
printf("%s Closing client persistence directory for client %s\n", RC, clientID);
|
||||
|
||||
/* clear */
|
||||
rc = pstclear(handle);
|
||||
printf("%s Deleting all persisted messages in %s\n", RC, handle);
|
||||
|
||||
/* keys ,ie, list keys added */
|
||||
rc = pstkeys(handle, &keys, &nkeys);
|
||||
printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
|
||||
for(i=0;i<nkeys;i++)
|
||||
printf("%13s\n", keys[i]);
|
||||
|
||||
if ( keys != NULL )
|
||||
free(keys);
|
||||
|
||||
/* close */
|
||||
rc = pstclose(&handle);
|
||||
printf("%s Closing client persistence directory for client %s\n", RC, clientID);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* NO_PERSISTENCE */
|
||||
36
3rd/paho.mqtt.c/src/MQTTPersistenceDefault.h
Normal file
36
3rd/paho.mqtt.c/src/MQTTPersistenceDefault.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2018 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPERSISTENCEDEFAULT_H)
|
||||
#define MQTTPERSISTENCEDEFAULT_H
|
||||
|
||||
/** Extension of the filename */
|
||||
#define MESSAGE_FILENAME_EXTENSION ".msg"
|
||||
|
||||
/* prototypes of the functions for the default file system persistence */
|
||||
int pstopen(void** handle, const char* clientID, const char* serverURI, void* context);
|
||||
int pstclose(void* handle);
|
||||
int pstput(void* handle, char* key, int bufcount, char* buffers[], int buflens[]);
|
||||
int pstget(void* handle, char* key, char** buffer, int* buflen);
|
||||
int pstremove(void* handle, char* key);
|
||||
int pstkeys(void* handle, char*** keys, int* nkeys);
|
||||
int pstclear(void* handle);
|
||||
int pstcontainskey(void* handle, char* key);
|
||||
|
||||
int pstmkdir(char *pPathname);
|
||||
|
||||
#endif
|
||||
|
||||
578
3rd/paho.mqtt.c/src/MQTTProperties.c
Normal file
578
3rd/paho.mqtt.c/src/MQTTProperties.c
Normal file
@@ -0,0 +1,578 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2024 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTProperties.h"
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "MQTTProtocolClient.h"
|
||||
#include "Heap.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
static struct nameToType
|
||||
{
|
||||
enum MQTTPropertyCodes name;
|
||||
enum MQTTPropertyTypes type;
|
||||
} namesToTypes[] =
|
||||
{
|
||||
{MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_CONTENT_TYPE, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_RESPONSE_TOPIC, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_CORRELATION_DATA, MQTTPROPERTY_TYPE_BINARY_DATA},
|
||||
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER, MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFIER, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_AUTHENTICATION_METHOD, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_AUTHENTICATION_DATA, MQTTPROPERTY_TYPE_BINARY_DATA},
|
||||
{MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_RESPONSE_INFORMATION, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_SERVER_REFERENCE, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_REASON_STRING, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_RECEIVE_MAXIMUM, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_TOPIC_ALIAS, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_MAXIMUM_QOS, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_RETAIN_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_USER_PROPERTY, MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR},
|
||||
{MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE, MQTTPROPERTY_TYPE_BYTE}
|
||||
};
|
||||
|
||||
|
||||
static char* datadup(const MQTTLenString* str)
|
||||
{
|
||||
char* temp = malloc(str->len);
|
||||
if (temp)
|
||||
memcpy(temp, str->data, str->len);
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperty_getType(enum MQTTPropertyCodes value)
|
||||
{
|
||||
int i, rc = -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(namesToTypes); ++i)
|
||||
{
|
||||
if (namesToTypes[i].name == value)
|
||||
{
|
||||
rc = namesToTypes[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_len(const MQTTProperties* props)
|
||||
{
|
||||
/* properties length is an mbi */
|
||||
return (props == NULL) ? 1 : props->length + MQTTPacket_VBIlen(props->length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new property to a property list
|
||||
* @param props the property list
|
||||
* @param prop the new property
|
||||
* @return code 0 is success
|
||||
*/
|
||||
int MQTTProperties_add(MQTTProperties* props, const MQTTProperty* prop)
|
||||
{
|
||||
int rc = 0, type;
|
||||
|
||||
if (props == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if ((type = MQTTProperty_getType(prop->identifier)) < 0)
|
||||
{
|
||||
/*StackTrace_printStack(stdout);*/
|
||||
rc = MQTT_INVALID_PROPERTY_ID;
|
||||
goto exit;
|
||||
}
|
||||
else if (props->array == NULL)
|
||||
{
|
||||
props->max_count = 10;
|
||||
props->array = malloc(sizeof(MQTTProperty) * props->max_count);
|
||||
}
|
||||
else if (props->count == props->max_count)
|
||||
{
|
||||
props->max_count += 10;
|
||||
void* newPtr = realloc(props->array, sizeof(MQTTProperty) * props->max_count);
|
||||
if (newPtr == NULL)
|
||||
{
|
||||
free(props->array);
|
||||
props->array = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
props->array = newPtr;
|
||||
}
|
||||
}
|
||||
|
||||
if (props->array)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
props->array[props->count++] = *prop;
|
||||
/* calculate length */
|
||||
switch (type)
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BYTE:
|
||||
len = 1;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
|
||||
len = 2;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
|
||||
len = 4;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
|
||||
len = MQTTPacket_VBIlen(prop->value.integer4);
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_BINARY_DATA:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
|
||||
len = 2 + prop->value.data.len;
|
||||
props->array[props->count-1].value.data.data = datadup(&prop->value.data);
|
||||
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
{
|
||||
len += 2 + prop->value.value.len;
|
||||
props->array[props->count-1].value.value.data = datadup(&prop->value.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
props->length += len + 1; /* add identifier byte */
|
||||
}
|
||||
else
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperty_write(char** pptr, MQTTProperty* prop)
|
||||
{
|
||||
int rc = -1,
|
||||
type = -1;
|
||||
|
||||
type = MQTTProperty_getType(prop->identifier);
|
||||
if (type >= MQTTPROPERTY_TYPE_BYTE && type <= MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
{
|
||||
writeChar(pptr, prop->identifier);
|
||||
switch (type)
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BYTE:
|
||||
writeChar(pptr, prop->value.byte);
|
||||
rc = 1;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
|
||||
writeInt(pptr, prop->value.integer2);
|
||||
rc = 2;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
|
||||
writeInt4(pptr, prop->value.integer4);
|
||||
rc = 4;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
|
||||
rc = MQTTPacket_encode(*pptr, prop->value.integer4);
|
||||
*pptr += rc;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_BINARY_DATA:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
|
||||
writeMQTTLenString(pptr, prop->value.data);
|
||||
rc = prop->value.data.len + 2; /* include length field */
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
|
||||
writeMQTTLenString(pptr, prop->value.data);
|
||||
writeMQTTLenString(pptr, prop->value.value);
|
||||
rc = prop->value.data.len + prop->value.value.len + 4; /* include length fields */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc + 1; /* include identifier byte */
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_write(char** pptr, const MQTTProperties* properties)
|
||||
{
|
||||
int rc = -1;
|
||||
int i = 0, len = 0;
|
||||
|
||||
/* write the entire property list length first */
|
||||
if (properties == NULL)
|
||||
{
|
||||
*pptr += MQTTPacket_encode(*pptr, 0);
|
||||
rc = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pptr += MQTTPacket_encode(*pptr, properties->length);
|
||||
len = rc = 1;
|
||||
for (i = 0; i < properties->count; ++i)
|
||||
{
|
||||
rc = MQTTProperty_write(pptr, &properties->array[i]);
|
||||
if (rc < 0)
|
||||
break;
|
||||
else
|
||||
len += rc;
|
||||
}
|
||||
if (rc >= 0)
|
||||
rc = len;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperty_read(MQTTProperty* prop, char** pptr, char* enddata)
|
||||
{
|
||||
int type = -1,
|
||||
len = -1;
|
||||
|
||||
memset(prop, 0, sizeof(MQTTProperty));
|
||||
prop->identifier = readChar(pptr);
|
||||
type = MQTTProperty_getType(prop->identifier);
|
||||
if (type >= MQTTPROPERTY_TYPE_BYTE && type <= MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BYTE:
|
||||
prop->value.byte = readChar(pptr);
|
||||
len = 1;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
|
||||
prop->value.integer2 = readInt(pptr);
|
||||
len = 2;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
|
||||
prop->value.integer4 = readInt4(pptr);
|
||||
len = 4;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
|
||||
len = MQTTPacket_decodeBuf(*pptr, &prop->value.integer4);
|
||||
*pptr += len;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_BINARY_DATA:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
|
||||
if ((len = MQTTLenStringRead(&prop->value.data, pptr, enddata)) == -1)
|
||||
break; /* error */
|
||||
if ((prop->value.data.data = datadup(&prop->value.data)) == NULL)
|
||||
{
|
||||
len = -1;
|
||||
break; /* error */
|
||||
}
|
||||
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
{
|
||||
int proplen = MQTTLenStringRead(&prop->value.value, pptr, enddata);
|
||||
|
||||
if (proplen == -1)
|
||||
{
|
||||
len = -1;
|
||||
free(prop->value.data.data);
|
||||
break;
|
||||
}
|
||||
len += proplen;
|
||||
if ((prop->value.value.data = datadup(&prop->value.value)) == NULL)
|
||||
{
|
||||
len = -1;
|
||||
free(prop->value.data.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (len == -1) ? -1 : len + 1; /* 1 byte for identifier */
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_read(MQTTProperties* properties, char** pptr, char* enddata)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned int remlength = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* we assume an initialized properties structure */
|
||||
if (enddata - (*pptr) > 0) /* enough length to read the VBI? */
|
||||
{
|
||||
int proplen = 0;
|
||||
|
||||
*pptr += MQTTPacket_decodeBuf(*pptr, &remlength);
|
||||
properties->length = remlength;
|
||||
while (remlength > 0)
|
||||
{
|
||||
if (properties->count == properties->max_count)
|
||||
{
|
||||
properties->max_count += 10;
|
||||
if (properties->max_count == 10)
|
||||
properties->array = malloc(sizeof(MQTTProperty) * properties->max_count);
|
||||
else
|
||||
{
|
||||
void* newPtr = realloc(properties->array, sizeof(MQTTProperty) * properties->max_count);
|
||||
if (newPtr == NULL)
|
||||
{
|
||||
free(properties->array);
|
||||
properties->array = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
properties->array = newPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (properties->array == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if ((proplen = MQTTProperty_read(&properties->array[properties->count], pptr, enddata)) > 0)
|
||||
remlength -= proplen;
|
||||
else
|
||||
break;
|
||||
properties->count++;
|
||||
}
|
||||
if (remlength == 0)
|
||||
rc = 1; /* data read successfully */
|
||||
}
|
||||
|
||||
if (rc != 1 && properties->array != NULL)
|
||||
MQTTProperties_free(properties);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct {
|
||||
enum MQTTPropertyCodes value;
|
||||
const char* name;
|
||||
} nameToString[] =
|
||||
{
|
||||
{MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR, "PAYLOAD_FORMAT_INDICATOR"},
|
||||
{MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL, "MESSAGE_EXPIRY_INTERVAL"},
|
||||
{MQTTPROPERTY_CODE_CONTENT_TYPE, "CONTENT_TYPE"},
|
||||
{MQTTPROPERTY_CODE_RESPONSE_TOPIC, "RESPONSE_TOPIC"},
|
||||
{MQTTPROPERTY_CODE_CORRELATION_DATA, "CORRELATION_DATA"},
|
||||
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER, "SUBSCRIPTION_IDENTIFIER"},
|
||||
{MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL, "SESSION_EXPIRY_INTERVAL"},
|
||||
{MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFIER, "ASSIGNED_CLIENT_IDENTIFIER"},
|
||||
{MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE, "SERVER_KEEP_ALIVE"},
|
||||
{MQTTPROPERTY_CODE_AUTHENTICATION_METHOD, "AUTHENTICATION_METHOD"},
|
||||
{MQTTPROPERTY_CODE_AUTHENTICATION_DATA, "AUTHENTICATION_DATA"},
|
||||
{MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION, "REQUEST_PROBLEM_INFORMATION"},
|
||||
{MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL, "WILL_DELAY_INTERVAL"},
|
||||
{MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION, "REQUEST_RESPONSE_INFORMATION"},
|
||||
{MQTTPROPERTY_CODE_RESPONSE_INFORMATION, "RESPONSE_INFORMATION"},
|
||||
{MQTTPROPERTY_CODE_SERVER_REFERENCE, "SERVER_REFERENCE"},
|
||||
{MQTTPROPERTY_CODE_REASON_STRING, "REASON_STRING"},
|
||||
{MQTTPROPERTY_CODE_RECEIVE_MAXIMUM, "RECEIVE_MAXIMUM"},
|
||||
{MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM, "TOPIC_ALIAS_MAXIMUM"},
|
||||
{MQTTPROPERTY_CODE_TOPIC_ALIAS, "TOPIC_ALIAS"},
|
||||
{MQTTPROPERTY_CODE_MAXIMUM_QOS, "MAXIMUM_QOS"},
|
||||
{MQTTPROPERTY_CODE_RETAIN_AVAILABLE, "RETAIN_AVAILABLE"},
|
||||
{MQTTPROPERTY_CODE_USER_PROPERTY, "USER_PROPERTY"},
|
||||
{MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE, "MAXIMUM_PACKET_SIZE"},
|
||||
{MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE, "WILDCARD_SUBSCRIPTION_AVAILABLE"},
|
||||
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, "SUBSCRIPTION_IDENTIFIERS_AVAILABLE"},
|
||||
{MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE, "SHARED_SUBSCRIPTION_AVAILABLE"}
|
||||
};
|
||||
|
||||
const char* MQTTPropertyName(enum MQTTPropertyCodes value)
|
||||
{
|
||||
int i = 0;
|
||||
const char* result = NULL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nameToString); ++i)
|
||||
{
|
||||
if (nameToString[i].value == value)
|
||||
{
|
||||
result = nameToString[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void MQTTProperties_free(MQTTProperties* props)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (props == NULL)
|
||||
goto exit;
|
||||
for (i = 0; i < props->count; ++i)
|
||||
{
|
||||
int id = props->array[i].identifier;
|
||||
int type = MQTTProperty_getType(id);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BINARY_DATA:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
|
||||
free(props->array[i].value.data.data);
|
||||
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
free(props->array[i].value.value.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (props->array)
|
||||
free(props->array);
|
||||
memset(props, '\0', sizeof(MQTTProperties)); /* zero all fields */
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
MQTTProperties MQTTProperties_copy(const MQTTProperties* props)
|
||||
{
|
||||
int i = 0;
|
||||
MQTTProperties result = MQTTProperties_initializer;
|
||||
|
||||
FUNC_ENTRY;
|
||||
for (i = 0; props != NULL && i < props->count; ++i)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if ((rc = MQTTProperties_add(&result, &props->array[i])) != 0)
|
||||
Log(LOG_ERROR, -1, "Error from MQTTProperties add %d", rc);
|
||||
}
|
||||
|
||||
FUNC_EXIT;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_hasProperty(const MQTTProperties *props, enum MQTTPropertyCodes propid)
|
||||
{
|
||||
int i = 0;
|
||||
int found = 0;
|
||||
|
||||
for (i = 0; props != NULL && i < props->count; ++i)
|
||||
{
|
||||
if (propid == props->array[i].identifier)
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_propertyCount(const MQTTProperties *props, enum MQTTPropertyCodes propid)
|
||||
{
|
||||
int i = 0;
|
||||
int count = 0;
|
||||
|
||||
for (i = 0; props != NULL && i < props->count; ++i)
|
||||
{
|
||||
if (propid == props->array[i].identifier)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int64_t MQTTProperties_getNumericValueAt(const MQTTProperties *props, enum MQTTPropertyCodes propid, int index)
|
||||
{
|
||||
int i = 0;
|
||||
int64_t rc = -9999999;
|
||||
int cur_index = 0;
|
||||
|
||||
for (i = 0; props != NULL && i < props->count; ++i)
|
||||
{
|
||||
int id = props->array[i].identifier;
|
||||
|
||||
if (id == propid)
|
||||
{
|
||||
if (cur_index < index)
|
||||
{
|
||||
cur_index++;
|
||||
continue;
|
||||
}
|
||||
switch (MQTTProperty_getType(id))
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BYTE:
|
||||
rc = props->array[i].value.byte;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
|
||||
rc = props->array[i].value.integer2;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
|
||||
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
|
||||
rc = props->array[i].value.integer4;
|
||||
break;
|
||||
default:
|
||||
rc = -999999;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int64_t MQTTProperties_getNumericValue(const MQTTProperties *props, enum MQTTPropertyCodes propid)
|
||||
{
|
||||
return MQTTProperties_getNumericValueAt(props, propid, 0);
|
||||
}
|
||||
|
||||
|
||||
MQTTProperty* MQTTProperties_getPropertyAt(const MQTTProperties *props, enum MQTTPropertyCodes propid, int index)
|
||||
{
|
||||
int i = 0;
|
||||
MQTTProperty* result = NULL;
|
||||
int cur_index = 0;
|
||||
|
||||
for (i = 0; props != 0 && i < props->count; ++i)
|
||||
{
|
||||
int id = props->array[i].identifier;
|
||||
|
||||
if (id == propid)
|
||||
{
|
||||
if (cur_index == index)
|
||||
{
|
||||
result = &props->array[i];
|
||||
break;
|
||||
}
|
||||
else
|
||||
cur_index++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
MQTTProperty* MQTTProperties_getProperty(const MQTTProperties *props, enum MQTTPropertyCodes propid)
|
||||
{
|
||||
return MQTTProperties_getPropertyAt(props, propid, 0);
|
||||
}
|
||||
225
3rd/paho.mqtt.c/src/MQTTProperties.h
Normal file
225
3rd/paho.mqtt.c/src/MQTTProperties.h
Normal file
@@ -0,0 +1,225 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2024 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROPERTIES_H)
|
||||
#define MQTTPROPERTIES_H
|
||||
|
||||
#include "MQTTExportDeclarations.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MQTT_INVALID_PROPERTY_ID -2
|
||||
|
||||
/** The one byte MQTT V5 property indicator */
|
||||
enum MQTTPropertyCodes {
|
||||
MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR = 1, /**< The value is 1 */
|
||||
MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL = 2, /**< The value is 2 */
|
||||
MQTTPROPERTY_CODE_CONTENT_TYPE = 3, /**< The value is 3 */
|
||||
MQTTPROPERTY_CODE_RESPONSE_TOPIC = 8, /**< The value is 8 */
|
||||
MQTTPROPERTY_CODE_CORRELATION_DATA = 9, /**< The value is 9 */
|
||||
MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER = 11, /**< The value is 11 */
|
||||
MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL = 17, /**< The value is 17 */
|
||||
MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFIER = 18,/**< The value is 18 */
|
||||
MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER = 18,/**< The value is 18 (obsolete, misspelled) */
|
||||
MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE = 19, /**< The value is 19 */
|
||||
MQTTPROPERTY_CODE_AUTHENTICATION_METHOD = 21, /**< The value is 21 */
|
||||
MQTTPROPERTY_CODE_AUTHENTICATION_DATA = 22, /**< The value is 22 */
|
||||
MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION = 23,/**< The value is 23 */
|
||||
MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL = 24, /**< The value is 24 */
|
||||
MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION = 25,/**< The value is 25 */
|
||||
MQTTPROPERTY_CODE_RESPONSE_INFORMATION = 26, /**< The value is 26 */
|
||||
MQTTPROPERTY_CODE_SERVER_REFERENCE = 28, /**< The value is 28 */
|
||||
MQTTPROPERTY_CODE_REASON_STRING = 31, /**< The value is 31 */
|
||||
MQTTPROPERTY_CODE_RECEIVE_MAXIMUM = 33, /**< The value is 33*/
|
||||
MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM = 34, /**< The value is 34 */
|
||||
MQTTPROPERTY_CODE_TOPIC_ALIAS = 35, /**< The value is 35 */
|
||||
MQTTPROPERTY_CODE_MAXIMUM_QOS = 36, /**< The value is 36 */
|
||||
MQTTPROPERTY_CODE_RETAIN_AVAILABLE = 37, /**< The value is 37 */
|
||||
MQTTPROPERTY_CODE_USER_PROPERTY = 38, /**< The value is 38 */
|
||||
MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE = 39, /**< The value is 39 */
|
||||
MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE = 40,/**< The value is 40 */
|
||||
MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE = 41,/**< The value is 41 */
|
||||
MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE = 42/**< The value is 241 */
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a printable string description of an MQTT V5 property code.
|
||||
* @param value an MQTT V5 property code.
|
||||
* @return the printable string description of the input property code.
|
||||
* NULL if the code was not found.
|
||||
*/
|
||||
LIBMQTT_API const char* MQTTPropertyName(enum MQTTPropertyCodes value);
|
||||
|
||||
/** The one byte MQTT V5 property type */
|
||||
enum MQTTPropertyTypes {
|
||||
MQTTPROPERTY_TYPE_BYTE,
|
||||
MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER,
|
||||
MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER,
|
||||
MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER,
|
||||
MQTTPROPERTY_TYPE_BINARY_DATA,
|
||||
MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING,
|
||||
MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the MQTT V5 type code of an MQTT V5 property.
|
||||
* @param value an MQTT V5 property code.
|
||||
* @return the MQTT V5 type code of the input property. -1 if the code was not found.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperty_getType(enum MQTTPropertyCodes value);
|
||||
|
||||
/**
|
||||
* The data for a length delimited string
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int len; /**< the length of the string */
|
||||
char* data; /**< pointer to the string data */
|
||||
} MQTTLenString;
|
||||
|
||||
|
||||
/**
|
||||
* Structure to hold an MQTT version 5 property of any type
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
enum MQTTPropertyCodes identifier; /**< The MQTT V5 property id. A multi-byte integer. */
|
||||
/** The value of the property, as a union of the different possible types. */
|
||||
union {
|
||||
unsigned char byte; /**< holds the value of a byte property type */
|
||||
unsigned short integer2; /**< holds the value of a 2 byte integer property type */
|
||||
unsigned int integer4; /**< holds the value of a 4 byte integer property type */
|
||||
struct {
|
||||
MQTTLenString data; /**< The value of a string property, or the name of a user property. */
|
||||
MQTTLenString value; /**< The value of a user property. */
|
||||
};
|
||||
} value;
|
||||
} MQTTProperty;
|
||||
|
||||
/**
|
||||
* MQTT version 5 property list
|
||||
*/
|
||||
typedef struct MQTTProperties
|
||||
{
|
||||
int count; /**< number of property entries in the array */
|
||||
int max_count; /**< max number of properties that the currently allocated array can store */
|
||||
int length; /**< mbi: byte length of all properties */
|
||||
MQTTProperty *array; /**< array of properties */
|
||||
} MQTTProperties;
|
||||
|
||||
#define MQTTProperties_initializer {0, 0, 0, NULL}
|
||||
|
||||
/**
|
||||
* Returns the length of the properties structure when serialized ready for network transmission.
|
||||
* @param props an MQTT V5 property structure.
|
||||
* @return the length in bytes of the properties when serialized.
|
||||
*/
|
||||
int MQTTProperties_len(const MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* Add a property pointer to the property array. Memory is allocated in this function,
|
||||
* so MQTTClient_create or MQTTAsync_create must be called first to initialize the
|
||||
* internal heap tracking. Alternatively MQTTAsync_global_init() can be called first
|
||||
* or build with the HIGH_PERFORMANCE option which disables the heap tracking.
|
||||
* @param props The property list to add the property to.
|
||||
* @param prop The property to add to the list.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperties_add(MQTTProperties* props, const MQTTProperty* prop);
|
||||
|
||||
/**
|
||||
* Serialize the given property list to a character buffer, e.g. for writing to the network.
|
||||
* @param pptr pointer to the buffer - move the pointer as we add data
|
||||
* @param properties pointer to the property list, can be NULL
|
||||
* @return whether the write succeeded or not: number of bytes written, or < 0 on failure.
|
||||
*/
|
||||
int MQTTProperties_write(char** pptr, const MQTTProperties* properties);
|
||||
|
||||
/**
|
||||
* Reads a property list from a character buffer into an array.
|
||||
* @param properties pointer to the property list to be filled. Should be initalized but empty.
|
||||
* @param pptr pointer to the character buffer.
|
||||
* @param enddata pointer to the end of the character buffer so we don't read beyond.
|
||||
* @return 1 if the properties were read successfully.
|
||||
*/
|
||||
int MQTTProperties_read(MQTTProperties* properties, char** pptr, char* enddata);
|
||||
|
||||
/**
|
||||
* Free all memory allocated to the property list, including any to individual properties.
|
||||
* @param properties pointer to the property list.
|
||||
*/
|
||||
LIBMQTT_API void MQTTProperties_free(MQTTProperties* properties);
|
||||
|
||||
/**
|
||||
* Copy the contents of a property list, allocating additional memory if needed.
|
||||
* @param props pointer to the property list.
|
||||
* @return the duplicated property list.
|
||||
*/
|
||||
LIBMQTT_API MQTTProperties MQTTProperties_copy(const MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* Checks if property list contains a specific property.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @return 1 if found, 0 if not.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperties_hasProperty(const MQTTProperties *props, enum MQTTPropertyCodes propid);
|
||||
|
||||
/**
|
||||
* Returns the number of instances of a property id. Most properties can exist only once.
|
||||
* User properties and subscription ids can exist more than once.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @return the number of times found. Can be 0.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperties_propertyCount(const MQTTProperties *props, enum MQTTPropertyCodes propid);
|
||||
|
||||
/**
|
||||
* Returns the integer value of a specific property. The property given must be a numeric type.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @return the integer value of the property. -9999999 on failure.
|
||||
*/
|
||||
LIBMQTT_API int64_t MQTTProperties_getNumericValue(const MQTTProperties *props, enum MQTTPropertyCodes propid);
|
||||
|
||||
/**
|
||||
* Returns the integer value of a specific property when it's not the only instance.
|
||||
* The property given must be a numeric type.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @param index the instance number, starting at 0.
|
||||
* @return the integer value of the property. -9999999 on failure.
|
||||
*/
|
||||
LIBMQTT_API int64_t MQTTProperties_getNumericValueAt(const MQTTProperties *props, enum MQTTPropertyCodes propid, int index);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the property structure for a specific property.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @return the pointer to the property structure if found. NULL if not found.
|
||||
*/
|
||||
LIBMQTT_API MQTTProperty* MQTTProperties_getProperty(const MQTTProperties *props, enum MQTTPropertyCodes propid);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the property structure for a specific property when it's not the only instance.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @param index the instance number, starting at 0.
|
||||
* @return the pointer to the property structure if found. NULL if not found.
|
||||
*/
|
||||
LIBMQTT_API MQTTProperty* MQTTProperties_getPropertyAt(const MQTTProperties *props, enum MQTTPropertyCodes propid, int index);
|
||||
|
||||
#endif /* MQTTPROPERTIES_H */
|
||||
46
3rd/paho.mqtt.c/src/MQTTProtocol.h
Normal file
46
3rd/paho.mqtt.c/src/MQTTProtocol.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - MQTT 3.1.1 updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROTOCOL_H)
|
||||
#define MQTTPROTOCOL_H
|
||||
|
||||
#include "LinkedList.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include "Clients.h"
|
||||
|
||||
#define MAX_MSG_ID 65535
|
||||
#define MAX_CLIENTID_LEN 65535
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SOCKET socket;
|
||||
Publications* p;
|
||||
} pending_write;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
List publications;
|
||||
unsigned int msgs_received;
|
||||
unsigned int msgs_sent;
|
||||
List pending_writes; /* for qos 0 writes not complete */
|
||||
} MQTTProtocol;
|
||||
|
||||
|
||||
#include "MQTTProtocolOut.h"
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user