首次提交
This commit is contained in:
230
Pods/MQTTClient/LICENSE
generated
Normal file
230
Pods/MQTTClient/LICENSE
generated
Normal file
@@ -0,0 +1,230 @@
|
||||
Eclipse Public License - v 1.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 code and documentation
|
||||
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 additions to the Program which: (i) are separate modules of
|
||||
software distributed in conjunction with the Program under their own
|
||||
license agreement, and (ii) are not derivative works of the Program.
|
||||
|
||||
"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,
|
||||
including all Contributors.
|
||||
|
||||
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, in source code and object code form.
|
||||
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 and
|
||||
object code 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.
|
||||
|
||||
3. REQUIREMENTS
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form under
|
||||
its own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
b) its license agreement:
|
||||
i) effectively disclaims on behalf of all 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 Contributors all liability for
|
||||
damages, including direct, indirect, special, incidental and
|
||||
consequential damages, such as lost profits;
|
||||
iii) states that any provisions which differ from this Agreement are
|
||||
offered by that Contributor alone and not by any other party; and
|
||||
iv) states that source code for the Program is available from such
|
||||
Contributor, and informs licensees how to obtain it in a reasonable
|
||||
manner on or through a medium customarily used for software exchange.
|
||||
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
Contributors may not remove or alter any copyright notices contained
|
||||
within the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its Contribution,
|
||||
if
|
||||
any, in a manner that reasonably allows subsequent Recipients to identify the
|
||||
originator of the Contribution.
|
||||
|
||||
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, 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, 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.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the
|
||||
intellectual property laws of the United States of America. No party to this
|
||||
Agreement will bring a legal action under this Agreement more than one year
|
||||
after the cause of action arose. Each party waives its rights to a jury trial in
|
||||
any resulting litigation.
|
||||
|
||||
Copyright (c) 2013-2015 Christoph Krey <krey.christoph@gmail.com>
|
||||
|
||||
Based on
|
||||
|
||||
https://github.com/m2mIO/mqttIO-objC
|
||||
|
||||
Copyright © 2011, 2013 2lemetry, LLC
|
||||
|
||||
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.
|
||||
|
||||
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 HOLDER 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.
|
||||
|
||||
|
||||
23
Pods/MQTTClient/MQTTClient/MQTTClient/ForegroundReconnection.h
generated
Normal file
23
Pods/MQTTClient/MQTTClient/MQTTClient/ForegroundReconnection.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// ForegroundReconnection.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Josip Cavar on 22/08/2017.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IPHONE == 1
|
||||
|
||||
@class MQTTSessionManager;
|
||||
|
||||
@interface ForegroundReconnection : NSObject
|
||||
|
||||
@property (weak, nonatomic) MQTTSessionManager *sessionManager;
|
||||
|
||||
- (instancetype)initWithMQTTSessionManager:(MQTTSessionManager *)manager;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
83
Pods/MQTTClient/MQTTClient/MQTTClient/ForegroundReconnection.m
generated
Normal file
83
Pods/MQTTClient/MQTTClient/MQTTClient/ForegroundReconnection.m
generated
Normal file
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// ForegroundReconnection.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Josip Cavar on 22/08/2017.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ForegroundReconnection.h"
|
||||
|
||||
#if TARGET_OS_IPHONE == 1
|
||||
#import "MQTTSessionManager.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
@interface ForegroundReconnection ()
|
||||
|
||||
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ForegroundReconnection
|
||||
|
||||
- (instancetype)initWithMQTTSessionManager:(MQTTSessionManager *)manager {
|
||||
self = [super init];
|
||||
self.sessionManager = manager;
|
||||
self.backgroundTask = UIBackgroundTaskInvalid;
|
||||
|
||||
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
|
||||
|
||||
[defaultCenter addObserver:self
|
||||
selector:@selector(appWillResignActive)
|
||||
name:UIApplicationWillResignActiveNotification
|
||||
object:nil];
|
||||
|
||||
[defaultCenter addObserver:self
|
||||
selector:@selector(appDidEnterBackground)
|
||||
name:UIApplicationDidEnterBackgroundNotification
|
||||
object:nil];
|
||||
|
||||
[defaultCenter addObserver:self
|
||||
selector:@selector(appDidBecomeActive)
|
||||
name:UIApplicationDidBecomeActiveNotification
|
||||
object:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
|
||||
[defaultCenter removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[defaultCenter removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
[defaultCenter removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)appWillResignActive {
|
||||
[self.sessionManager disconnectWithDisconnectHandler:nil];
|
||||
}
|
||||
|
||||
- (void)appDidEnterBackground {
|
||||
if (!self.sessionManager.requiresTearDown) {
|
||||
// we don't want to tear down session as it's already closed
|
||||
return;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
[strongSelf endBackgroundTask];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)appDidBecomeActive {
|
||||
[self.sessionManager connectToLast:nil];
|
||||
}
|
||||
|
||||
- (void)endBackgroundTask {
|
||||
if (self.backgroundTask) {
|
||||
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
|
||||
self.backgroundTask = UIBackgroundTaskInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
19
Pods/MQTTClient/MQTTClient/MQTTClient/GCDTimer.h
generated
Normal file
19
Pods/MQTTClient/MQTTClient/MQTTClient/GCDTimer.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Timer.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Josip Cavar on 06/11/2017.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GCDTimer: NSObject
|
||||
|
||||
+ (GCDTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
|
||||
repeats:(BOOL)repeats
|
||||
queue:(dispatch_queue_t)queue
|
||||
block:(void (^)(void))block;
|
||||
- (void)invalidate;
|
||||
|
||||
@end
|
||||
59
Pods/MQTTClient/MQTTClient/MQTTClient/GCDTimer.m
generated
Normal file
59
Pods/MQTTClient/MQTTClient/MQTTClient/GCDTimer.m
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Timer.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Josip Cavar on 06/11/2017.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GCDTimer.h"
|
||||
|
||||
@interface GCDTimer ()
|
||||
|
||||
@property (strong, nonatomic) dispatch_source_t timer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDTimer
|
||||
|
||||
+ (GCDTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
|
||||
repeats:(BOOL)repeats
|
||||
queue:(dispatch_queue_t)queue
|
||||
block:(void (^)(void))block {
|
||||
GCDTimer *timer = [[GCDTimer alloc] initWithInterval:interval
|
||||
repeats:repeats
|
||||
queue:queue
|
||||
block:block];
|
||||
return timer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithInterval:(NSTimeInterval)interval
|
||||
repeats:(BOOL)repeats
|
||||
queue:(dispatch_queue_t)queue
|
||||
block:(void (^)(void))block {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
|
||||
dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);
|
||||
dispatch_source_set_event_handler(self.timer, ^{
|
||||
if (!repeats) {
|
||||
dispatch_source_cancel(self.timer);
|
||||
}
|
||||
block();
|
||||
});
|
||||
dispatch_resume(self.timer);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)invalidate {
|
||||
if (self.timer) {
|
||||
dispatch_source_cancel(self.timer);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
39
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketDecoder.h
generated
Normal file
39
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketDecoder.h
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// MQTTCFSocketDecoder.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, MQTTCFSocketDecoderState) {
|
||||
MQTTCFSocketDecoderStateInitializing,
|
||||
MQTTCFSocketDecoderStateReady,
|
||||
MQTTCFSocketDecoderStateError
|
||||
};
|
||||
|
||||
@class MQTTCFSocketDecoder;
|
||||
|
||||
@protocol MQTTCFSocketDecoderDelegate <NSObject>
|
||||
|
||||
- (void)decoder:(MQTTCFSocketDecoder *)sender didReceiveMessage:(NSData *)data;
|
||||
- (void)decoderDidOpen:(MQTTCFSocketDecoder *)sender;
|
||||
- (void)decoder:(MQTTCFSocketDecoder *)sender didFailWithError:(NSError *)error;
|
||||
- (void)decoderdidClose:(MQTTCFSocketDecoder *)sender;
|
||||
|
||||
@end
|
||||
|
||||
@interface MQTTCFSocketDecoder : NSObject <NSStreamDelegate>
|
||||
|
||||
@property (nonatomic) MQTTCFSocketDecoderState state;
|
||||
@property (strong, nonatomic) NSError *error;
|
||||
@property (strong, nonatomic) NSInputStream *stream;
|
||||
@property (weak, nonatomic ) id<MQTTCFSocketDecoderDelegate> delegate;
|
||||
|
||||
- (void)open;
|
||||
- (void)close;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
90
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketDecoder.m
generated
Normal file
90
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketDecoder.m
generated
Normal file
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// MQTTCFSocketDecoder.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTCFSocketDecoder.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTCFSocketDecoder()
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTCFSocketDecoder
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.state = MQTTCFSocketDecoderStateInitializing;
|
||||
|
||||
self.stream = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)open {
|
||||
if (self.state == MQTTCFSocketDecoderStateInitializing) {
|
||||
(self.stream).delegate = self;
|
||||
[self.stream open];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
[self.stream close];
|
||||
[self.stream setDelegate:nil];
|
||||
}
|
||||
|
||||
- (void)stream:(NSStream *)sender handleEvent:(NSStreamEvent)eventCode {
|
||||
if (eventCode & NSStreamEventOpenCompleted) {
|
||||
DDLogVerbose(@"[MQTTCFSocketDecoder] NSStreamEventOpenCompleted");
|
||||
self.state = MQTTCFSocketDecoderStateReady;
|
||||
[self.delegate decoderDidOpen:self];
|
||||
}
|
||||
|
||||
if (eventCode & NSStreamEventHasBytesAvailable) {
|
||||
DDLogVerbose(@"[MQTTCFSocketDecoder] NSStreamEventHasBytesAvailable");
|
||||
if (self.state == MQTTCFSocketDecoderStateInitializing) {
|
||||
self.state = MQTTCFSocketDecoderStateReady;
|
||||
}
|
||||
|
||||
if (self.state == MQTTCFSocketDecoderStateReady) {
|
||||
NSInteger n;
|
||||
UInt8 buffer[768];
|
||||
|
||||
n = [self.stream read:buffer maxLength:sizeof(buffer)];
|
||||
if (n == -1) {
|
||||
self.state = MQTTCFSocketDecoderStateError;
|
||||
[self.delegate decoder:self didFailWithError:nil];
|
||||
} else {
|
||||
NSData *data = [NSData dataWithBytes:buffer length:n];
|
||||
DDLogVerbose(@"[MQTTCFSocketDecoder] received (%lu)=%@...", (unsigned long)data.length,
|
||||
[data subdataWithRange:NSMakeRange(0, MIN(256, data.length))]);
|
||||
[self.delegate decoder:self didReceiveMessage:data];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (eventCode & NSStreamEventHasSpaceAvailable) {
|
||||
DDLogVerbose(@"[MQTTCFSocketDecoder] NSStreamEventHasSpaceAvailable");
|
||||
}
|
||||
|
||||
if (eventCode & NSStreamEventEndEncountered) {
|
||||
DDLogVerbose(@"[MQTTCFSocketDecoder] NSStreamEventEndEncountered");
|
||||
self.state = MQTTCFSocketDecoderStateInitializing;
|
||||
self.error = nil;
|
||||
[self.delegate decoderdidClose:self];
|
||||
}
|
||||
|
||||
if (eventCode & NSStreamEventErrorOccurred) {
|
||||
DDLogVerbose(@"[MQTTCFSocketDecoder] NSStreamEventErrorOccurred");
|
||||
self.state = MQTTCFSocketDecoderStateError;
|
||||
self.error = self.stream.streamError;
|
||||
[self.delegate decoder:self didFailWithError:self.error];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
38
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketEncoder.h
generated
Normal file
38
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketEncoder.h
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// MQTTCFSocketEncoder.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, MQTTCFSocketEncoderState) {
|
||||
MQTTCFSocketEncoderStateInitializing,
|
||||
MQTTCFSocketEncoderStateReady,
|
||||
MQTTCFSocketEncoderStateError
|
||||
};
|
||||
|
||||
@class MQTTCFSocketEncoder;
|
||||
|
||||
@protocol MQTTCFSocketEncoderDelegate <NSObject>
|
||||
|
||||
- (void)encoderDidOpen:(MQTTCFSocketEncoder *)sender;
|
||||
- (void)encoder:(MQTTCFSocketEncoder *)sender didFailWithError:(NSError *)error;
|
||||
- (void)encoderdidClose:(MQTTCFSocketEncoder *)sender;
|
||||
|
||||
@end
|
||||
|
||||
@interface MQTTCFSocketEncoder : NSObject <NSStreamDelegate>
|
||||
|
||||
@property (nonatomic) MQTTCFSocketEncoderState state;
|
||||
@property (strong, nonatomic) NSError *error;
|
||||
@property (strong, nonatomic) NSOutputStream *stream;
|
||||
@property (weak, nonatomic ) id<MQTTCFSocketEncoderDelegate> delegate;
|
||||
|
||||
- (void)open;
|
||||
- (void)close;
|
||||
- (BOOL)send:(NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
115
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketEncoder.m
generated
Normal file
115
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketEncoder.m
generated
Normal file
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// MQTTCFSocketEncoder.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTCFSocketEncoder.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTCFSocketEncoder()
|
||||
|
||||
@property (strong, nonatomic) NSMutableData *buffer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTCFSocketEncoder
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.state = MQTTCFSocketEncoderStateInitializing;
|
||||
self.buffer = [[NSMutableData alloc] init];
|
||||
self.stream = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (void)open {
|
||||
(self.stream).delegate = self;
|
||||
[self.stream open];
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
[self.stream close];
|
||||
[self.stream setDelegate:nil];
|
||||
}
|
||||
|
||||
- (void)setState:(MQTTCFSocketEncoderState)state {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] setState %ld/%ld", (long)_state, (long)state);
|
||||
_state = state;
|
||||
}
|
||||
|
||||
- (void)stream:(NSStream *)sender handleEvent:(NSStreamEvent)eventCode {
|
||||
if (eventCode & NSStreamEventOpenCompleted) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] NSStreamEventOpenCompleted");
|
||||
}
|
||||
if (eventCode & NSStreamEventHasBytesAvailable) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] NSStreamEventHasBytesAvailable");
|
||||
}
|
||||
if (eventCode & NSStreamEventHasSpaceAvailable) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] NSStreamEventHasSpaceAvailable");
|
||||
if (self.state == MQTTCFSocketEncoderStateInitializing) {
|
||||
self.state = MQTTCFSocketEncoderStateReady;
|
||||
[self.delegate encoderDidOpen:self];
|
||||
}
|
||||
|
||||
if (self.state == MQTTCFSocketEncoderStateReady) {
|
||||
if (self.buffer.length) {
|
||||
[self send:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (eventCode & NSStreamEventEndEncountered) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] NSStreamEventEndEncountered");
|
||||
self.state = MQTTCFSocketEncoderStateInitializing;
|
||||
self.error = nil;
|
||||
[self.delegate encoderdidClose:self];
|
||||
}
|
||||
if (eventCode & NSStreamEventErrorOccurred) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] NSStreamEventErrorOccurred");
|
||||
self.state = MQTTCFSocketEncoderStateError;
|
||||
self.error = self.stream.streamError;
|
||||
[self.delegate encoder:self didFailWithError:self.error];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)send:(NSData *)data {
|
||||
@synchronized(self) {
|
||||
if (self.state != MQTTCFSocketEncoderStateReady) {
|
||||
DDLogInfo(@"[MQTTCFSocketEncoder] not MQTTCFSocketEncoderStateReady");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
[self.buffer appendData:data];
|
||||
}
|
||||
|
||||
if (self.buffer.length) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] buffer to write (%lu)=%@...",
|
||||
(unsigned long)self.buffer.length,
|
||||
[self.buffer subdataWithRange:NSMakeRange(0, MIN(256, self.buffer.length))]);
|
||||
|
||||
NSInteger n = [self.stream write:self.buffer.bytes maxLength:self.buffer.length];
|
||||
|
||||
if (n == -1) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] streamError: %@", self.error);
|
||||
self.state = MQTTCFSocketEncoderStateError;
|
||||
self.error = self.stream.streamError;
|
||||
return NO;
|
||||
} else {
|
||||
if (n < self.buffer.length) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] buffer partially written: %ld", (long)n);
|
||||
}
|
||||
[self.buffer replaceBytesInRange:NSMakeRange(0, n) withBytes:NULL length:0];
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
79
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketTransport.h
generated
Normal file
79
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketTransport.h
generated
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// MQTTCFSocketTransport.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 06.12.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTTransport.h"
|
||||
#import "MQTTCFSocketDecoder.h"
|
||||
#import "MQTTCFSocketEncoder.h"
|
||||
|
||||
/** MQTTCFSocketTransport
|
||||
* implements an MQTTTransport on top of CFNetwork
|
||||
*/
|
||||
@interface MQTTCFSocketTransport : MQTTTransport <MQTTTransport, MQTTCFSocketDecoderDelegate, MQTTCFSocketEncoderDelegate>
|
||||
|
||||
/** streamSSLLevel an NSString containing the security level for read and write streams
|
||||
* For list of possible values see:
|
||||
* https://developer.apple.com/documentation/corefoundation/cfstream/cfstream_socket_security_level_constants
|
||||
* Please also note that kCFStreamSocketSecurityLevelTLSv1_2 is not in a list
|
||||
* and cannot be used as constant, but you can use it as a string value
|
||||
* defaults to kCFStreamSocketSecurityLevelNegotiatedSSL
|
||||
*/
|
||||
@property (strong, nonatomic) NSString *streamSSLLevel;
|
||||
|
||||
/** host an NSString containing the hostName or IP address of the host to connect to
|
||||
* defaults to @"localhost"
|
||||
*/
|
||||
@property (strong, nonatomic) NSString *host;
|
||||
|
||||
/** port an unsigned 32 bit integer containing the IP port number to connect to
|
||||
* defaults to 1883
|
||||
*/
|
||||
@property (nonatomic) UInt32 port;
|
||||
|
||||
/** tls a boolean indicating whether the transport should be using security
|
||||
* defaults to NO
|
||||
*/
|
||||
@property (nonatomic) BOOL tls;
|
||||
|
||||
/** Require for VoIP background service
|
||||
* defaults to NO
|
||||
*/
|
||||
@property (nonatomic) BOOL voip;
|
||||
|
||||
/** certificates An identity certificate used to reply to a server requiring client certificates according
|
||||
* to the description given for SSLSetCertificate(). You may build the certificates array yourself or use the
|
||||
* sundry method clientCertFromP12.
|
||||
*/
|
||||
@property (strong, nonatomic) NSArray *certificates;
|
||||
|
||||
/** reads the content of a PKCS12 file and converts it to an certificates array for initWith...
|
||||
@param path the path to a PKCS12 file
|
||||
@param passphrase the passphrase to unlock the PKCS12 file
|
||||
@returns a certificates array or nil if an error occured
|
||||
|
||||
@code
|
||||
NSString *path = [[NSBundle bundleForClass:[MQTTClientTests class]] pathForResource:@"filename"
|
||||
ofType:@"p12"];
|
||||
|
||||
NSArray *myCerts = [MQTTCFSocketTransport clientCertsFromP12:path passphrase:@"passphrase"];
|
||||
if (myCerts) {
|
||||
|
||||
self.session = [[MQTTSession alloc] init];
|
||||
...
|
||||
self.session.certificates = myCerts;
|
||||
|
||||
[self.session connect];
|
||||
...
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
+ (NSArray *)clientCertsFromP12:(NSString *)path passphrase:(NSString *)passphrase;
|
||||
|
||||
@end
|
||||
237
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketTransport.m
generated
Normal file
237
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCFSocketTransport.m
generated
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// MQTTCFSocketTransport.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 06.12.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTCFSocketTransport.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTCFSocketTransport() {
|
||||
void *QueueIdentityKey;
|
||||
}
|
||||
|
||||
@property (strong, nonatomic) MQTTCFSocketEncoder *encoder;
|
||||
@property (strong, nonatomic) MQTTCFSocketDecoder *decoder;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTCFSocketTransport
|
||||
|
||||
@synthesize state;
|
||||
@synthesize delegate;
|
||||
@synthesize queue = _queue;
|
||||
@synthesize streamSSLLevel;
|
||||
@synthesize host;
|
||||
@synthesize port;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.host = @"localhost";
|
||||
self.port = 1883;
|
||||
self.tls = false;
|
||||
self.voip = false;
|
||||
self.certificates = nil;
|
||||
self.queue = dispatch_get_main_queue();
|
||||
self.streamSSLLevel = (NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (void)setQueue:(dispatch_queue_t)queue {
|
||||
_queue = queue;
|
||||
|
||||
// We're going to use dispatch_queue_set_specific() to "mark" our queue.
|
||||
// The dispatch_queue_set_specific() and dispatch_get_specific() functions take a "void *key" parameter.
|
||||
// Later we can use dispatch_get_specific() to determine if we're executing on our queue.
|
||||
// From the documentation:
|
||||
//
|
||||
// > Keys are only compared as pointers and are never dereferenced.
|
||||
// > Thus, you can use a pointer to a static variable for a specific subsystem or
|
||||
// > any other value that allows you to identify the value uniquely.
|
||||
//
|
||||
// So we're just going to use the memory address of an ivar.
|
||||
|
||||
dispatch_queue_set_specific(_queue, &QueueIdentityKey, (__bridge void *)_queue, NULL);
|
||||
}
|
||||
|
||||
- (void)open {
|
||||
DDLogVerbose(@"[MQTTCFSocketTransport] open");
|
||||
self.state = MQTTTransportOpening;
|
||||
|
||||
NSError* connectError;
|
||||
|
||||
CFReadStreamRef readStream;
|
||||
CFWriteStreamRef writeStream;
|
||||
|
||||
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.host, self.port, &readStream, &writeStream);
|
||||
|
||||
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
|
||||
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
|
||||
|
||||
if (self.tls) {
|
||||
NSMutableDictionary *sslOptions = [[NSMutableDictionary alloc] init];
|
||||
|
||||
sslOptions[(NSString *)kCFStreamSSLLevel] = self.streamSSLLevel;
|
||||
|
||||
if (self.certificates) {
|
||||
sslOptions[(NSString *)kCFStreamSSLCertificates] = self.certificates;
|
||||
}
|
||||
|
||||
if (!CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(sslOptions))) {
|
||||
connectError = [NSError errorWithDomain:@"MQTT"
|
||||
code:errSSLInternal
|
||||
userInfo:@{NSLocalizedDescriptionKey : @"Fail to init ssl input stream!"}];
|
||||
}
|
||||
if (!CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(sslOptions))) {
|
||||
connectError = [NSError errorWithDomain:@"MQTT"
|
||||
code:errSSLInternal
|
||||
userInfo:@{NSLocalizedDescriptionKey : @"Fail to init ssl output stream!"}];
|
||||
}
|
||||
}
|
||||
|
||||
if (!connectError) {
|
||||
self.encoder.delegate = nil;
|
||||
self.encoder = [[MQTTCFSocketEncoder alloc] init];
|
||||
CFWriteStreamSetDispatchQueue(writeStream, self.queue);
|
||||
self.encoder.stream = CFBridgingRelease(writeStream);
|
||||
self.encoder.delegate = self;
|
||||
if (self.voip) {
|
||||
[self.encoder.stream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
|
||||
}
|
||||
[self.encoder open];
|
||||
|
||||
self.decoder.delegate = nil;
|
||||
self.decoder = [[MQTTCFSocketDecoder alloc] init];
|
||||
CFReadStreamSetDispatchQueue(readStream, self.queue);
|
||||
self.decoder.stream = CFBridgingRelease(readStream);
|
||||
self.decoder.delegate = self;
|
||||
if (self.voip) {
|
||||
[self.decoder.stream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
|
||||
}
|
||||
[self.decoder open];
|
||||
} else {
|
||||
[self close];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
// https://github.com/novastone-media/MQTT-Client-Framework/issues/325
|
||||
// We need to make sure that we are closing streams on their queue
|
||||
// Otherwise, we end up with race condition where delegate is deallocated
|
||||
// but still used by run loop event
|
||||
if (self.queue != dispatch_get_specific(&QueueIdentityKey)) {
|
||||
dispatch_sync(self.queue, ^{
|
||||
[self internalClose];
|
||||
});
|
||||
} else {
|
||||
[self internalClose];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)internalClose {
|
||||
DDLogVerbose(@"[MQTTCFSocketTransport] close");
|
||||
self.state = MQTTTransportClosing;
|
||||
|
||||
if (self.encoder) {
|
||||
[self.encoder close];
|
||||
self.encoder.delegate = nil;
|
||||
}
|
||||
|
||||
if (self.decoder) {
|
||||
[self.decoder close];
|
||||
self.decoder.delegate = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)send:(nonnull NSData *)data {
|
||||
return [self.encoder send:data];
|
||||
}
|
||||
|
||||
- (void)decoder:(MQTTCFSocketDecoder *)sender didReceiveMessage:(nonnull NSData *)data {
|
||||
[self.delegate mqttTransport:self didReceiveMessage:data];
|
||||
}
|
||||
|
||||
- (void)decoder:(MQTTCFSocketDecoder *)sender didFailWithError:(NSError *)error {
|
||||
//self.state = MQTTTransportClosing;
|
||||
//[self.delegate mqttTransport:self didFailWithError:error];
|
||||
}
|
||||
- (void)encoder:(MQTTCFSocketEncoder *)sender didFailWithError:(NSError *)error {
|
||||
self.state = MQTTTransportClosing;
|
||||
[self.delegate mqttTransport:self didFailWithError:error];
|
||||
}
|
||||
|
||||
- (void)decoderdidClose:(MQTTCFSocketDecoder *)sender {
|
||||
self.state = MQTTTransportClosed;
|
||||
[self.delegate mqttTransportDidClose:self];
|
||||
}
|
||||
- (void)encoderdidClose:(MQTTCFSocketEncoder *)sender {
|
||||
//self.state = MQTTTransportClosed;
|
||||
//[self.delegate mqttTransportDidClose:self];
|
||||
}
|
||||
|
||||
- (void)decoderDidOpen:(MQTTCFSocketDecoder *)sender {
|
||||
//self.state = MQTTTransportOpen;
|
||||
//[self.delegate mqttTransportDidOpen:self];
|
||||
}
|
||||
- (void)encoderDidOpen:(MQTTCFSocketEncoder *)sender {
|
||||
self.state = MQTTTransportOpen;
|
||||
[self.delegate mqttTransportDidOpen:self];
|
||||
}
|
||||
|
||||
+ (NSArray *)clientCertsFromP12:(NSString *)path passphrase:(NSString *)passphrase {
|
||||
if (!path) {
|
||||
DDLogWarn(@"[MQTTCFSocketTransport] no p12 path given");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *pkcs12data = [[NSData alloc] initWithContentsOfFile:path];
|
||||
if (!pkcs12data) {
|
||||
DDLogWarn(@"[MQTTCFSocketTransport] reading p12 failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!passphrase) {
|
||||
DDLogWarn(@"[MQTTCFSocketTransport] no passphrase given");
|
||||
return nil;
|
||||
}
|
||||
CFArrayRef keyref = NULL;
|
||||
OSStatus importStatus = SecPKCS12Import((__bridge CFDataRef)pkcs12data,
|
||||
(__bridge CFDictionaryRef)@{(__bridge id)kSecImportExportPassphrase: passphrase},
|
||||
&keyref);
|
||||
if (importStatus != noErr) {
|
||||
DDLogWarn(@"[MQTTCFSocketTransport] Error while importing pkcs12 [%d]", (int)importStatus);
|
||||
return nil;
|
||||
}
|
||||
|
||||
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyref, 0);
|
||||
if (!identityDict) {
|
||||
DDLogWarn(@"[MQTTCFSocketTransport] could not CFArrayGetValueAtIndex");
|
||||
return nil;
|
||||
}
|
||||
|
||||
SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict,
|
||||
kSecImportItemIdentity);
|
||||
if (!identityRef) {
|
||||
DDLogWarn(@"[MQTTCFSocketTransport] could not CFDictionaryGetValue");
|
||||
return nil;
|
||||
};
|
||||
|
||||
SecCertificateRef cert = NULL;
|
||||
OSStatus status = SecIdentityCopyCertificate(identityRef, &cert);
|
||||
if (status != noErr) {
|
||||
DDLogWarn(@"[MQTTCFSocketTransport] SecIdentityCopyCertificate failed [%d]", (int)status);
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSArray *clientCerts = @[(__bridge id)identityRef, (__bridge id)cert];
|
||||
return clientCerts;
|
||||
}
|
||||
|
||||
@end
|
||||
39
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTClient.h
generated
Normal file
39
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTClient.h
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// MQTTClient.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 13.01.14.
|
||||
// Copyright © 2013-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
/**
|
||||
Include this file to use MQTTClient classes in your application
|
||||
|
||||
@author Christoph Krey c@ckrey.de
|
||||
@see http://mqtt.org
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MQTTClient/MQTTSession.h>
|
||||
#import <MQTTClient/MQTTDecoder.h>
|
||||
#import <MQTTClient/MQTTSessionLegacy.h>
|
||||
#import <MQTTClient/MQTTSessionSynchron.h>
|
||||
#import <MQTTClient/MQTTProperties.h>
|
||||
#import <MQTTClient/MQTTMessage.h>
|
||||
#import <MQTTClient/MQTTTransport.h>
|
||||
#import <MQTTClient/MQTTCFSocketTransport.h>
|
||||
#import <MQTTClient/MQTTCoreDataPersistence.h>
|
||||
#import <MQTTClient/MQTTSSLSecurityPolicyTransport.h>
|
||||
#import <MQTTClient/MQTTLog.h>
|
||||
|
||||
#if __has_include(<MQTTClient/MQTTSessionManager.h>)
|
||||
#import <MQTTClient/MQTTSessionManager.h>
|
||||
#endif
|
||||
|
||||
//! Project version number for MQTTClient.
|
||||
FOUNDATION_EXPORT double MQTTClientVersionNumber;
|
||||
|
||||
//! Project version string for MQTTClient<.
|
||||
FOUNDATION_EXPORT const unsigned char MQTTClientVersionString[];
|
||||
|
||||
21
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCoreDataPersistence.h
generated
Normal file
21
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCoreDataPersistence.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// MQTTCoreDataPersistence.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 22.03.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MQTTPersistence.h"
|
||||
|
||||
@interface MQTTCoreDataPersistence : NSObject <MQTTPersistence>
|
||||
|
||||
@end
|
||||
|
||||
@interface MQTTFlow : NSManagedObject <MQTTFlow>
|
||||
@end
|
||||
|
||||
@interface MQTTCoreDataFlow : NSObject <MQTTFlow>
|
||||
@end
|
||||
513
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCoreDataPersistence.m
generated
Executable file
513
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTCoreDataPersistence.m
generated
Executable file
@@ -0,0 +1,513 @@
|
||||
//
|
||||
// MQTTCoreDataPersistence.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 22.03.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTCoreDataPersistence.h"
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@implementation MQTTFlow
|
||||
@dynamic clientId;
|
||||
@dynamic incomingFlag;
|
||||
@dynamic retainedFlag;
|
||||
@dynamic commandType;
|
||||
@dynamic qosLevel;
|
||||
@dynamic messageId;
|
||||
@dynamic topic;
|
||||
@dynamic data;
|
||||
@dynamic deadline;
|
||||
|
||||
@end
|
||||
|
||||
@interface MQTTCoreDataFlow ()
|
||||
|
||||
- (MQTTCoreDataFlow *)initWithContext:(NSManagedObjectContext *)context andObject:(id<MQTTFlow>)object;
|
||||
@property NSManagedObjectContext *context;
|
||||
@property id<MQTTFlow> object;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTCoreDataFlow
|
||||
|
||||
@synthesize context;
|
||||
@synthesize object;
|
||||
|
||||
- (MQTTCoreDataFlow *)initWithContext:(NSManagedObjectContext *)c andObject:(id<MQTTFlow>)o {
|
||||
self = [super init];
|
||||
self.context = c;
|
||||
self.object = o;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)clientId {
|
||||
__block NSString *_clientId;
|
||||
[context performBlockAndWait:^{
|
||||
_clientId = self.object.clientId;
|
||||
}];
|
||||
return _clientId;
|
||||
}
|
||||
|
||||
- (void)setClientId:(NSString *)clientId {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.clientId = clientId;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSNumber *)incomingFlag {
|
||||
__block NSNumber *_incomingFlag;
|
||||
[context performBlockAndWait:^{
|
||||
_incomingFlag = self.object.incomingFlag;
|
||||
}];
|
||||
return _incomingFlag;
|
||||
}
|
||||
|
||||
- (void)setIncomingFlag:(NSNumber *)incomingFlag {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.incomingFlag = incomingFlag;
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (NSNumber *)retainedFlag {
|
||||
__block NSNumber *_retainedFlag;
|
||||
[context performBlockAndWait:^{
|
||||
_retainedFlag = self.object.retainedFlag;
|
||||
}];
|
||||
return _retainedFlag;
|
||||
}
|
||||
|
||||
- (void)setRetainedFlag:(NSNumber *)retainedFlag {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.retainedFlag = retainedFlag;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSNumber *)commandType {
|
||||
__block NSNumber *_commandType;
|
||||
[context performBlockAndWait:^{
|
||||
_commandType = self.object.commandType;
|
||||
}];
|
||||
return _commandType;
|
||||
}
|
||||
|
||||
- (void)setCommandType:(NSNumber *)commandType {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.commandType = commandType;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSNumber *)qosLevel {
|
||||
__block NSNumber *_qosLevel;
|
||||
[context performBlockAndWait:^{
|
||||
_qosLevel = self.object.qosLevel;
|
||||
}];
|
||||
return _qosLevel;
|
||||
}
|
||||
|
||||
- (void)setQosLevel:(NSNumber *)qosLevel {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.qosLevel = qosLevel;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSNumber *)messageId {
|
||||
__block NSNumber *_messageId;
|
||||
[context performBlockAndWait:^{
|
||||
_messageId = self.object.messageId;
|
||||
}];
|
||||
return _messageId;
|
||||
}
|
||||
|
||||
- (void)setMessageId:(NSNumber *)messageId {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.messageId = messageId;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)topic {
|
||||
__block NSString *_topic;
|
||||
[context performBlockAndWait:^{
|
||||
_topic = self.object.topic;
|
||||
}];
|
||||
return _topic;
|
||||
}
|
||||
|
||||
- (void)setTopic:(NSString *)topic {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.topic = topic;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSData *)data {
|
||||
__block NSData *_data;
|
||||
[context performBlockAndWait:^{
|
||||
_data = self.object.data;
|
||||
}];
|
||||
return _data;
|
||||
}
|
||||
|
||||
- (void)setData:(NSData *)data {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.data = data;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDate *)deadline {
|
||||
__block NSDate *_deadline;
|
||||
[context performBlockAndWait:^{
|
||||
_deadline = self.object.deadline;
|
||||
}];
|
||||
return _deadline;
|
||||
}
|
||||
|
||||
- (void)setDeadline:(NSDate *)deadline {
|
||||
[context performBlockAndWait:^{
|
||||
self.object.deadline = deadline;
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MQTTCoreDataPersistence ()
|
||||
|
||||
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
|
||||
@property (assign, nonatomic) unsigned long long fileSize;
|
||||
@property (assign, nonatomic) unsigned long long fileSystemFreeSize;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTCoreDataPersistence
|
||||
@synthesize persistent;
|
||||
@synthesize maxSize;
|
||||
@synthesize maxMessages;
|
||||
@synthesize maxWindowSize;
|
||||
|
||||
- (MQTTCoreDataPersistence *)init {
|
||||
self = [super init];
|
||||
self.persistent = MQTT_PERSISTENT;
|
||||
self.maxSize = MQTT_MAX_SIZE;
|
||||
self.maxMessages = MQTT_MAX_MESSAGES;
|
||||
self.maxWindowSize = MQTT_MAX_WINDOW_SIZE;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSManagedObjectContext *)managedObjectContext {
|
||||
if (!_managedObjectContext) {
|
||||
NSPersistentStoreCoordinator *coordinator = [self createPersistentStoreCoordinator];
|
||||
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
_managedObjectContext.persistentStoreCoordinator = coordinator;
|
||||
}
|
||||
return _managedObjectContext;
|
||||
}
|
||||
|
||||
- (NSUInteger)windowSize:(NSString *)clientId {
|
||||
NSUInteger windowSize = 0;
|
||||
NSArray *flows = [self allFlowsforClientId:clientId
|
||||
incomingFlag:NO];
|
||||
for (MQTTCoreDataFlow *flow in flows) {
|
||||
if ((flow.commandType).unsignedIntegerValue != MQTT_None) {
|
||||
windowSize++;
|
||||
}
|
||||
}
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
- (MQTTCoreDataFlow *)storeMessageForClientId:(NSString *)clientId
|
||||
topic:(NSString *)topic
|
||||
data:(NSData *)data
|
||||
retainFlag:(BOOL)retainFlag
|
||||
qos:(MQTTQosLevel)qos
|
||||
msgId:(UInt16)msgId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
commandType:(UInt8)commandType
|
||||
deadline:(NSDate *)deadline {
|
||||
if (([self allFlowsforClientId:clientId incomingFlag:incomingFlag].count <= self.maxMessages) &&
|
||||
(self.fileSize <= self.maxSize)) {
|
||||
MQTTCoreDataFlow *flow = [self createFlowforClientId:clientId
|
||||
incomingFlag:incomingFlag
|
||||
messageId:msgId];
|
||||
flow.topic = topic;
|
||||
flow.data = data;
|
||||
flow.retainedFlag = @(retainFlag);
|
||||
flow.qosLevel = @(qos);
|
||||
flow.commandType = [NSNumber numberWithUnsignedInteger:commandType];
|
||||
flow.deadline = deadline;
|
||||
return flow;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deleteFlow:(MQTTCoreDataFlow *)flow {
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
[self.managedObjectContext deleteObject:(NSManagedObject *)flow.object];
|
||||
}];
|
||||
[self sync];
|
||||
}
|
||||
|
||||
- (void)deleteAllFlowsForClientId:(NSString *)clientId {
|
||||
DDLogInfo(@"[MQTTCoreDataPersistence] deleteAllFlowsForClientId %@", clientId);
|
||||
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
for (MQTTCoreDataFlow *flow in [self allFlowsforClientId:clientId incomingFlag:TRUE]) {
|
||||
[self.managedObjectContext deleteObject:(NSManagedObject *)flow.object];
|
||||
}
|
||||
for (MQTTCoreDataFlow *flow in [self allFlowsforClientId:clientId incomingFlag:FALSE]) {
|
||||
[self.managedObjectContext deleteObject:(NSManagedObject *)flow.object];
|
||||
}
|
||||
}];
|
||||
[self sync];
|
||||
}
|
||||
|
||||
- (void)sync {
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
[self internalSync];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)internalSync {
|
||||
if (self.managedObjectContext.hasChanges) {
|
||||
DDLogVerbose(@"[MQTTPersistence] pre-sync: i%lu u%lu d%lu",
|
||||
(unsigned long)self.managedObjectContext.insertedObjects.count,
|
||||
(unsigned long)self.managedObjectContext.updatedObjects.count,
|
||||
(unsigned long)self.managedObjectContext.deletedObjects.count
|
||||
);
|
||||
NSError *error = nil;
|
||||
if (![self.managedObjectContext save:&error]) {
|
||||
DDLogError(@"[MQTTPersistence] sync error %@", error);
|
||||
}
|
||||
if (self.managedObjectContext.hasChanges) {
|
||||
DDLogError(@"[MQTTPersistence] sync not complete");
|
||||
}
|
||||
DDLogVerbose(@"[MQTTPersistence] postsync: i%lu u%lu d%lu",
|
||||
(unsigned long)self.managedObjectContext.insertedObjects.count,
|
||||
(unsigned long)self.managedObjectContext.updatedObjects.count,
|
||||
(unsigned long)self.managedObjectContext.deletedObjects.count
|
||||
);
|
||||
[self sizes];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)allFlowsforClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag {
|
||||
NSMutableArray *flows = [NSMutableArray array];
|
||||
__block NSArray *rows;
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"MQTTFlow"];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:
|
||||
@"clientId = %@ and incomingFlag = %@",
|
||||
clientId,
|
||||
@(incomingFlag)
|
||||
];
|
||||
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"deadline" ascending:YES]];
|
||||
NSError *error = nil;
|
||||
rows = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!rows) {
|
||||
DDLogError(@"[MQTTPersistence] allFlowsforClientId %@", error);
|
||||
}
|
||||
}];
|
||||
for (id<MQTTFlow>row in rows) {
|
||||
[flows addObject:[[MQTTCoreDataFlow alloc] initWithContext:self.managedObjectContext andObject:row]];
|
||||
}
|
||||
return flows;
|
||||
}
|
||||
|
||||
- (MQTTCoreDataFlow *)flowforClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
messageId:(UInt16)messageId {
|
||||
__block MQTTCoreDataFlow *flow = nil;
|
||||
|
||||
DDLogVerbose(@"flowforClientId requestingPerform");
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
flow = [self internalFlowForClientId:clientId
|
||||
incomingFlag:incomingFlag
|
||||
messageId:messageId];
|
||||
}];
|
||||
DDLogVerbose(@"flowforClientId performed");
|
||||
return flow;
|
||||
}
|
||||
|
||||
- (MQTTCoreDataFlow *)internalFlowForClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
messageId:(UInt16)messageId {
|
||||
MQTTCoreDataFlow *flow = nil;
|
||||
|
||||
DDLogVerbose(@"flowforClientId performing");
|
||||
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"MQTTFlow"];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:
|
||||
@"clientId = %@ and incomingFlag = %@ and messageId = %@",
|
||||
clientId,
|
||||
@(incomingFlag),
|
||||
@(messageId)
|
||||
];
|
||||
NSArray *rows;
|
||||
NSError *error = nil;
|
||||
rows = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!rows) {
|
||||
DDLogError(@"[MQTTPersistence] flowForClientId %@", error);
|
||||
} else {
|
||||
if (rows.count) {
|
||||
flow = [[MQTTCoreDataFlow alloc] initWithContext:self.managedObjectContext andObject:rows.lastObject];
|
||||
}
|
||||
}
|
||||
return flow;
|
||||
}
|
||||
|
||||
- (MQTTCoreDataFlow *)createFlowforClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
messageId:(UInt16)messageId {
|
||||
MQTTCoreDataFlow *flow = (MQTTCoreDataFlow *)[self flowforClientId:clientId
|
||||
incomingFlag:incomingFlag
|
||||
messageId:messageId];
|
||||
if (!flow) {
|
||||
__block id<MQTTFlow> row;
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
row = [NSEntityDescription insertNewObjectForEntityForName:@"MQTTFlow"
|
||||
inManagedObjectContext:self.managedObjectContext];
|
||||
|
||||
row.clientId = clientId;
|
||||
row.incomingFlag = @(incomingFlag);
|
||||
row.messageId = @(messageId);
|
||||
}];
|
||||
flow = [[MQTTCoreDataFlow alloc] initWithContext:self.managedObjectContext andObject:row];
|
||||
}
|
||||
|
||||
return flow;
|
||||
}
|
||||
|
||||
#pragma mark - Core Data stack
|
||||
|
||||
- (NSManagedObjectModel *)createManagedObjectModel {
|
||||
NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] init];
|
||||
NSMutableArray *entities = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *properties = [[NSMutableArray alloc] init];
|
||||
|
||||
NSAttributeDescription *attributeDescription;
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"clientId";
|
||||
attributeDescription.attributeType = NSStringAttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSString";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"incomingFlag";
|
||||
attributeDescription.attributeType = NSBooleanAttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSNumber";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"retainedFlag";
|
||||
attributeDescription.attributeType = NSBooleanAttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSNumber";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"commandType";
|
||||
attributeDescription.attributeType = NSInteger16AttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSNumber";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"qosLevel";
|
||||
attributeDescription.attributeType = NSInteger16AttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSNumber";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"messageId";
|
||||
attributeDescription.attributeType = NSInteger32AttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSNumber";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"topic";
|
||||
attributeDescription.attributeType = NSStringAttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSString";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"data";
|
||||
attributeDescription.attributeType = NSBinaryDataAttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSData";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
attributeDescription = [[NSAttributeDescription alloc] init];
|
||||
attributeDescription.name = @"deadline";
|
||||
attributeDescription.attributeType = NSDateAttributeType;
|
||||
attributeDescription.attributeValueClassName = @"NSDate";
|
||||
[properties addObject:attributeDescription];
|
||||
|
||||
NSEntityDescription *entityDescription = [[NSEntityDescription alloc] init];
|
||||
entityDescription.name = @"MQTTFlow";
|
||||
entityDescription.managedObjectClassName = @"MQTTFlow";
|
||||
entityDescription.abstract = FALSE;
|
||||
entityDescription.properties = properties;
|
||||
|
||||
[entities addObject:entityDescription];
|
||||
managedObjectModel.entities = entities;
|
||||
|
||||
return managedObjectModel;
|
||||
}
|
||||
|
||||
- (NSPersistentStoreCoordinator *)createPersistentStoreCoordinator {
|
||||
NSURL *persistentStoreURL = [[self applicationDocumentsDirectory]
|
||||
URLByAppendingPathComponent:@"MQTTClient"];
|
||||
DDLogInfo(@"[MQTTPersistence] Persistent store: %@", persistentStoreURL.path);
|
||||
|
||||
|
||||
NSError *error = nil;
|
||||
NSManagedObjectModel *model = [self createManagedObjectModel];
|
||||
NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
|
||||
initWithManagedObjectModel:model];
|
||||
NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption: @YES,
|
||||
NSInferMappingModelAutomaticallyOption: @YES,
|
||||
NSSQLiteAnalyzeOption: @YES,
|
||||
NSSQLiteManualVacuumOption: @YES
|
||||
};
|
||||
|
||||
if (![persistentStoreCoordinator addPersistentStoreWithType:self.persistent ? NSSQLiteStoreType : NSInMemoryStoreType
|
||||
configuration:nil
|
||||
URL:self.persistent ? persistentStoreURL : nil
|
||||
options:options
|
||||
error:&error]) {
|
||||
DDLogError(@"[MQTTPersistence] managedObjectContext save: %@", error);
|
||||
persistentStoreCoordinator = nil;
|
||||
}
|
||||
return persistentStoreCoordinator;
|
||||
}
|
||||
|
||||
#pragma mark - Application's Documents directory
|
||||
|
||||
- (NSURL *)applicationDocumentsDirectory
|
||||
{
|
||||
return [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].lastObject;
|
||||
}
|
||||
|
||||
- (void)sizes {
|
||||
if (self.persistent) {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = paths[0];
|
||||
NSString *persistentStorePath = [documentsDirectory stringByAppendingPathComponent:@"MQTTClient"];
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *fileAttributes = [[NSFileManager defaultManager]
|
||||
attributesOfItemAtPath:persistentStorePath error:&error];
|
||||
NSDictionary *fileSystemAttributes = [[NSFileManager defaultManager]
|
||||
attributesOfFileSystemForPath:persistentStorePath
|
||||
error:&error];
|
||||
self.fileSize = [fileAttributes[NSFileSize] unsignedLongLongValue];
|
||||
self.fileSystemFreeSize = [fileSystemAttributes[NSFileSystemFreeSize] unsignedLongLongValue];
|
||||
} else {
|
||||
self.fileSize = 0;
|
||||
self.fileSystemFreeSize = 0;
|
||||
}
|
||||
DDLogVerbose(@"[MQTTPersistence] sizes %llu/%llu", self.fileSize, self.fileSystemFreeSize);
|
||||
}
|
||||
@end
|
||||
66
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTDecoder.h
generated
Normal file
66
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTDecoder.h
generated
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// MQTTDecoder.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
// based on
|
||||
//
|
||||
// Copyright (c) 2011, 2013, 2lemetry LLC
|
||||
//
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// which accompanies this distribution, and is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// Contributors:
|
||||
// Kyle Roche - initial API and implementation and/or initial documentation
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MQTTMessage.h"
|
||||
|
||||
typedef NS_ENUM(unsigned int, MQTTDecoderEvent) {
|
||||
MQTTDecoderEventProtocolError,
|
||||
MQTTDecoderEventConnectionClosed,
|
||||
MQTTDecoderEventConnectionError
|
||||
};
|
||||
|
||||
typedef NS_ENUM(unsigned int, MQTTDecoderState) {
|
||||
MQTTDecoderStateInitializing,
|
||||
MQTTDecoderStateDecodingHeader,
|
||||
MQTTDecoderStateDecodingLength,
|
||||
MQTTDecoderStateDecodingData,
|
||||
MQTTDecoderStateConnectionClosed,
|
||||
MQTTDecoderStateConnectionError,
|
||||
MQTTDecoderStateProtocolError
|
||||
};
|
||||
|
||||
@class MQTTDecoder;
|
||||
|
||||
@protocol MQTTDecoderDelegate <NSObject>
|
||||
|
||||
- (void)decoder:(MQTTDecoder *)sender didReceiveMessage:(NSData *)data;
|
||||
- (void)decoder:(MQTTDecoder *)sender handleEvent:(MQTTDecoderEvent)eventCode error:(NSError *)error;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface MQTTDecoder: NSObject <NSStreamDelegate>
|
||||
|
||||
@property (nonatomic) MQTTDecoderState state;
|
||||
@property (strong, nonatomic) dispatch_queue_t queue;
|
||||
@property (nonatomic) UInt32 length;
|
||||
@property (nonatomic) UInt32 lengthMultiplier;
|
||||
@property (nonatomic) int offset;
|
||||
@property (strong, nonatomic) NSMutableData *dataBuffer;
|
||||
|
||||
@property (weak, nonatomic) id<MQTTDecoderDelegate> delegate;
|
||||
|
||||
- (void)open;
|
||||
- (void)close;
|
||||
- (void)decodeMessage:(NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
215
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTDecoder.m
generated
Normal file
215
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTDecoder.m
generated
Normal file
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// MQTTDecoder.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTDecoder.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTDecoder() {
|
||||
void *QueueIdentityKey;
|
||||
}
|
||||
|
||||
@property (nonatomic) NSMutableArray<NSInputStream *> *streams;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTDecoder
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.state = MQTTDecoderStateInitializing;
|
||||
self.streams = [NSMutableArray arrayWithCapacity:5];
|
||||
self.queue = dispatch_get_main_queue();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (void)setQueue:(dispatch_queue_t)queue {
|
||||
_queue = queue;
|
||||
|
||||
// We're going to use dispatch_queue_set_specific() to "mark" our queue.
|
||||
// The dispatch_queue_set_specific() and dispatch_get_specific() functions take a "void *key" parameter.
|
||||
// Later we can use dispatch_get_specific() to determine if we're executing on our queue.
|
||||
// From the documentation:
|
||||
//
|
||||
// > Keys are only compared as pointers and are never dereferenced.
|
||||
// > Thus, you can use a pointer to a static variable for a specific subsystem or
|
||||
// > any other value that allows you to identify the value uniquely.
|
||||
//
|
||||
// So we're just going to use the memory address of an ivar.
|
||||
|
||||
dispatch_queue_set_specific(_queue, &QueueIdentityKey, (__bridge void *)_queue, NULL);
|
||||
}
|
||||
|
||||
- (void)decodeMessage:(NSData *)data {
|
||||
NSInputStream *stream = [NSInputStream inputStreamWithData:data];
|
||||
CFReadStreamRef readStream = (__bridge CFReadStreamRef)stream;
|
||||
CFReadStreamSetDispatchQueue(readStream, self.queue);
|
||||
[self openStream:stream];
|
||||
}
|
||||
|
||||
- (void)openStream:(NSInputStream *)stream {
|
||||
[self.streams addObject:stream];
|
||||
stream.delegate = self;
|
||||
DDLogVerbose(@"[MQTTDecoder] #streams=%lu", (unsigned long)self.streams.count);
|
||||
if (self.streams.count == 1) {
|
||||
[stream open];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)open {
|
||||
self.state = MQTTDecoderStateDecodingHeader;
|
||||
}
|
||||
|
||||
- (void)internalClose {
|
||||
if (self.streams) {
|
||||
for (NSInputStream *stream in self.streams) {
|
||||
[stream close];
|
||||
[stream setDelegate:nil];
|
||||
}
|
||||
[self.streams removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
// https://github.com/novastone-media/MQTT-Client-Framework/issues/325
|
||||
// We need to make sure that we are closing streams on their queue
|
||||
// Otherwise, we end up with race condition where delegate is deallocated
|
||||
// but still used by run loop event
|
||||
if (self.queue != dispatch_get_specific(&QueueIdentityKey)) {
|
||||
dispatch_sync(self.queue, ^{
|
||||
[self internalClose];
|
||||
});
|
||||
} else {
|
||||
[self internalClose];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stream:(NSStream *)sender handleEvent:(NSStreamEvent)eventCode {
|
||||
// We contact our delegate, MQTTSession at some point in this method
|
||||
// This call can cause MQTTSession to dealloc and thus, MQTTDecoder to dealloc
|
||||
// So we end up with invalid object in the middle of the method
|
||||
// To prevent this we retain self for duration of this method call
|
||||
MQTTDecoder *strongDecoder = self;
|
||||
(void)strongDecoder;
|
||||
|
||||
NSInputStream *stream = (NSInputStream *)sender;
|
||||
|
||||
if (eventCode & NSStreamEventOpenCompleted) {
|
||||
DDLogVerbose(@"[MQTTDecoder] NSStreamEventOpenCompleted");
|
||||
}
|
||||
|
||||
if (eventCode & NSStreamEventHasBytesAvailable) {
|
||||
DDLogVerbose(@"[MQTTDecoder] NSStreamEventHasBytesAvailable");
|
||||
|
||||
if (self.state == MQTTDecoderStateDecodingHeader) {
|
||||
UInt8 buffer;
|
||||
NSInteger n = [stream read:&buffer maxLength:1];
|
||||
if (n == -1) {
|
||||
self.state = MQTTDecoderStateConnectionError;
|
||||
[self.delegate decoder:self handleEvent:MQTTDecoderEventConnectionError error:stream.streamError];
|
||||
} else if (n == 1) {
|
||||
self.length = 0;
|
||||
self.lengthMultiplier = 1;
|
||||
self.state = MQTTDecoderStateDecodingLength;
|
||||
self.dataBuffer = [[NSMutableData alloc] init];
|
||||
[self.dataBuffer appendBytes:&buffer length:1];
|
||||
self.offset = 1;
|
||||
DDLogVerbose(@"[MQTTDecoder] fixedHeader=0x%02x", buffer);
|
||||
}
|
||||
}
|
||||
while (self.state == MQTTDecoderStateDecodingLength) {
|
||||
// TODO: check max packet length(prevent evil server response)
|
||||
UInt8 digit;
|
||||
NSInteger n = [stream read:&digit maxLength:1];
|
||||
if (n == -1) {
|
||||
self.state = MQTTDecoderStateConnectionError;
|
||||
[self.delegate decoder:self handleEvent:MQTTDecoderEventConnectionError error:stream.streamError];
|
||||
break;
|
||||
} else if (n == 0) {
|
||||
break;
|
||||
}
|
||||
DDLogVerbose(@"[MQTTDecoder] digit=0x%02x 0x%02x %d %d", digit, digit & 0x7f, (unsigned int)self.length, (unsigned int)self.lengthMultiplier);
|
||||
[self.dataBuffer appendBytes:&digit length:1];
|
||||
self.offset++;
|
||||
self.length += ((digit & 0x7f) * self.lengthMultiplier);
|
||||
if ((digit & 0x80) == 0x00) {
|
||||
self.state = MQTTDecoderStateDecodingData;
|
||||
} else {
|
||||
self.lengthMultiplier *= 128;
|
||||
}
|
||||
}
|
||||
DDLogVerbose(@"[MQTTDecoder] remainingLength=%d", (unsigned int)self.length);
|
||||
|
||||
if (self.state == MQTTDecoderStateDecodingData) {
|
||||
if (self.length > 0) {
|
||||
NSInteger n, toRead;
|
||||
UInt8 buffer[768];
|
||||
toRead = self.length + self.offset - self.dataBuffer.length;
|
||||
if (toRead > sizeof buffer) {
|
||||
toRead = sizeof buffer;
|
||||
}
|
||||
n = [stream read:buffer maxLength:toRead];
|
||||
if (n == -1) {
|
||||
self.state = MQTTDecoderStateConnectionError;
|
||||
[self.delegate decoder:self handleEvent:MQTTDecoderEventConnectionError error:stream.streamError];
|
||||
} else {
|
||||
DDLogVerbose(@"[MQTTDecoder] read %ld %ld", (long)toRead, (long)n);
|
||||
[self.dataBuffer appendBytes:buffer length:n];
|
||||
}
|
||||
}
|
||||
if (self.dataBuffer.length == self.length + self.offset) {
|
||||
DDLogVerbose(@"[MQTTDecoder] received (%lu)=%@...", (unsigned long)self.dataBuffer.length,
|
||||
[self.dataBuffer subdataWithRange:NSMakeRange(0, MIN(256, self.dataBuffer.length))]);
|
||||
[self.delegate decoder:self didReceiveMessage:self.dataBuffer];
|
||||
self.dataBuffer = nil;
|
||||
self.state = MQTTDecoderStateDecodingHeader;
|
||||
} else {
|
||||
DDLogWarn(@"[MQTTDecoder] oops received (%lu)=%@...", (unsigned long)self.dataBuffer.length,
|
||||
[self.dataBuffer subdataWithRange:NSMakeRange(0, MIN(256, self.dataBuffer.length))]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eventCode & NSStreamEventHasSpaceAvailable) {
|
||||
DDLogVerbose(@"[MQTTDecoder] NSStreamEventHasSpaceAvailable");
|
||||
}
|
||||
|
||||
if (eventCode & NSStreamEventEndEncountered) {
|
||||
DDLogVerbose(@"[MQTTDecoder] NSStreamEventEndEncountered");
|
||||
|
||||
if (self.streams) {
|
||||
[stream setDelegate:nil];
|
||||
[stream close];
|
||||
[self.streams removeObject:stream];
|
||||
if (self.streams.count) {
|
||||
NSInputStream *stream = (self.streams)[0];
|
||||
[stream open];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eventCode & NSStreamEventErrorOccurred) {
|
||||
DDLogVerbose(@"[MQTTDecoder] NSStreamEventErrorOccurred");
|
||||
|
||||
self.state = MQTTDecoderStateConnectionError;
|
||||
NSError *error = stream.streamError;
|
||||
if (self.streams) {
|
||||
[self.streams removeObject:stream];
|
||||
if (self.streams.count) {
|
||||
NSInputStream *stream = (self.streams)[0];
|
||||
[stream open];
|
||||
}
|
||||
}
|
||||
[self.delegate decoder:self handleEvent:MQTTDecoderEventConnectionError error:error];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
16
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTInMemoryPersistence.h
generated
Normal file
16
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTInMemoryPersistence.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// MQTTInMemoryPersistence.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 22.03.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MQTTPersistence.h"
|
||||
|
||||
@interface MQTTInMemoryPersistence : NSObject <MQTTPersistence>
|
||||
@end
|
||||
|
||||
@interface MQTTInMemoryFlow : NSObject <MQTTFlow>
|
||||
@end
|
||||
180
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTInMemoryPersistence.m
generated
Executable file
180
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTInMemoryPersistence.m
generated
Executable file
@@ -0,0 +1,180 @@
|
||||
//
|
||||
// MQTTInMemoryPersistence.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 22.03.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTInMemoryPersistence.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@implementation MQTTInMemoryFlow
|
||||
@synthesize clientId;
|
||||
@synthesize incomingFlag;
|
||||
@synthesize retainedFlag;
|
||||
@synthesize commandType;
|
||||
@synthesize qosLevel;
|
||||
@synthesize messageId;
|
||||
@synthesize topic;
|
||||
@synthesize data;
|
||||
@synthesize deadline;
|
||||
|
||||
@end
|
||||
|
||||
@interface MQTTInMemoryPersistence()
|
||||
@end
|
||||
|
||||
static NSMutableDictionary *clientIds;
|
||||
|
||||
@implementation MQTTInMemoryPersistence
|
||||
@synthesize maxSize;
|
||||
@synthesize persistent;
|
||||
@synthesize maxMessages;
|
||||
@synthesize maxWindowSize;
|
||||
|
||||
- (MQTTInMemoryPersistence *)init {
|
||||
self = [super init];
|
||||
self.maxMessages = MQTT_MAX_MESSAGES;
|
||||
self.maxWindowSize = MQTT_MAX_WINDOW_SIZE;
|
||||
@synchronized(clientIds) {
|
||||
if (!clientIds) {
|
||||
clientIds = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)windowSize:(NSString *)clientId {
|
||||
NSUInteger windowSize = 0;
|
||||
NSArray *flows = [self allFlowsforClientId:clientId
|
||||
incomingFlag:NO];
|
||||
for (MQTTInMemoryFlow *flow in flows) {
|
||||
if ((flow.commandType).intValue != MQTT_None) {
|
||||
windowSize++;
|
||||
}
|
||||
}
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
- (MQTTInMemoryFlow *)storeMessageForClientId:(NSString *)clientId
|
||||
topic:(NSString *)topic
|
||||
data:(NSData *)data
|
||||
retainFlag:(BOOL)retainFlag
|
||||
qos:(MQTTQosLevel)qos
|
||||
msgId:(UInt16)msgId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
commandType:(UInt8)commandType
|
||||
deadline:(NSDate *)deadline {
|
||||
@synchronized(clientIds) {
|
||||
|
||||
if (([self allFlowsforClientId:clientId incomingFlag:incomingFlag].count <= self.maxMessages)) {
|
||||
MQTTInMemoryFlow *flow = (MQTTInMemoryFlow *)[self createFlowforClientId:clientId
|
||||
incomingFlag:incomingFlag
|
||||
messageId:msgId];
|
||||
flow.topic = topic;
|
||||
flow.data = data;
|
||||
flow.retainedFlag = @(retainFlag);
|
||||
flow.qosLevel = @(qos);
|
||||
flow.commandType = [NSNumber numberWithUnsignedInteger:commandType];
|
||||
flow.deadline = deadline;
|
||||
return flow;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deleteFlow:(MQTTInMemoryFlow *)flow {
|
||||
@synchronized(clientIds) {
|
||||
|
||||
NSMutableDictionary *clientIdFlows = clientIds[flow.clientId];
|
||||
if (clientIdFlows) {
|
||||
NSMutableDictionary *clientIdDirectedFlow = clientIdFlows[flow.incomingFlag];
|
||||
if (clientIdDirectedFlow) {
|
||||
[clientIdDirectedFlow removeObjectForKey:flow.messageId];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deleteAllFlowsForClientId:(NSString *)clientId {
|
||||
@synchronized(clientIds) {
|
||||
|
||||
DDLogInfo(@"[MQTTInMemoryPersistence] deleteAllFlowsForClientId %@", clientId);
|
||||
[clientIds removeObjectForKey:clientId];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sync {
|
||||
//
|
||||
}
|
||||
|
||||
- (NSArray *)allFlowsforClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag {
|
||||
@synchronized(clientIds) {
|
||||
|
||||
NSMutableArray *flows = nil;
|
||||
NSMutableDictionary *clientIdFlows = clientIds[clientId];
|
||||
if (clientIdFlows) {
|
||||
NSMutableDictionary *clientIdDirectedFlow = clientIdFlows[@(incomingFlag)];
|
||||
if (clientIdDirectedFlow) {
|
||||
flows = [NSMutableArray array];
|
||||
NSArray *keys = [clientIdDirectedFlow.allKeys sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES]]];
|
||||
for (id key in keys) {
|
||||
[flows addObject:clientIdDirectedFlow[key]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return flows;
|
||||
}
|
||||
}
|
||||
|
||||
- (MQTTInMemoryFlow *)flowforClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
messageId:(UInt16)messageId {
|
||||
@synchronized(clientIds) {
|
||||
|
||||
MQTTInMemoryFlow *flow = nil;
|
||||
|
||||
NSMutableDictionary *clientIdFlows = clientIds[clientId];
|
||||
if (clientIdFlows) {
|
||||
NSMutableDictionary *clientIdDirectedFlow = clientIdFlows[@(incomingFlag)];
|
||||
if (clientIdDirectedFlow) {
|
||||
flow = clientIdDirectedFlow[[NSNumber numberWithUnsignedInteger:messageId]];
|
||||
}
|
||||
}
|
||||
|
||||
return flow;
|
||||
}
|
||||
}
|
||||
|
||||
- (MQTTInMemoryFlow *)createFlowforClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
messageId:(UInt16)messageId {
|
||||
@synchronized(clientIds) {
|
||||
NSMutableDictionary *clientIdFlows = clientIds[clientId];
|
||||
if (!clientIdFlows) {
|
||||
clientIdFlows = [[NSMutableDictionary alloc] init];
|
||||
clientIds[clientId] = clientIdFlows;
|
||||
}
|
||||
|
||||
NSMutableDictionary *clientIdDirectedFlow = clientIdFlows[@(incomingFlag)];
|
||||
if (!clientIdDirectedFlow) {
|
||||
clientIdDirectedFlow = [[NSMutableDictionary alloc] init];
|
||||
clientIdFlows[@(incomingFlag)] = clientIdDirectedFlow;
|
||||
}
|
||||
|
||||
MQTTInMemoryFlow *flow = [[MQTTInMemoryFlow alloc] init];
|
||||
flow.clientId = clientId;
|
||||
flow.incomingFlag = @(incomingFlag);
|
||||
flow.messageId = [NSNumber numberWithUnsignedInteger:messageId];
|
||||
|
||||
clientIdDirectedFlow[[NSNumber numberWithUnsignedInteger:messageId]] = flow;
|
||||
|
||||
return flow;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
123
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTLog.h
generated
Normal file
123
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTLog.h
generated
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// MQTTLog.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 10.02.16.
|
||||
// Copyright © 2016-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#ifdef LUMBERJACK
|
||||
|
||||
#define LOG_LEVEL_DEF ddLogLevel
|
||||
#import <CocoaLumberjack/CocoaLumberjack.h>
|
||||
|
||||
#else /* LUMBERJACK */
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, DDLogFlag){
|
||||
/**
|
||||
* 0...00001 DDLogFlagError
|
||||
*/
|
||||
DDLogFlagError = (1 << 0),
|
||||
|
||||
/**
|
||||
* 0...00010 DDLogFlagWarning
|
||||
*/
|
||||
DDLogFlagWarning = (1 << 1),
|
||||
|
||||
/**
|
||||
* 0...00100 DDLogFlagInfo
|
||||
*/
|
||||
DDLogFlagInfo = (1 << 2),
|
||||
|
||||
/**
|
||||
* 0...01000 DDLogFlagDebug
|
||||
*/
|
||||
DDLogFlagDebug = (1 << 3),
|
||||
|
||||
/**
|
||||
* 0...10000 DDLogFlagVerbose
|
||||
*/
|
||||
DDLogFlagVerbose = (1 << 4)
|
||||
};
|
||||
|
||||
|
||||
typedef NS_ENUM(NSUInteger, DDLogLevel){
|
||||
DDLogLevelOff = 0,
|
||||
|
||||
/**
|
||||
* Error logs only
|
||||
*/
|
||||
DDLogLevelError = (DDLogFlagError),
|
||||
|
||||
/**
|
||||
* Error and warning logs
|
||||
*/
|
||||
DDLogLevelWarning = (DDLogLevelError | DDLogFlagWarning),
|
||||
|
||||
/**
|
||||
* Error, warning and info logs
|
||||
*/
|
||||
DDLogLevelInfo = (DDLogLevelWarning | DDLogFlagInfo),
|
||||
|
||||
/**
|
||||
* Error, warning, info and debug logs
|
||||
*/
|
||||
DDLogLevelDebug = (DDLogLevelInfo | DDLogFlagDebug),
|
||||
|
||||
/**
|
||||
* Error, warning, info, debug and verbose logs
|
||||
*/
|
||||
DDLogLevelVerbose = (DDLogLevelDebug | DDLogFlagVerbose),
|
||||
|
||||
/**
|
||||
* All logs (1...11111)
|
||||
*/
|
||||
DDLogLevelAll = NSUIntegerMax
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define DDLogVerbose if (ddLogLevel & DDLogFlagVerbose) NSLog
|
||||
#define DDLogDebug if (ddLogLevel & DDLogFlagDebug) NSLog
|
||||
#define DDLogWarn if (ddLogLevel & DDLogFlagWarning) NSLog
|
||||
#define DDLogInfo if (ddLogLevel & DDLogFlagInfo) NSLog
|
||||
#define DDLogError if (ddLogLevel & DDLogFlagError) NSLog
|
||||
|
||||
#else
|
||||
|
||||
#define DDLogVerbose(...)
|
||||
#define DDLogDebug(...)
|
||||
#define DDLogWarn(...)
|
||||
#define DDLogInfo(...)
|
||||
#define DDLogError(...)
|
||||
|
||||
#endif /* DEBUG */
|
||||
#endif /* LUMBERJACK */
|
||||
|
||||
extern DDLogLevel ddLogLevel;
|
||||
|
||||
/** MQTTLog lets you define the log level for MQTTClient
|
||||
* independently of using CocoaLumberjack
|
||||
*/
|
||||
@interface MQTTLog: NSObject
|
||||
|
||||
/** setLogLevel controls the log level for MQTTClient
|
||||
* @param logLevel as follows:
|
||||
*
|
||||
* default for DEBUG builds is DDLogLevelVerbose
|
||||
* default for RELEASE builds is DDLogLevelWarning
|
||||
*
|
||||
* Available log levels:
|
||||
* DDLogLevelAll
|
||||
* DDLogLevelVerbose
|
||||
* DDLogLevelDebug
|
||||
* DDLogLevelInfo
|
||||
* DDLogLevelWarning
|
||||
* DDLogLevelError
|
||||
* DDLogLevelOff
|
||||
*/
|
||||
+ (void)setLogLevel:(DDLogLevel)logLevel;
|
||||
|
||||
@end
|
||||
27
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTLog.m
generated
Normal file
27
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTLog.m
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// MQTTLog.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Josip Cavar on 06/07/2017.
|
||||
//
|
||||
//
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@implementation MQTTLog
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
DDLogLevel ddLogLevel = DDLogLevelVerbose;
|
||||
|
||||
#else
|
||||
|
||||
DDLogLevel ddLogLevel = DDLogLevelWarning;
|
||||
|
||||
#endif
|
||||
|
||||
+ (void)setLogLevel:(DDLogLevel)logLevel {
|
||||
ddLogLevel = logLevel;
|
||||
}
|
||||
|
||||
@end
|
||||
233
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTMessage.h
generated
Normal file
233
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTMessage.h
generated
Normal file
@@ -0,0 +1,233 @@
|
||||
//
|
||||
// MQTTMessage.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
// based on
|
||||
//
|
||||
// Copyright (c) 2011, 2013, 2lemetry LLC
|
||||
//
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// which accompanies this distribution, and is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// Contributors:
|
||||
// Kyle Roche - initial API and implementation and/or initial documentation
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@class MQTTProperties;
|
||||
|
||||
/**
|
||||
Enumeration of MQTT Quality of Service levels
|
||||
*/
|
||||
typedef NS_ENUM(UInt8, MQTTQosLevel) {
|
||||
MQTTQosLevelAtMostOnce = 0,
|
||||
MQTTQosLevelAtLeastOnce = 1,
|
||||
MQTTQosLevelExactlyOnce = 2
|
||||
};
|
||||
|
||||
/**
|
||||
Enumeration of MQTT protocol version
|
||||
*/
|
||||
typedef NS_ENUM(UInt8, MQTTProtocolVersion) {
|
||||
MQTTProtocolVersion0 = 0,
|
||||
MQTTProtocolVersion31 = 3,
|
||||
MQTTProtocolVersion311 = 4,
|
||||
MQTTProtocolVersion50 = 5
|
||||
};
|
||||
|
||||
typedef NS_ENUM(UInt8, MQTTCommandType) {
|
||||
MQTT_None = 0,
|
||||
MQTTConnect = 1,
|
||||
MQTTConnack = 2,
|
||||
MQTTPublish = 3,
|
||||
MQTTPuback = 4,
|
||||
MQTTPubrec = 5,
|
||||
MQTTPubrel = 6,
|
||||
MQTTPubcomp = 7,
|
||||
MQTTSubscribe = 8,
|
||||
MQTTSuback = 9,
|
||||
MQTTUnsubscribe = 10,
|
||||
MQTTUnsuback = 11,
|
||||
MQTTPingreq = 12,
|
||||
MQTTPingresp = 13,
|
||||
MQTTDisconnect = 14,
|
||||
MQTTAuth = 15
|
||||
};
|
||||
|
||||
@interface MQTTMessage : NSObject
|
||||
|
||||
@property (nonatomic) MQTTCommandType type;
|
||||
@property (nonatomic) MQTTQosLevel qos;
|
||||
@property (nonatomic) BOOL retainFlag;
|
||||
@property (nonatomic) BOOL dupFlag;
|
||||
@property (nonatomic) UInt16 mid;
|
||||
@property (strong, nonatomic) NSData *data;
|
||||
@property (strong, nonatomic) NSNumber *returnCode;
|
||||
@property (strong, nonatomic) NSNumber *connectAcknowledgeFlags;
|
||||
@property (strong, nonatomic) MQTTProperties *properties;
|
||||
|
||||
/**
|
||||
Enumeration of MQTT return codes
|
||||
*/
|
||||
|
||||
typedef NS_ENUM(NSUInteger, MQTTReturnCode) {
|
||||
MQTTAccepted = 0,
|
||||
MQTTRefusedUnacceptableProtocolVersion = 1,
|
||||
MQTTRefusedIdentiferRejected = 2,
|
||||
MQTTRefusedServerUnavailable = 3,
|
||||
MQTTRefusedBadUserNameOrPassword = 4,
|
||||
MQTTRefusedNotAuthorized = 5,
|
||||
|
||||
MQTTSuccess = 0,
|
||||
MQTTDisconnectWithWillMessage = 4,
|
||||
MQTTNoSubscriptionExisted = 17,
|
||||
MQTTContinueAuthentication = 24,
|
||||
MQTTReAuthenticate = 25,
|
||||
MQTTUnspecifiedError = 128,
|
||||
MQTTMalformedPacket = 129,
|
||||
MQTTProtocolError = 130,
|
||||
MQTTImplementationSpecificError = 131,
|
||||
MQTTUnsupportedProtocolVersion = 132,
|
||||
MQTTClientIdentifierNotValid = 133,
|
||||
MQTTBadUserNameOrPassword = 134,
|
||||
MQTTNotAuthorized = 135,
|
||||
MQTTServerUnavailable = 136,
|
||||
MQTTServerBusy = 137,
|
||||
MQTTBanned = 138,
|
||||
MQTTServerShuttingDown = 139,
|
||||
MQTTBadAuthenticationMethod = 140,
|
||||
MQTTKeepAliveTimeout = 141,
|
||||
MQTTSessionTakenOver = 142,
|
||||
MQTTTopicFilterInvalid = 143,
|
||||
MQTTTopicNameInvalid = 144,
|
||||
MQTTPacketIdentifierInUse = 145,
|
||||
MQTTPacketIdentifierNotFound = 146,
|
||||
MQTTReceiveMaximumExceeded = 147,
|
||||
MQTTPacketTooLarge = 149,
|
||||
MQTTMessageRateTooHigh = 150,
|
||||
MQTTQuotaExceeded = 151,
|
||||
MQTTAdministrativeAction = 152,
|
||||
MQTTPayloadFormatInvalid = 153,
|
||||
MQTTRetainNotSupported = 154,
|
||||
MQTTQoSNotSupported = 155,
|
||||
MQTTUseAnotherServer = 156,
|
||||
MQTTServerMoved = 157,
|
||||
MQTTSharedSubscriptionNotSupported = 158,
|
||||
MQTTConnectionRateExceeded = 159,
|
||||
MQTTSubscriptionIdentifiersNotSupported = 161,
|
||||
MQTTWildcardSubscriptionNotSupported = 162
|
||||
};
|
||||
|
||||
// factory methods
|
||||
+ (MQTTMessage *)connectMessageWithClientId:(NSString*)clientId
|
||||
userName:(NSString*)userName
|
||||
password:(NSString*)password
|
||||
keepAlive:(NSInteger)keeplive
|
||||
cleanSession:(BOOL)cleanSessionFlag
|
||||
will:(BOOL)will
|
||||
willTopic:(NSString*)willTopic
|
||||
willMsg:(NSData*)willData
|
||||
willQoS:(MQTTQosLevel)willQoS
|
||||
willRetain:(BOOL)willRetainFlag
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
sessionExpiryInterval:(NSNumber *)sessionExpiryInterval
|
||||
authMethod:(NSString *)authMethod
|
||||
authData:(NSData *)authData
|
||||
requestProblemInformation:(NSNumber *)requestProblemInformation
|
||||
willDelayInterval:(NSNumber *)willDelayInterval
|
||||
requestResponseInformation:(NSNumber *)requestResponseInformation
|
||||
receiveMaximum:(NSNumber *)receiveMaximum
|
||||
topicAliasMaximum:(NSNumber *)topicAliasMaximum
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty
|
||||
maximumPacketSize:(NSNumber *)maximumPacketSize
|
||||
;
|
||||
|
||||
+ (MQTTMessage *)pingreqMessage;
|
||||
|
||||
+ (MQTTMessage *)disconnectMessage:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
sessionExpiryInterval:(NSNumber *)sessionExpiryInterval
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty;
|
||||
|
||||
+ (MQTTMessage *)subscribeMessageWithMessageId:(UInt16)msgId
|
||||
topics:(NSDictionary *)topics
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
subscriptionIdentifier:(NSNumber *)subscriptionIdentifier;
|
||||
|
||||
+ (MQTTMessage *)unsubscribeMessageWithMessageId:(UInt16)msgId
|
||||
topics:(NSArray *)topics
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel;
|
||||
|
||||
+ (MQTTMessage *)publishMessageWithData:(NSData*)payload
|
||||
onTopic:(NSString*)topic
|
||||
qos:(MQTTQosLevel)qosLevel
|
||||
msgId:(UInt16)msgId
|
||||
retainFlag:(BOOL)retain
|
||||
dupFlag:(BOOL)dup
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
payloadFormatIndicator:(NSNumber *)payloadFormatIndicator
|
||||
publicationExpiryInterval:(NSNumber *)publicationExpiryInterval
|
||||
topicAlias:(NSNumber *)topicAlias
|
||||
responseTopic:(NSString *)responseTopic
|
||||
correlationData:(NSData *)correlationData
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty
|
||||
contentType:(NSString *)contentType;
|
||||
|
||||
+ (MQTTMessage *)pubackMessageWithMessageId:(UInt16)msgId
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty;
|
||||
|
||||
+ (MQTTMessage *)pubrecMessageWithMessageId:(UInt16)msgId
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty;
|
||||
|
||||
+ (MQTTMessage *)pubrelMessageWithMessageId:(UInt16)msgId
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty;
|
||||
|
||||
+ (MQTTMessage *)pubcompMessageWithMessageId:(UInt16)msgId
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty;
|
||||
|
||||
+ (MQTTMessage *)messageFromData:(NSData *)data protocolLevel:(MQTTProtocolVersion)protocolLevel;
|
||||
|
||||
// instance methods
|
||||
- (instancetype)initWithType:(MQTTCommandType)type;
|
||||
- (instancetype)initWithType:(MQTTCommandType)type
|
||||
data:(NSData *)data;
|
||||
- (instancetype)initWithType:(MQTTCommandType)type
|
||||
qos:(MQTTQosLevel)qos
|
||||
data:(NSData *)data;
|
||||
- (instancetype)initWithType:(MQTTCommandType)type
|
||||
qos:(MQTTQosLevel)qos
|
||||
retainFlag:(BOOL)retainFlag
|
||||
dupFlag:(BOOL)dupFlag
|
||||
data:(NSData *)data;
|
||||
|
||||
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSData *wireFormat;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@interface NSMutableData (MQTT)
|
||||
- (void)appendByte:(UInt8)byte;
|
||||
- (void)appendUInt16BigEndian:(UInt16)val;
|
||||
- (void)appendUInt32BigEndian:(UInt32)val;
|
||||
- (void)appendVariableLength:(unsigned long)length;
|
||||
- (void)appendMQTTString:(NSString *)string;
|
||||
- (void)appendBinaryData:(NSData *)data;
|
||||
|
||||
@end
|
||||
762
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTMessage.m
generated
Normal file
762
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTMessage.m
generated
Normal file
@@ -0,0 +1,762 @@
|
||||
//
|
||||
// MQTTMessage.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
// based on
|
||||
//
|
||||
// Copyright (c) 2011, 2013, 2lemetry LLC
|
||||
//
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// which accompanies this distribution, and is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// Contributors:
|
||||
// Kyle Roche - initial API and implementation and/or initial documentation
|
||||
//
|
||||
|
||||
#import "MQTTMessage.h"
|
||||
#import "MQTTProperties.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@implementation MQTTMessage
|
||||
|
||||
+ (MQTTMessage *)connectMessageWithClientId:(NSString *)clientId
|
||||
userName:(NSString *)userName
|
||||
password:(NSString *)password
|
||||
keepAlive:(NSInteger)keepAlive
|
||||
cleanSession:(BOOL)cleanSessionFlag
|
||||
will:(BOOL)will
|
||||
willTopic:(NSString *)willTopic
|
||||
willMsg:(NSData *)willMsg
|
||||
willQoS:(MQTTQosLevel)willQoS
|
||||
willRetain:(BOOL)willRetainFlag
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
sessionExpiryInterval:(NSNumber *)sessionExpiryInterval
|
||||
authMethod:(NSString *)authMethod
|
||||
authData:(NSData *)authData
|
||||
requestProblemInformation:(NSNumber *)requestProblemInformation
|
||||
willDelayInterval:(NSNumber *)willDelayInterval
|
||||
requestResponseInformation:(NSNumber *)requestResponseInformation
|
||||
receiveMaximum:(NSNumber *)receiveMaximum
|
||||
topicAliasMaximum:(NSNumber *)topicAliasMaximum
|
||||
userProperty:(NSDictionary<NSString *,NSString *> *)userProperty
|
||||
maximumPacketSize:(NSNumber *)maximumPacketSize {
|
||||
/*
|
||||
* setup flags w/o basic plausibility checks
|
||||
*
|
||||
*/
|
||||
UInt8 flags = 0x00;
|
||||
|
||||
if (cleanSessionFlag) {
|
||||
flags |= 0x02;
|
||||
}
|
||||
|
||||
if (userName) {
|
||||
flags |= 0x80;
|
||||
}
|
||||
if (password) {
|
||||
flags |= 0x40;
|
||||
}
|
||||
|
||||
if (will) {
|
||||
flags |= 0x04;
|
||||
}
|
||||
|
||||
flags |= ((willQoS & 0x03) << 3);
|
||||
|
||||
if (willRetainFlag) {
|
||||
flags |= 0x20;
|
||||
}
|
||||
|
||||
NSMutableData* data = [NSMutableData data];
|
||||
|
||||
switch (protocolLevel) {
|
||||
case MQTTProtocolVersion50:
|
||||
[data appendMQTTString:@"MQTT"];
|
||||
[data appendByte:MQTTProtocolVersion50];
|
||||
break;
|
||||
|
||||
case MQTTProtocolVersion311:
|
||||
[data appendMQTTString:@"MQTT"];
|
||||
[data appendByte:MQTTProtocolVersion311];
|
||||
break;
|
||||
|
||||
case MQTTProtocolVersion31:
|
||||
[data appendMQTTString:@"MQIsdp"];
|
||||
[data appendByte:MQTTProtocolVersion31];
|
||||
break;
|
||||
|
||||
case MQTTProtocolVersion0:
|
||||
[data appendMQTTString:@""];
|
||||
[data appendByte:protocolLevel];
|
||||
break;
|
||||
|
||||
default:
|
||||
[data appendMQTTString:@"MQTT"];
|
||||
[data appendByte:protocolLevel];
|
||||
break;
|
||||
}
|
||||
[data appendByte:flags];
|
||||
[data appendUInt16BigEndian:keepAlive];
|
||||
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
NSMutableData *properties = [[NSMutableData alloc] init];
|
||||
if (sessionExpiryInterval) {
|
||||
[properties appendByte:MQTTSessionExpiryInterval];
|
||||
[properties appendUInt32BigEndian:sessionExpiryInterval.unsignedIntValue];
|
||||
}
|
||||
if (authMethod) {
|
||||
[properties appendByte:MQTTAuthMethod];
|
||||
[properties appendMQTTString:authMethod];
|
||||
}
|
||||
if (authData) {
|
||||
[properties appendByte:MQTTAuthData];
|
||||
[properties appendBinaryData:authData];
|
||||
}
|
||||
if (requestProblemInformation) {
|
||||
[properties appendByte:MQTTRequestProblemInformation];
|
||||
[properties appendByte:requestProblemInformation.unsignedIntValue];
|
||||
}
|
||||
if (willDelayInterval) {
|
||||
[properties appendByte:MQTTWillDelayInterval];
|
||||
[properties appendUInt32BigEndian:willDelayInterval.unsignedIntValue];
|
||||
}
|
||||
if (requestResponseInformation) {
|
||||
[properties appendByte:MQTTRequestResponseInformation];
|
||||
[properties appendByte:requestResponseInformation.unsignedIntValue];
|
||||
}
|
||||
if (receiveMaximum) {
|
||||
[properties appendByte:MQTTReceiveMaximum];
|
||||
[properties appendUInt16BigEndian:receiveMaximum.unsignedIntValue];
|
||||
}
|
||||
if (topicAliasMaximum) {
|
||||
[properties appendByte:MQTTTopicAliasMaximum];
|
||||
[properties appendUInt16BigEndian:topicAliasMaximum.unsignedIntValue];
|
||||
}
|
||||
if (userProperty) {
|
||||
for (NSString *key in userProperty.allKeys) {
|
||||
[properties appendByte:MQTTUserProperty];
|
||||
[properties appendMQTTString:key];
|
||||
[properties appendMQTTString:userProperty[key]];
|
||||
}
|
||||
}
|
||||
if (maximumPacketSize) {
|
||||
[properties appendByte:MQTTMaximumPacketSize];
|
||||
[properties appendUInt32BigEndian:maximumPacketSize.unsignedIntValue];
|
||||
}
|
||||
[data appendVariableLength:properties.length];
|
||||
[data appendData:properties];
|
||||
}
|
||||
|
||||
[data appendMQTTString:clientId];
|
||||
if (willTopic) {
|
||||
[data appendMQTTString:willTopic];
|
||||
}
|
||||
if (willMsg) {
|
||||
[data appendUInt16BigEndian:willMsg.length];
|
||||
[data appendData:willMsg];
|
||||
}
|
||||
if (userName) {
|
||||
[data appendMQTTString:userName];
|
||||
}
|
||||
if (password) {
|
||||
[data appendMQTTString:password];
|
||||
}
|
||||
|
||||
MQTTMessage *msg = [[MQTTMessage alloc] initWithType:MQTTConnect
|
||||
data:data];
|
||||
return msg;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)pingreqMessage {
|
||||
return [[MQTTMessage alloc] initWithType:MQTTPingreq];
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)disconnectMessage:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
sessionExpiryInterval:(NSNumber *)sessionExpiryInterval
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary<NSString *,NSString *> *)userProperty {
|
||||
NSMutableData* data = [NSMutableData data];
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
NSMutableData *properties = [[NSMutableData alloc] init];
|
||||
if (sessionExpiryInterval) {
|
||||
[properties appendByte:MQTTSessionExpiryInterval];
|
||||
[properties appendUInt32BigEndian:sessionExpiryInterval.unsignedIntValue];
|
||||
}
|
||||
if (reasonString) {
|
||||
[properties appendByte:MQTTReasonString];
|
||||
[properties appendMQTTString:reasonString];
|
||||
}
|
||||
if (userProperty) {
|
||||
for (NSString *key in userProperty.allKeys) {
|
||||
[properties appendByte:MQTTUserProperty];
|
||||
[properties appendMQTTString:key];
|
||||
[properties appendMQTTString:userProperty[key]];
|
||||
}
|
||||
}
|
||||
if (returnCode != MQTTSuccess || properties.length > 0) {
|
||||
[data appendByte:returnCode];
|
||||
}
|
||||
if (properties.length > 0) {
|
||||
[data appendVariableLength:properties.length];
|
||||
[data appendData:properties];
|
||||
}
|
||||
}
|
||||
MQTTMessage *msg = [[MQTTMessage alloc] initWithType:MQTTDisconnect
|
||||
data:data];
|
||||
return msg;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)subscribeMessageWithMessageId:(UInt16)msgId
|
||||
topics:(NSDictionary *)topics
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
subscriptionIdentifier:(NSNumber *)subscriptionIdentifier {
|
||||
NSMutableData* data = [NSMutableData data];
|
||||
[data appendUInt16BigEndian:msgId];
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
NSMutableData *properties = [[NSMutableData alloc] init];
|
||||
if (subscriptionIdentifier) {
|
||||
[properties appendByte:MQTTSubscriptionIdentifier];
|
||||
[properties appendVariableLength:subscriptionIdentifier.unsignedLongValue];
|
||||
}
|
||||
[data appendVariableLength:properties.length];
|
||||
[data appendData:properties];
|
||||
}
|
||||
|
||||
for (NSString *topic in topics.allKeys) {
|
||||
[data appendMQTTString:topic];
|
||||
[data appendByte:[topics[topic] intValue]];
|
||||
}
|
||||
MQTTMessage* msg = [[MQTTMessage alloc] initWithType:MQTTSubscribe
|
||||
qos:1
|
||||
data:data];
|
||||
msg.mid = msgId;
|
||||
return msg;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)unsubscribeMessageWithMessageId:(UInt16)msgId
|
||||
topics:(NSArray *)topics
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel {
|
||||
NSMutableData* data = [NSMutableData data];
|
||||
[data appendUInt16BigEndian:msgId];
|
||||
for (NSString *topic in topics) {
|
||||
[data appendMQTTString:topic];
|
||||
}
|
||||
MQTTMessage* msg = [[MQTTMessage alloc] initWithType:MQTTUnsubscribe
|
||||
qos:1
|
||||
data:data];
|
||||
msg.mid = msgId;
|
||||
return msg;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)publishMessageWithData:(NSData *)payload
|
||||
onTopic:(NSString *)topic
|
||||
qos:(MQTTQosLevel)qosLevel
|
||||
msgId:(UInt16)msgId
|
||||
retainFlag:(BOOL)retain
|
||||
dupFlag:(BOOL)dup
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
payloadFormatIndicator:(NSNumber *)payloadFormatIndicator
|
||||
publicationExpiryInterval:(NSNumber *)publicationExpiryInterval
|
||||
topicAlias:(NSNumber *)topicAlias
|
||||
responseTopic:(NSString *)responseTopic
|
||||
correlationData:(NSData *)correlationData
|
||||
userProperty:(NSDictionary<NSString *,NSString *> *)userProperty
|
||||
contentType:(NSString *)contentType {
|
||||
NSMutableData *data = [[NSMutableData alloc] init];
|
||||
[data appendMQTTString:topic];
|
||||
if (msgId) [data appendUInt16BigEndian:msgId];
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
NSMutableData *properties = [[NSMutableData alloc] init];
|
||||
if (payloadFormatIndicator) {
|
||||
[properties appendByte:MQTTPayloadFormatIndicator];
|
||||
[properties appendByte:payloadFormatIndicator.unsignedIntValue];
|
||||
}
|
||||
if (publicationExpiryInterval) {
|
||||
[properties appendByte:MQTTPublicationExpiryInterval];
|
||||
[properties appendUInt32BigEndian:publicationExpiryInterval.unsignedIntValue];
|
||||
}
|
||||
if (topicAlias) {
|
||||
[properties appendByte:MQTTTopicAlias];
|
||||
[properties appendUInt16BigEndian:topicAlias.unsignedIntValue];
|
||||
}
|
||||
if (responseTopic) {
|
||||
[properties appendByte:MQTTResponseTopic];
|
||||
[properties appendMQTTString:responseTopic];
|
||||
}
|
||||
if (correlationData) {
|
||||
[properties appendByte:MQTTCorrelationData];
|
||||
[properties appendBinaryData:correlationData];
|
||||
}
|
||||
if (userProperty) {
|
||||
for (NSString *key in userProperty.allKeys) {
|
||||
[properties appendByte:MQTTUserProperty];
|
||||
[properties appendMQTTString:key];
|
||||
[properties appendMQTTString:userProperty[key]];
|
||||
}
|
||||
}
|
||||
if (contentType) {
|
||||
[properties appendByte:MQTTContentType];
|
||||
[properties appendMQTTString:contentType];
|
||||
}
|
||||
[data appendVariableLength:properties.length];
|
||||
[data appendData:properties];
|
||||
}
|
||||
[data appendData:payload];
|
||||
MQTTMessage *msg = [[MQTTMessage alloc] initWithType:MQTTPublish
|
||||
qos:qosLevel
|
||||
retainFlag:retain
|
||||
dupFlag:dup
|
||||
data:data];
|
||||
msg.mid = msgId;
|
||||
return msg;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)pubackMessageWithMessageId:(UInt16)msgId
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary<NSString *,NSString *> *)userProperty {
|
||||
NSMutableData* data = [NSMutableData data];
|
||||
[data appendUInt16BigEndian:msgId];
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
NSMutableData *properties = [[NSMutableData alloc] init];
|
||||
if (reasonString) {
|
||||
[properties appendByte:MQTTReasonString];
|
||||
[properties appendMQTTString:reasonString];
|
||||
}
|
||||
if (userProperty) {
|
||||
for (NSString *key in userProperty.allKeys) {
|
||||
[properties appendByte:MQTTUserProperty];
|
||||
[properties appendMQTTString:key];
|
||||
[properties appendMQTTString:userProperty[key]];
|
||||
}
|
||||
}
|
||||
[data appendByte:returnCode];
|
||||
[data appendVariableLength:properties.length];
|
||||
[data appendData:properties];
|
||||
}
|
||||
MQTTMessage *msg = [[MQTTMessage alloc] initWithType:MQTTPuback
|
||||
data:data];
|
||||
msg.mid = msgId;
|
||||
return msg;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)pubrecMessageWithMessageId:(UInt16)msgId
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary<NSString *,NSString *> *)userProperty {
|
||||
NSMutableData* data = [NSMutableData data];
|
||||
[data appendUInt16BigEndian:msgId];
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
NSMutableData *properties = [[NSMutableData alloc] init];
|
||||
if (reasonString) {
|
||||
[properties appendByte:MQTTReasonString];
|
||||
[properties appendMQTTString:reasonString];
|
||||
}
|
||||
if (userProperty) {
|
||||
for (NSString *key in userProperty.allKeys) {
|
||||
[properties appendByte:MQTTUserProperty];
|
||||
[properties appendMQTTString:key];
|
||||
[properties appendMQTTString:userProperty[key]];
|
||||
}
|
||||
}
|
||||
[data appendByte:returnCode];
|
||||
[data appendVariableLength:properties.length];
|
||||
[data appendData:properties];
|
||||
}
|
||||
MQTTMessage *msg = [[MQTTMessage alloc] initWithType:MQTTPubrec
|
||||
data:data];
|
||||
msg.mid = msgId;
|
||||
return msg;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)pubrelMessageWithMessageId:(UInt16)msgId
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary<NSString *,NSString *> *)userProperty {
|
||||
NSMutableData* data = [NSMutableData data];
|
||||
[data appendUInt16BigEndian:msgId];
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
NSMutableData *properties = [[NSMutableData alloc] init];
|
||||
if (reasonString) {
|
||||
[properties appendByte:MQTTReasonString];
|
||||
[properties appendMQTTString:reasonString];
|
||||
}
|
||||
if (userProperty) {
|
||||
for (NSString *key in userProperty.allKeys) {
|
||||
[properties appendByte:MQTTUserProperty];
|
||||
[properties appendMQTTString:key];
|
||||
[properties appendMQTTString:userProperty[key]];
|
||||
}
|
||||
}
|
||||
[data appendByte:returnCode];
|
||||
[data appendVariableLength:properties.length];
|
||||
[data appendData:properties];
|
||||
}
|
||||
MQTTMessage *msg = [[MQTTMessage alloc] initWithType:MQTTPubrel
|
||||
qos:1
|
||||
data:data];
|
||||
msg.mid = msgId;
|
||||
return msg;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)pubcompMessageWithMessageId:(UInt16)msgId
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
returnCode:(MQTTReturnCode)returnCode
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary<NSString *,NSString *> *)userProperty {
|
||||
NSMutableData* data = [NSMutableData data];
|
||||
[data appendUInt16BigEndian:msgId];
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
NSMutableData *properties = [[NSMutableData alloc] init];
|
||||
if (reasonString) {
|
||||
[properties appendByte:MQTTReasonString];
|
||||
[properties appendMQTTString:reasonString];
|
||||
}
|
||||
if (userProperty) {
|
||||
for (NSString *key in userProperty.allKeys) {
|
||||
[properties appendByte:MQTTUserProperty];
|
||||
[properties appendMQTTString:key];
|
||||
[properties appendMQTTString:userProperty[key]];
|
||||
}
|
||||
}
|
||||
[data appendByte:returnCode];
|
||||
[data appendVariableLength:properties.length];
|
||||
[data appendData:properties];
|
||||
}
|
||||
MQTTMessage *msg = [[MQTTMessage alloc] initWithType:MQTTPubcomp
|
||||
data:data];
|
||||
msg.mid = msgId;
|
||||
return msg;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.type = 0;
|
||||
self.qos = MQTTQosLevelAtMostOnce;
|
||||
self.retainFlag = false;
|
||||
self.mid = 0;
|
||||
self.data = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithType:(MQTTCommandType)type {
|
||||
self = [self init];
|
||||
self.type = type;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithType:(MQTTCommandType)type
|
||||
data:(NSData *)data {
|
||||
self = [self init];
|
||||
self.type = type;
|
||||
self.data = data;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithType:(MQTTCommandType)type
|
||||
qos:(MQTTQosLevel)qos
|
||||
data:(NSData *)data {
|
||||
self = [self init];
|
||||
self.type = type;
|
||||
self.qos = qos;
|
||||
self.data = data;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithType:(MQTTCommandType)type
|
||||
qos:(MQTTQosLevel)qos
|
||||
retainFlag:(BOOL)retainFlag
|
||||
dupFlag:(BOOL)dupFlag
|
||||
data:(NSData *)data {
|
||||
self = [self init];
|
||||
self.type = type;
|
||||
self.qos = qos;
|
||||
self.retainFlag = retainFlag;
|
||||
self.dupFlag = dupFlag;
|
||||
self.data = data;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)wireFormat {
|
||||
NSMutableData *buffer = [[NSMutableData alloc] init];
|
||||
|
||||
// encode fixed header
|
||||
UInt8 header;
|
||||
header = (self.type & 0x0f) << 4;
|
||||
if (self.dupFlag) {
|
||||
header |= 0x08;
|
||||
}
|
||||
header |= (self.qos & 0x03) << 1;
|
||||
if (self.retainFlag) {
|
||||
header |= 0x01;
|
||||
}
|
||||
[buffer appendBytes:&header length:1];
|
||||
[buffer appendVariableLength:self.data.length];
|
||||
|
||||
// encode message data
|
||||
if (self.data != nil) {
|
||||
[buffer appendData:self.data];
|
||||
}
|
||||
|
||||
DDLogVerbose(@"[MQTTMessage] wireFormat(%lu)=%@...",
|
||||
(unsigned long)buffer.length,
|
||||
[buffer subdataWithRange:NSMakeRange(0, MIN(256, buffer.length))]);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
+ (MQTTMessage *)messageFromData:(NSData *)data protocolLevel:(MQTTProtocolVersion)protocolLevel {
|
||||
MQTTMessage *message = nil;
|
||||
if (data.length >= 2) {
|
||||
UInt8 header;
|
||||
[data getBytes:&header length:sizeof(header)];
|
||||
UInt8 type = (header >> 4) & 0x0f;
|
||||
UInt8 dupFlag = (header >> 3) & 0x01;
|
||||
UInt8 qos = (header >> 1) & 0x03;
|
||||
UInt8 retainFlag = header & 0x01;
|
||||
UInt32 remainingLength = 0;
|
||||
UInt32 multiplier = 1;
|
||||
UInt8 offset = 1;
|
||||
UInt8 digit;
|
||||
do {
|
||||
if (data.length < offset) {
|
||||
DDLogWarn(@"[MQTTMessage] message data incomplete remaining length");
|
||||
offset = -1;
|
||||
break;
|
||||
}
|
||||
[data getBytes:&digit range:NSMakeRange(offset, 1)];
|
||||
offset++;
|
||||
remainingLength += (digit & 0x7f) * multiplier;
|
||||
multiplier *= 128;
|
||||
if (multiplier > 128*128*128) {
|
||||
DDLogWarn(@"[MQTTMessage] message data too long remaining length");
|
||||
multiplier = -1;
|
||||
break;
|
||||
}
|
||||
} while ((digit & 0x80) != 0);
|
||||
|
||||
if (type >= MQTTConnect &&
|
||||
type <= MQTTDisconnect) {
|
||||
if (offset > 0 &&
|
||||
multiplier > 0 &&
|
||||
data.length == remainingLength + offset) {
|
||||
if ((type == MQTTPublish && (qos >= MQTTQosLevelAtMostOnce && qos <= MQTTQosLevelExactlyOnce)) ||
|
||||
(type == MQTTConnect && qos == 0) ||
|
||||
(type == MQTTConnack && qos == 0) ||
|
||||
(type == MQTTPuback && qos == 0) ||
|
||||
(type == MQTTPubrec && qos == 0) ||
|
||||
(type == MQTTPubrel && qos == 1) ||
|
||||
(type == MQTTPubcomp && qos == 0) ||
|
||||
(type == MQTTSubscribe && qos == 1) ||
|
||||
(type == MQTTSuback && qos == 0) ||
|
||||
(type == MQTTUnsubscribe && qos == 1) ||
|
||||
(type == MQTTUnsuback && qos == 0) ||
|
||||
(type == MQTTPingreq && qos == 0) ||
|
||||
(type == MQTTPingresp && qos == 0) ||
|
||||
(type == MQTTDisconnect && qos == 0)) {
|
||||
message = [[MQTTMessage alloc] init];
|
||||
message.type = type;
|
||||
message.dupFlag = dupFlag == 1;
|
||||
message.retainFlag = retainFlag == 1;
|
||||
message.qos = qos;
|
||||
message.data = [data subdataWithRange:NSMakeRange(offset, remainingLength)];
|
||||
if ((type == MQTTPublish &&
|
||||
(qos == MQTTQosLevelAtLeastOnce ||
|
||||
qos == MQTTQosLevelExactlyOnce)
|
||||
) ||
|
||||
type == MQTTPuback ||
|
||||
type == MQTTPubrec ||
|
||||
type == MQTTPubrel ||
|
||||
type == MQTTPubcomp ||
|
||||
type == MQTTSubscribe ||
|
||||
type == MQTTSuback ||
|
||||
type == MQTTUnsubscribe ||
|
||||
type == MQTTUnsuback) {
|
||||
if (message.data.length >= 2) {
|
||||
[message.data getBytes:&digit range:NSMakeRange(0, 1)];
|
||||
message.mid = digit * 256;
|
||||
[message.data getBytes:&digit range:NSMakeRange(1, 1)];
|
||||
message.mid += digit;
|
||||
} else {
|
||||
DDLogWarn(@"[MQTTMessage] missing packet identifier");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
if (type == MQTTPuback ||
|
||||
type == MQTTPubrec ||
|
||||
type == MQTTPubrel ||
|
||||
type == MQTTPubcomp) {
|
||||
if (protocolLevel != MQTTProtocolVersion50) {
|
||||
if (message.data.length > 2) {
|
||||
DDLogWarn(@"[MQTTMessage] unexpected payload after packet identifier");
|
||||
message = nil;
|
||||
}
|
||||
} else {
|
||||
if (message.data.length < 3) {
|
||||
DDLogWarn(@"[MQTTMessage] no returncode");
|
||||
message = nil;
|
||||
} else {
|
||||
const UInt8 *bytes = message.data.bytes;
|
||||
message.returnCode = [NSNumber numberWithInt:bytes[2]];
|
||||
if (message.data.length >= 3) {
|
||||
message.properties = [[MQTTProperties alloc] initFromData:
|
||||
[message.data subdataWithRange:NSMakeRange(3, message.data.length - 3)]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (type == MQTTUnsuback ) {
|
||||
if (message.data.length > 2) {
|
||||
DDLogWarn(@"[MQTTMessage] unexpected payload after packet identifier");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
if (type == MQTTPingreq ||
|
||||
type == MQTTPingresp) {
|
||||
if (message.data.length > 0) {
|
||||
DDLogWarn(@"[MQTTMessage] unexpected payload");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
if (type == MQTTDisconnect) {
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
if (message.data.length == 0) {
|
||||
message.properties = nil;
|
||||
message.returnCode = @(MQTTSuccess);
|
||||
} else if (message.data.length == 1) {
|
||||
message.properties = nil;
|
||||
const UInt8 *bytes = message.data.bytes;
|
||||
message.returnCode = [NSNumber numberWithUnsignedInt:bytes[0]];
|
||||
} else if (message.data.length > 1) {
|
||||
const UInt8 *bytes = message.data.bytes;
|
||||
message.returnCode = [NSNumber numberWithUnsignedInt:bytes[0]];
|
||||
message.properties = [[MQTTProperties alloc] initFromData:
|
||||
[message.data subdataWithRange:NSMakeRange(1, message.data.length - 1)]];
|
||||
}
|
||||
} else {
|
||||
if (message.data.length != 2) {
|
||||
DDLogWarn(@"[MQTTMessage] unexpected payload");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == MQTTConnect) {
|
||||
if (message.data.length < 3) {
|
||||
DDLogWarn(@"[MQTTMessage] missing connect variable header");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
if (type == MQTTConnack) {
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
if (message.data.length < 3) {
|
||||
DDLogWarn(@"[MQTTMessage] missing connack variable header");
|
||||
message = nil;
|
||||
}
|
||||
} else {
|
||||
if (message.data.length != 2) {
|
||||
DDLogWarn(@"[MQTTMessage] missing connack variable header");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
if (message) {
|
||||
const UInt8 *bytes = message.data.bytes;
|
||||
message.connectAcknowledgeFlags = [NSNumber numberWithUnsignedInt:bytes[0]];
|
||||
message.returnCode = [NSNumber numberWithUnsignedInt:bytes[1]];
|
||||
if (protocolLevel == MQTTProtocolVersion50) {
|
||||
message.properties = [[MQTTProperties alloc] initFromData:
|
||||
[message.data subdataWithRange:NSMakeRange(2, message.data.length - 2)]];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == MQTTSubscribe) {
|
||||
if (message.data.length < 3) {
|
||||
DDLogWarn(@"[MQTTMessage] missing subscribe variable header");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
if (type == MQTTSuback) {
|
||||
if (message.data.length < 3) {
|
||||
DDLogWarn(@"[MQTTMessage] missing suback variable header");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
if (type == MQTTUnsubscribe) {
|
||||
if (message.data.length < 3) {
|
||||
DDLogWarn(@"[MQTTMessage] missing unsubscribe variable header");
|
||||
message = nil;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DDLogWarn(@"[MQTTMessage] illegal header flags");
|
||||
}
|
||||
} else {
|
||||
DDLogWarn(@"[MQTTMessage] remaining data wrong length");
|
||||
}
|
||||
} else {
|
||||
DDLogWarn(@"[MQTTMessage] illegal message type");
|
||||
}
|
||||
} else {
|
||||
DDLogWarn(@"[MQTTMessage] message data length < 2");
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSMutableData (MQTT)
|
||||
|
||||
- (void)appendByte:(UInt8)byte {
|
||||
[self appendBytes:&byte length:1];
|
||||
}
|
||||
|
||||
- (void)appendUInt16BigEndian:(UInt16)val {
|
||||
[self appendByte:val / 256];
|
||||
[self appendByte:val % 256];
|
||||
}
|
||||
|
||||
- (void)appendUInt32BigEndian:(UInt32)val {
|
||||
[self appendByte:(val / (256 * 256 * 256))];
|
||||
[self appendByte:(val / (256 * 256)) & 0xff];
|
||||
[self appendByte:(val / 256) & 0xff];
|
||||
[self appendByte:val % 256];
|
||||
}
|
||||
|
||||
- (void)appendVariableLength:(unsigned long)length {
|
||||
do {
|
||||
UInt8 digit = length % 128;
|
||||
length /= 128;
|
||||
if (length > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
[self appendBytes:&digit length:1];
|
||||
}
|
||||
while (length > 0);
|
||||
}
|
||||
|
||||
- (void)appendMQTTString:(NSString *)string {
|
||||
if (string) {
|
||||
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[self appendUInt16BigEndian:data.length];
|
||||
[self appendData:data];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appendBinaryData:(NSData *)data {
|
||||
if (data) {
|
||||
[self appendUInt16BigEndian:data.length];
|
||||
[self appendData:data];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
124
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTPersistence.h
generated
Normal file
124
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTPersistence.h
generated
Normal file
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// MQTTPersistence.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 22.03.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MQTTMessage.h"
|
||||
|
||||
static BOOL const MQTT_PERSISTENT = NO;
|
||||
static NSInteger const MQTT_MAX_SIZE = 64 * 1024 * 1024;
|
||||
static NSInteger const MQTT_MAX_WINDOW_SIZE = 16;
|
||||
static NSInteger const MQTT_MAX_MESSAGES = 1024;
|
||||
|
||||
/** MQTTFlow is an abstraction of the entity to be stored for persistence */
|
||||
|
||||
@protocol MQTTFlow
|
||||
/** The clientID of the flow element */
|
||||
@property (strong, nonatomic) NSString *clientId;
|
||||
|
||||
/** The flag indicating incoming or outgoing flow element */
|
||||
@property (strong, nonatomic) NSNumber *incomingFlag;
|
||||
|
||||
/** The flag indicating if the flow element is retained*/
|
||||
@property (strong, nonatomic) NSNumber *retainedFlag;
|
||||
|
||||
/** The MQTTCommandType of the flow element, might be MQTT_None for offline queueing */
|
||||
@property (strong, nonatomic) NSNumber *commandType;
|
||||
|
||||
/** The MQTTQosLevel of the flow element */
|
||||
@property (strong, nonatomic) NSNumber *qosLevel;
|
||||
|
||||
/** The messageId of the flow element */
|
||||
@property (strong, nonatomic) NSNumber *messageId;
|
||||
|
||||
/** The topic of the flow element */
|
||||
@property (strong, nonatomic) NSString *topic;
|
||||
|
||||
/** The data of the flow element */
|
||||
@property (strong, nonatomic) NSData *data;
|
||||
|
||||
/** The deadline of the flow elelment before (re)trying transmission */
|
||||
@property (strong, nonatomic) NSDate *deadline;
|
||||
|
||||
@end
|
||||
|
||||
/** The MQTTPersistence protocol is an abstraction of persistence classes for MQTTSession */
|
||||
|
||||
@protocol MQTTPersistence
|
||||
|
||||
/** The maximum Window Size for outgoing inflight messages per clientID. Defaults to 16 */
|
||||
@property (nonatomic) NSUInteger maxWindowSize;
|
||||
|
||||
/** The maximum number of messages kept per clientID and direction. Defaults to 1024 */
|
||||
@property (nonatomic) NSUInteger maxMessages;
|
||||
|
||||
/** Indicates if the persistence implementation should make the information permannent. Defaults to NO */
|
||||
@property (nonatomic) BOOL persistent;
|
||||
|
||||
/** The maximum size of the storage used for persistence in total in bytes. Defaults to 1024*1024 bytes */
|
||||
@property (nonatomic) NSUInteger maxSize;
|
||||
|
||||
/** The current Window Size for outgoing inflight messages per clientID.
|
||||
* @param clientId identifying the session
|
||||
* @return the current size of the outgoing inflight window
|
||||
*/
|
||||
- (NSUInteger)windowSize:(NSString *)clientId;
|
||||
|
||||
/** Stores one new message
|
||||
* @param clientId identifying the session
|
||||
* @param topic the topic of the message
|
||||
* @param data the message's data
|
||||
* @param retainFlag the retain flag of the message
|
||||
* @param qos the quality of service of the message
|
||||
* @param msgId the id of the message or zero for qos zero
|
||||
* @param incomingFlag the direction of the message
|
||||
* @param commandType the command of the message
|
||||
* @param deadline the deadline of the message for repetitions
|
||||
* @return the created MQTTFlow element or nil if the maxWindowSize has been exceeded
|
||||
*/
|
||||
- (id<MQTTFlow>)storeMessageForClientId:(NSString *)clientId
|
||||
topic:(NSString *)topic
|
||||
data:(NSData *)data
|
||||
retainFlag:(BOOL)retainFlag
|
||||
qos:(MQTTQosLevel)qos
|
||||
msgId:(UInt16)msgId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
commandType:(UInt8)commandType
|
||||
deadline:(NSDate *)deadline;
|
||||
|
||||
/** Deletes an MQTTFlow element
|
||||
* @param flow the MQTTFlow to delete
|
||||
*/
|
||||
- (void)deleteFlow:(id<MQTTFlow>)flow;
|
||||
|
||||
/** Deletes all MQTTFlow elements of a clientId
|
||||
* @param clientId the client Id identifying all MQTTFlows to be deleted
|
||||
*/
|
||||
- (void)deleteAllFlowsForClientId:(NSString *)clientId;
|
||||
|
||||
/** Retrieves all MQTTFlow elements of a clientId and direction
|
||||
* @param clientId whos MQTTFlows should be retrieved
|
||||
* @param incomingFlag specifies the wether incoming or outgoing flows should be retrieved
|
||||
* @return an NSArray of the retrieved MQTTFlow elements
|
||||
*/
|
||||
- (NSArray *)allFlowsforClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag;
|
||||
|
||||
/** Retrieves an MQTTFlow element
|
||||
* @param clientId to which the MQTTFlow belongs to
|
||||
* @param incomingFlag specifies the direction of the flow
|
||||
* @param messageId specifies the message Id of the flow
|
||||
* @return the retrieved MQTTFlow element or nil if the elememt was not found
|
||||
*/
|
||||
- (id<MQTTFlow>)flowforClientId:(NSString *)clientId
|
||||
incomingFlag:(BOOL)incomingFlag
|
||||
messageId:(UInt16)messageId;
|
||||
|
||||
/** sync is called to allow the MQTTPersistence implemetation to save data permanently */
|
||||
- (void)sync;
|
||||
|
||||
@end
|
||||
76
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTProperties.h
generated
Normal file
76
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTProperties.h
generated
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// MQTTProperties.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 04.04.17.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(UInt8, MQTTPropertyIdentifier) {
|
||||
MQTTPayloadFormatIndicator = 1,
|
||||
MQTTPublicationExpiryInterval = 2,
|
||||
MQTTContentType = 3,
|
||||
MQTTResponseTopic = 8,
|
||||
MQTTCorrelationData = 9,
|
||||
MQTTSubscriptionIdentifier = 11,
|
||||
MQTTSessionExpiryInterval = 17,
|
||||
MQTTAssignedClientIdentifier = 18,
|
||||
MQTTServerKeepAlive = 19,
|
||||
MQTTAuthMethod = 21,
|
||||
MQTTAuthData = 22,
|
||||
MQTTRequestProblemInformation = 23,
|
||||
MQTTWillDelayInterval = 24,
|
||||
MQTTRequestResponseInformation = 25,
|
||||
MQTTResponseInformation = 26,
|
||||
MQTTServerReference = 28,
|
||||
MQTTReasonString = 31,
|
||||
MQTTReceiveMaximum = 33,
|
||||
MQTTTopicAliasMaximum = 34,
|
||||
MQTTTopicAlias = 35,
|
||||
MQTTMaximumQoS = 36,
|
||||
MQTTRetainAvailable = 37,
|
||||
MQTTUserProperty = 38,
|
||||
MQTTMaximumPacketSize = 39,
|
||||
MQTTWildcardSubscriptionAvailable = 40,
|
||||
MQTTSubscriptionIdentifiersAvailable = 41,
|
||||
MQTTSharedSubscriptionAvailable = 42
|
||||
};
|
||||
|
||||
|
||||
@interface MQTTProperties : NSObject
|
||||
|
||||
@property (strong, nonatomic) NSNumber *payloadFormatIndicator;
|
||||
@property (strong, nonatomic) NSNumber *publicationExpiryInterval;
|
||||
@property (strong, nonatomic) NSString *contentType;
|
||||
@property (strong, nonatomic) NSString *responseTopic;
|
||||
@property (strong, nonatomic) NSData *correlationData;
|
||||
@property (strong, nonatomic) NSNumber *subscriptionIdentifier;
|
||||
@property (strong, nonatomic) NSNumber *sessionExpiryInterval;
|
||||
@property (strong, nonatomic) NSString *assignedClientIdentifier;
|
||||
@property (strong, nonatomic) NSNumber *serverKeepAlive;
|
||||
@property (strong, nonatomic) NSString *authMethod;
|
||||
@property (strong, nonatomic) NSData *authData;
|
||||
@property (strong, nonatomic) NSNumber *requestProblemInformation;
|
||||
@property (strong, nonatomic) NSNumber *willDelayInterval;
|
||||
@property (strong, nonatomic) NSNumber *requestResponseInformation;
|
||||
@property (strong, nonatomic) NSString *responseInformation;
|
||||
@property (strong, nonatomic) NSString *serverReference;
|
||||
@property (strong, nonatomic) NSString *reasonString;
|
||||
@property (strong, nonatomic) NSNumber *receiveMaximum;
|
||||
@property (strong, nonatomic) NSNumber *topicAliasMaximum;
|
||||
@property (strong, nonatomic) NSNumber *topicAlias;
|
||||
@property (strong, nonatomic) NSNumber *maximumQoS;
|
||||
@property (strong, nonatomic) NSNumber *retainAvailable;
|
||||
@property (strong, nonatomic) NSMutableDictionary <NSString *, NSString *> *userProperty;
|
||||
@property (strong, nonatomic) NSNumber *maximumPacketSize;
|
||||
@property (strong, nonatomic) NSNumber *wildcardSubscriptionAvailable;
|
||||
@property (strong, nonatomic) NSNumber *subscriptionIdentifiersAvailable;
|
||||
@property (strong, nonatomic) NSNumber *sharedSubscriptionAvailable;
|
||||
|
||||
- (instancetype)initFromData:(NSData *)data NS_DESIGNATED_INITIALIZER;
|
||||
+ (int)getVariableLength:(NSData *)data;
|
||||
+ (int)variableIntLength:(int)length;
|
||||
|
||||
@end
|
||||
336
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTProperties.m
generated
Normal file
336
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTProperties.m
generated
Normal file
@@ -0,0 +1,336 @@
|
||||
//
|
||||
// MQTTProperties.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 04.04.17.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTProperties.h"
|
||||
|
||||
@implementation MQTTProperties
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initFromData:[[NSData alloc] init]];
|
||||
}
|
||||
- (instancetype)initFromData:(NSData *)data {
|
||||
self = [super init];
|
||||
|
||||
int propertyLength = [MQTTProperties getVariableLength:data];
|
||||
int offset = [MQTTProperties variableIntLength:propertyLength];
|
||||
NSData *remainingData = [data subdataWithRange:NSMakeRange(offset, data.length - offset)];
|
||||
offset = 0;
|
||||
if (remainingData.length >= propertyLength) {
|
||||
while (propertyLength - offset > 0) {
|
||||
const UInt8 *bytes = remainingData.bytes;
|
||||
UInt8 propertyType = bytes[offset];
|
||||
switch (propertyType) {
|
||||
case MQTTPayloadFormatIndicator:
|
||||
if (propertyLength - offset > 1) {
|
||||
self.payloadFormatIndicator = [NSNumber numberWithInt:bytes[offset + 1]];
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
case MQTTPublicationExpiryInterval:
|
||||
if (propertyLength - offset > 4) {
|
||||
self.publicationExpiryInterval = @([MQTTProperties getFourByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
|
||||
offset += 5;
|
||||
}
|
||||
break;
|
||||
case MQTTContentType:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.contentType = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTResponseTopic:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.responseTopic = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTCorrelationData:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.correlationData = [MQTTProperties getBinaryData:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTSubscriptionIdentifier:
|
||||
if (propertyLength - offset > 1) {
|
||||
int subscriptionIdentifier = [MQTTProperties getVariableLength:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
int l = [MQTTProperties variableIntLength:subscriptionIdentifier];
|
||||
self.subscriptionIdentifier = @(subscriptionIdentifier);
|
||||
offset += 1 + l;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTSessionExpiryInterval:
|
||||
if (propertyLength - offset > 4) {
|
||||
self.sessionExpiryInterval = @([MQTTProperties getFourByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
|
||||
offset += 5;
|
||||
}
|
||||
break;
|
||||
case MQTTAssignedClientIdentifier:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.assignedClientIdentifier = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case MQTTServerKeepAlive:
|
||||
if (propertyLength - offset > 2) {
|
||||
self.serverKeepAlive = @([MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
|
||||
offset += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTAuthMethod:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.authMethod = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case MQTTAuthData:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.authData = [MQTTProperties getBinaryData:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case MQTTRequestProblemInformation:
|
||||
if (propertyLength - offset > 1) {
|
||||
self.requestProblemInformation = [NSNumber numberWithInt:bytes[offset + 1]];
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTWillDelayInterval:
|
||||
if (propertyLength - offset > 4) {
|
||||
self.willDelayInterval = @([MQTTProperties getFourByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
|
||||
offset += 5;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTRequestResponseInformation:
|
||||
if (propertyLength - offset > 1) {
|
||||
self.requestResponseInformation = [NSNumber numberWithInt:bytes[offset + 1]];
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTResponseInformation:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.responseInformation = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case MQTTServerReference:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.serverReference = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case MQTTReasonString:
|
||||
if (propertyLength - offset > 2) {
|
||||
int l = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
self.reasonString = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
offset += 1 + 2 + l;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case MQTTReceiveMaximum:
|
||||
if (propertyLength - offset > 2) {
|
||||
self.receiveMaximum = @([MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
|
||||
offset += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTTopicAliasMaximum:
|
||||
if (propertyLength - offset > 2) {
|
||||
self.topicAliasMaximum = @([MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
|
||||
offset += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTTopicAlias:
|
||||
if (propertyLength - offset > 2) {
|
||||
self.topicAlias = @([MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
|
||||
offset += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTMaximumQoS:
|
||||
if (propertyLength - offset > 1) {
|
||||
self.maximumQoS = [NSNumber numberWithInt:bytes[offset + 1]];
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTRetainAvailable:
|
||||
if (propertyLength - offset > 1) {
|
||||
self.retainAvailable = [NSNumber numberWithInt:bytes[offset + 1]];
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTUserProperty:
|
||||
if (propertyLength - offset > 4) {
|
||||
int keyL = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
NSString *key = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
|
||||
|
||||
int valueL = [MQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1 + 2 + keyL, remainingData.length - (offset + 1))]];
|
||||
|
||||
NSString *value = [MQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1 + 2 + keyL, remainingData.length - (offset + 1))]];
|
||||
|
||||
if (!self.userProperty) {
|
||||
self.userProperty = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
self.userProperty[key] = value;
|
||||
offset += 1 + 2 + keyL + 2 + valueL;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTMaximumPacketSize:
|
||||
if (propertyLength - offset > 4) {
|
||||
self.maximumPacketSize = @([MQTTProperties getFourByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
|
||||
offset += 5;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTWildcardSubscriptionAvailable:
|
||||
if (propertyLength - offset > 1) {
|
||||
self.wildcardSubscriptionAvailable = [NSNumber numberWithInt:bytes[offset + 1]];
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTSubscriptionIdentifiersAvailable:
|
||||
if (propertyLength - offset > 1) {
|
||||
self.subscriptionIdentifiersAvailable = [NSNumber numberWithInt:bytes[offset + 1]];
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTTSharedSubscriptionAvailable:
|
||||
if (propertyLength - offset > 1) {
|
||||
self.sharedSubscriptionAvailable = [NSNumber numberWithInt:bytes[offset + 1]];
|
||||
offset += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return self;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (int)getVariableLength:(NSData *)data {
|
||||
int length = 0;
|
||||
int offset = 0;
|
||||
int multiplier = 1;
|
||||
UInt8 digit;
|
||||
|
||||
do {
|
||||
if (data.length < offset) {
|
||||
return -1;
|
||||
}
|
||||
[data getBytes:&digit range:NSMakeRange(offset, 1)];
|
||||
offset++;
|
||||
length += (digit & 0x7f) * multiplier;
|
||||
multiplier *= 128;
|
||||
if (multiplier > 128 * 128 * 128) {
|
||||
return -2;
|
||||
}
|
||||
} while ((digit & 0x80) != 0);
|
||||
return length;
|
||||
}
|
||||
|
||||
+ (int)getTwoByteInt:(NSData *)data {
|
||||
int i = 0;
|
||||
if (data.length >= 2) {
|
||||
const UInt8 *bytes = data.bytes;
|
||||
i = bytes[0] * 256 +
|
||||
bytes[1];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
+ (int)getFourByteInt:(NSData *)data {
|
||||
int i = 0;
|
||||
if (data.length >= 4) {
|
||||
const UInt8 *bytes = data.bytes;
|
||||
i = bytes[0] * 256 * 256 * 256 +
|
||||
bytes[1] * 256 * 256 +
|
||||
bytes[2] * 256 +
|
||||
bytes[3];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
+ (NSString *)getUtf8String:(NSData *)data {
|
||||
NSString *s;
|
||||
int l = [MQTTProperties getTwoByteInt:data];
|
||||
if (data.length >= l + 2) {
|
||||
s = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, l)] encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
+ (NSData *)getBinaryData:(NSData *)data {
|
||||
NSData *d;
|
||||
int l = [MQTTProperties getTwoByteInt:data];
|
||||
if (data.length >= l + 2) {
|
||||
d = [data subdataWithRange:NSMakeRange(2, l)];
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
+ (int)variableIntLength:(int)length {
|
||||
int l = 0;
|
||||
if (length <= 127) {
|
||||
l = 1;
|
||||
} else if (length <= 16383) {
|
||||
l = 2;
|
||||
} else if (length <= 2097151) {
|
||||
l = 3;
|
||||
} else if (length <= 268435455) {
|
||||
l = 4;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
158
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicy.h
generated
Normal file
158
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicy.h
generated
Normal file
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// MQTTSSLSecurityPolicy.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Created by @bobwenx on 15/6/1.
|
||||
//
|
||||
// based on
|
||||
//
|
||||
// Copyright (c) 2011–2015 AFNetwork (http://alamofire.org/)
|
||||
//
|
||||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
/**
|
||||
## SSL Pinning Modes
|
||||
|
||||
The following constants are provided by `MQTTSSLPinningModeNone` as possible SSL pinning modes.
|
||||
|
||||
enum {
|
||||
MQTTSSLPinningModeNone,
|
||||
MQTTSSLPinningModePublicKey,
|
||||
MQTTSSLPinningModeCertificate,
|
||||
}
|
||||
|
||||
`MQTTSSLPinningModeNone`
|
||||
Do not used pinned certificates to validate servers.
|
||||
|
||||
`MQTTSSLPinningModePublicKey`
|
||||
Validate host certificates against public keys of pinned certificates.
|
||||
|
||||
`MQTTSSLPinningModeCertificate`
|
||||
Validate host certificates against pinned certificates.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, MQTTSSLPinningMode) {
|
||||
// Do not used pinned certificates to validate servers.
|
||||
MQTTSSLPinningModeNone,
|
||||
// Validate host certificates against public keys of pinned certificates.
|
||||
MQTTSSLPinningModePublicKey,
|
||||
// Validate host certificates against pinned certificates.
|
||||
MQTTSSLPinningModeCertificate,
|
||||
};
|
||||
|
||||
/**
|
||||
`MQTTSSLSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections.
|
||||
|
||||
If your app using security model which require pinning SSL certificates to helps prevent man-in-the-middle attacks
|
||||
and other vulnerabilities. you need to set securityPolicy to properly value(see MQTTSSLSecurityPolicy.h for more detail).
|
||||
|
||||
NOTE: about self-signed server certificates:
|
||||
if your server using Self-signed certificates to establish SSL/TLS connection, you need to set property:
|
||||
MQTTSSLSecurityPolicy.allowInvalidCertificates=YES.
|
||||
|
||||
If SSL is enabled, by default it only evaluate server's certificates using CA infrastructure, and for most case, this type of check is enough.
|
||||
However, if your app using security model which require pinning SSL certificates to helps prevent man-in-the-middle attacks
|
||||
and other vulnerabilities. you may need to set securityPolicy to properly value(see MQTTSSLSecurityPolicy.h for more detail).
|
||||
|
||||
NOTE: about self-signed server certificates:
|
||||
In CA infrastructure, you may establish a SSL/TLS connection with server which using self-signed certificates
|
||||
by install the certificates into OS keychain(either programmatically or manually). however, this method has some disadvantages:
|
||||
1. every socket you app created will trust certificates you added.
|
||||
2. if user choice to remove certificates from keychain, you app need to handling certificates re-adding.
|
||||
|
||||
If you only want to verify the cert for the socket you are creating and for no other sockets in your app, you need to use
|
||||
MQTTSSLSecurityPolicy.
|
||||
And if you use self-signed server certificates, your need to set property: MQTTSSLSecurityPolicy.allowInvalidCertificates=YES
|
||||
|
||||
Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities.
|
||||
Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication
|
||||
over an SSL/TLS connection with SSL pinning configured and enabled.
|
||||
*/
|
||||
@interface MQTTSSLSecurityPolicy : NSObject
|
||||
/**
|
||||
The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `MQTTSSLPinningMode`.
|
||||
*/
|
||||
@property (readonly, nonatomic, assign) MQTTSSLPinningMode SSLPinningMode;
|
||||
|
||||
/**
|
||||
Whether to evaluate an entire SSL certificate chain, or just the leaf certificate. Defaults to `YES`.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL validatesCertificateChain;
|
||||
|
||||
/**
|
||||
The certificates used to evaluate server trust according to the SSL pinning mode. By default, this property is set to any (`.cer`) certificates included in the app bundle.
|
||||
Note: Array item type: NSData - Bytes of X.509 certificate file in der format.
|
||||
Note that if you create an array with duplicate certificates, the duplicate certificates will be removed.
|
||||
*/
|
||||
@property (nonatomic, strong) NSArray *pinnedCertificates;
|
||||
|
||||
/**
|
||||
Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`.
|
||||
Note: If your server-certificates are self signed, your should set this property to 'YES'.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL allowInvalidCertificates;
|
||||
|
||||
/**
|
||||
Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL validatesDomainName;
|
||||
|
||||
///-----------------------------------------
|
||||
/// @name Getting Specific Security Policies
|
||||
///-----------------------------------------
|
||||
|
||||
/**
|
||||
Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys.
|
||||
|
||||
@return The default security policy.
|
||||
*/
|
||||
+ (instancetype)defaultPolicy;
|
||||
|
||||
///---------------------
|
||||
/// @name Initialization
|
||||
///---------------------
|
||||
|
||||
/**
|
||||
Creates and returns a security policy with the specified pinning mode.
|
||||
|
||||
@param pinningMode The SSL pinning mode.
|
||||
|
||||
@return A new security policy.
|
||||
*/
|
||||
+ (instancetype)policyWithPinningMode:(MQTTSSLPinningMode)pinningMode;
|
||||
|
||||
///------------------------------
|
||||
/// @name Evaluating Server Trust
|
||||
///------------------------------
|
||||
|
||||
/**
|
||||
Whether or not the specified server trust should be accepted, based on the security policy.
|
||||
|
||||
This method should be used when responding to an authentication challenge from a server.
|
||||
|
||||
@param serverTrust The X.509 certificate trust of the server.
|
||||
@param domain The domain of serverTrust. If `nil`, the domain will not be validated.
|
||||
|
||||
@return Whether or not to trust the server.
|
||||
*/
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
|
||||
forDomain:(NSString *)domain;
|
||||
@end
|
||||
297
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicy.m
generated
Normal file
297
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicy.m
generated
Normal file
@@ -0,0 +1,297 @@
|
||||
//
|
||||
// MQTTSSLSecurityPolicy.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Created by @bobwenx on 15/6/1.
|
||||
//
|
||||
// based on
|
||||
//
|
||||
// Copyright (c) 2011–2015 AFNetwork (http://alamofire.org/)
|
||||
//
|
||||
// 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.
|
||||
|
||||
#import "MQTTSSLSecurityPolicy.h"
|
||||
#import <AssertMacros.h>
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
static BOOL SSLSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
|
||||
return [(__bridge id) key1 isEqual:(__bridge id) key2];
|
||||
}
|
||||
|
||||
static id SSLPublicKeyForCertificate(NSData *certificate) {
|
||||
id allowedPublicKey = nil;
|
||||
SecCertificateRef allowedCertificate;
|
||||
SecCertificateRef allowedCertificates[1];
|
||||
CFArrayRef tempCertificates = nil;
|
||||
SecPolicyRef policy = nil;
|
||||
SecTrustRef allowedTrust = nil;
|
||||
SecTrustResultType result;
|
||||
|
||||
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
|
||||
__Require_Quiet(allowedCertificate != NULL, _out);
|
||||
|
||||
allowedCertificates[0] = allowedCertificate;
|
||||
tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
|
||||
|
||||
policy = SecPolicyCreateBasicX509();
|
||||
__Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
|
||||
|
||||
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
|
||||
|
||||
_out:
|
||||
if (allowedTrust) {
|
||||
CFRelease(allowedTrust);
|
||||
}
|
||||
|
||||
if (policy) {
|
||||
CFRelease(policy);
|
||||
}
|
||||
|
||||
if (tempCertificates) {
|
||||
CFRelease(tempCertificates);
|
||||
}
|
||||
|
||||
if (allowedCertificate) {
|
||||
CFRelease(allowedCertificate);
|
||||
}
|
||||
|
||||
return allowedPublicKey;
|
||||
}
|
||||
|
||||
static BOOL SSLServerTrustIsValid(SecTrustRef serverTrust) {
|
||||
BOOL isValid = NO;
|
||||
SecTrustResultType result;
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
|
||||
|
||||
isValid = (result == kSecTrustResultUnspecified // The OS trusts this certificate implicitly.
|
||||
|| result == kSecTrustResultProceed); // The user explicitly told the OS to trust it.
|
||||
|
||||
// else? It's somebody else's key. Fall immediately.
|
||||
|
||||
_out:
|
||||
return isValid;
|
||||
}
|
||||
|
||||
static NSArray * SSLCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
|
||||
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
|
||||
|
||||
for (CFIndex i = 0; i < certificateCount; i++) {
|
||||
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
|
||||
[trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
|
||||
}
|
||||
|
||||
return [NSArray arrayWithArray:trustChain];
|
||||
}
|
||||
|
||||
static NSArray * SSLPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
SecPolicyRef policy = SecPolicyCreateBasicX509();
|
||||
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
|
||||
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
|
||||
for (CFIndex i = 0; i < certificateCount; i++) {
|
||||
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
|
||||
|
||||
SecCertificateRef someCertificates[] = {certificate};
|
||||
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
|
||||
|
||||
SecTrustRef trust;
|
||||
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
|
||||
|
||||
SecTrustResultType result;
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
|
||||
|
||||
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
|
||||
|
||||
_out:
|
||||
if (trust) {
|
||||
CFRelease(trust);
|
||||
}
|
||||
|
||||
if (certificates) {
|
||||
CFRelease(certificates);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
CFRelease(policy);
|
||||
|
||||
return [NSArray arrayWithArray:trustChain];
|
||||
}
|
||||
|
||||
@interface MQTTSSLSecurityPolicy()
|
||||
@property (readwrite, nonatomic, assign) MQTTSSLPinningMode SSLPinningMode;
|
||||
@property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys;
|
||||
@end
|
||||
|
||||
@implementation MQTTSSLSecurityPolicy
|
||||
|
||||
#pragma mark - SSL Security Policy
|
||||
|
||||
+ (NSArray *)defaultPinnedCertificates {
|
||||
static NSArray *_defaultPinnedCertificates = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
|
||||
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
|
||||
|
||||
NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:paths.count];
|
||||
for (NSString *path in paths) {
|
||||
NSData *certificateData = [NSData dataWithContentsOfFile:path];
|
||||
[certificates addObject:certificateData];
|
||||
}
|
||||
|
||||
_defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates];
|
||||
});
|
||||
|
||||
return _defaultPinnedCertificates;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultPolicy {
|
||||
MQTTSSLSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
securityPolicy.SSLPinningMode = MQTTSSLPinningModeNone;
|
||||
|
||||
return securityPolicy;
|
||||
}
|
||||
|
||||
+ (instancetype)policyWithPinningMode:(MQTTSSLPinningMode)pinningMode {
|
||||
MQTTSSLSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
securityPolicy.SSLPinningMode = pinningMode;
|
||||
|
||||
securityPolicy.pinnedCertificates = [self defaultPinnedCertificates];
|
||||
|
||||
return securityPolicy;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.validatesCertificateChain = YES;
|
||||
self.validatesDomainName = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setPinnedCertificates:(NSArray *)pinnedCertificates {
|
||||
_pinnedCertificates = [NSOrderedSet orderedSetWithArray:pinnedCertificates].array;
|
||||
|
||||
if (self.pinnedCertificates) {
|
||||
NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:(self.pinnedCertificates).count];
|
||||
for (NSData *certificate in self.pinnedCertificates) {
|
||||
id publicKey = SSLPublicKeyForCertificate(certificate);
|
||||
if (!publicKey) {
|
||||
continue;
|
||||
}
|
||||
[mutablePinnedPublicKeys addObject:publicKey];
|
||||
}
|
||||
self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys];
|
||||
} else {
|
||||
self.pinnedPublicKeys = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
|
||||
forDomain:(NSString *)domain
|
||||
{
|
||||
NSMutableArray *policies = [NSMutableArray array];
|
||||
if (self.validatesDomainName) {
|
||||
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
|
||||
} else {
|
||||
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
|
||||
}
|
||||
|
||||
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
|
||||
|
||||
if (self.SSLPinningMode == MQTTSSLPinningModeNone) {
|
||||
return self.allowInvalidCertificates || SSLServerTrustIsValid(serverTrust);
|
||||
}
|
||||
// if client didn't allow invalid certs, it must pass CA infrastructure
|
||||
// TODO: Can we change order here?
|
||||
else if (!SSLServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSArray *serverCertificates = SSLCertificateTrustChainForServerTrust(serverTrust);
|
||||
switch (self.SSLPinningMode) {
|
||||
case MQTTSSLPinningModeNone:
|
||||
default:
|
||||
return NO;
|
||||
case MQTTSSLPinningModeCertificate: {
|
||||
NSMutableArray *pinnedCertificates = [NSMutableArray array];
|
||||
for (NSData *certificateData in self.pinnedCertificates) {
|
||||
@try {
|
||||
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
|
||||
} @catch (NSException *exception) {
|
||||
//fix issue #151, if the pinnedCertification is not a valid DER-encoded X.509 certificate, for example it is the PEM format, SecCertificateCreateWithData will return nil, and application will crash
|
||||
if ([exception.name isEqual:NSInvalidArgumentException]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
|
||||
|
||||
if (!SSLServerTrustIsValid(serverTrust)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!self.validatesCertificateChain) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSUInteger trustedCertificateCount = 0;
|
||||
for (NSData *trustChainCertificate in serverCertificates) {
|
||||
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
|
||||
trustedCertificateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return trustedCertificateCount == serverCertificates.count;
|
||||
}
|
||||
case MQTTSSLPinningModePublicKey: {
|
||||
NSUInteger trustedPublicKeyCount = 0;
|
||||
NSArray *publicKeys = SSLPublicKeyTrustChainForServerTrust(serverTrust);
|
||||
if (!self.validatesCertificateChain && publicKeys.count > 0) {
|
||||
publicKeys = @[publicKeys.firstObject];
|
||||
}
|
||||
|
||||
for (id trustChainPublicKey in publicKeys) {
|
||||
for (id pinnedPublicKey in self.pinnedPublicKeys) {
|
||||
if (SSLSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
|
||||
trustedPublicKeyCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return trustedPublicKeyCount > 0 && ((self.validatesCertificateChain && trustedPublicKeyCount == serverCertificates.count) || (!self.validatesCertificateChain && trustedPublicKeyCount >= 1));
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - NSKeyValueObserving
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {
|
||||
return [NSSet setWithObject:@"pinnedCertificates"];
|
||||
}
|
||||
@end
|
||||
19
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyDecoder.h
generated
Normal file
19
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyDecoder.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// MQTTSSLSecurityPolicyDecoder.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MQTTSSLSecurityPolicy.h"
|
||||
#import "MQTTCFSocketDecoder.h"
|
||||
|
||||
@interface MQTTSSLSecurityPolicyDecoder : MQTTCFSocketDecoder
|
||||
|
||||
@property(strong, nonatomic) MQTTSSLSecurityPolicy *securityPolicy;
|
||||
@property(strong, nonatomic) NSString *securityDomain;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
60
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyDecoder.m
generated
Normal file
60
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyDecoder.m
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// MQTTSSLSecurityPolicyDecoder.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTSSLSecurityPolicyDecoder.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTSSLSecurityPolicyDecoder()
|
||||
@property (nonatomic) BOOL securityPolicyApplied;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTSSLSecurityPolicyDecoder
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.securityPolicy = nil;
|
||||
self.securityDomain = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)applySSLSecurityPolicy:(NSStream *)readStream withEvent:(NSStreamEvent)eventCode{
|
||||
if (!self.securityPolicy) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (self.securityPolicyApplied) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
SecTrustRef serverTrust = (__bridge SecTrustRef) [readStream propertyForKey: (__bridge NSString *)kCFStreamPropertySSLPeerTrust];
|
||||
if (!serverTrust) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
self.securityPolicyApplied = [self.securityPolicy evaluateServerTrust:serverTrust forDomain:self.securityDomain];
|
||||
return self.securityPolicyApplied;
|
||||
}
|
||||
|
||||
- (void)stream:(NSStream *)sender handleEvent:(NSStreamEvent)eventCode {
|
||||
if (eventCode & NSStreamEventHasBytesAvailable) {
|
||||
DDLogVerbose(@"[MQTTCFSocketDecoder] NSStreamEventHasBytesAvailable");
|
||||
if (![self applySSLSecurityPolicy:sender withEvent:eventCode]){
|
||||
self.state = MQTTCFSocketDecoderStateError;
|
||||
self.error = [NSError errorWithDomain:@"MQTT"
|
||||
code:errSSLXCertChainInvalid
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Unable to apply security policy, the SSL connection is insecure!"}];
|
||||
[self.delegate decoder:self didFailWithError:self.error];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[super stream:sender handleEvent:eventCode];
|
||||
}
|
||||
|
||||
@end
|
||||
18
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyEncoder.h
generated
Normal file
18
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyEncoder.h
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// MQTTSSLSecurityPolicyEncoder.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MQTTSSLSecurityPolicy.h"
|
||||
#import "MQTTCFSocketEncoder.h"
|
||||
|
||||
@interface MQTTSSLSecurityPolicyEncoder : MQTTCFSocketEncoder
|
||||
|
||||
@property(strong, nonatomic) MQTTSSLSecurityPolicy *securityPolicy;
|
||||
@property(strong, nonatomic) NSString *securityDomain;
|
||||
|
||||
@end
|
||||
|
||||
60
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyEncoder.m
generated
Normal file
60
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyEncoder.m
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// MQTTSSLSecurityPolicyEncoder.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTSSLSecurityPolicyEncoder.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTSSLSecurityPolicyEncoder()
|
||||
@property (nonatomic) BOOL securityPolicyApplied;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTSSLSecurityPolicyEncoder
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.securityPolicy = nil;
|
||||
self.securityDomain = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)applySSLSecurityPolicy:(NSStream *)writeStream withEvent:(NSStreamEvent)eventCode {
|
||||
if (!self.securityPolicy) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (self.securityPolicyApplied) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
SecTrustRef serverTrust = (__bridge SecTrustRef)[writeStream propertyForKey:(__bridge NSString *)kCFStreamPropertySSLPeerTrust];
|
||||
if (!serverTrust) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
self.securityPolicyApplied = [self.securityPolicy evaluateServerTrust:serverTrust forDomain:self.securityDomain];
|
||||
return self.securityPolicyApplied;
|
||||
}
|
||||
|
||||
- (void)stream:(NSStream *)sender handleEvent:(NSStreamEvent)eventCode {
|
||||
if (eventCode & NSStreamEventHasSpaceAvailable) {
|
||||
DDLogVerbose(@"[MQTTCFSocketEncoder] NSStreamEventHasSpaceAvailable");
|
||||
if (![self applySSLSecurityPolicy:sender withEvent:eventCode]){
|
||||
self.state = MQTTCFSocketEncoderStateError;
|
||||
self.error = [NSError errorWithDomain:@"MQTT"
|
||||
code:errSSLXCertChainInvalid
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Unable to apply security policy, the SSL connection is insecure!"}];
|
||||
[self.delegate encoder:self didFailWithError:self.error];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[super stream:sender handleEvent:eventCode];
|
||||
}
|
||||
|
||||
@end
|
||||
30
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyTransport.h
generated
Normal file
30
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyTransport.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// MQTTSSLSecurityPolicyTransport.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 06.12.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTTransport.h"
|
||||
#import "MQTTSSLSecurityPolicy.h"
|
||||
#import "MQTTCFSocketTransport.h"
|
||||
|
||||
/** MQTTSSLSecurityPolicyTransport
|
||||
* implements an extension of the MQTTCFSocketTransport by replacing the OS's certificate chain evaluation
|
||||
*/
|
||||
@interface MQTTSSLSecurityPolicyTransport : MQTTCFSocketTransport
|
||||
|
||||
/**
|
||||
* The security policy used to evaluate server trust for secure connections.
|
||||
*
|
||||
* if your app using security model which require pinning SSL certificates to helps prevent man-in-the-middle attacks
|
||||
* and other vulnerabilities. you need to set securityPolicy to properly value(see MQTTSSLSecurityPolicy.h for more detail).
|
||||
*
|
||||
* NOTE: about self-signed server certificates:
|
||||
* if your server using Self-signed certificates to establish SSL/TLS connection, you need to set property:
|
||||
* MQTTSSLSecurityPolicy.allowInvalidCertificates=YES.
|
||||
*/
|
||||
@property (strong, nonatomic) MQTTSSLSecurityPolicy *securityPolicy;
|
||||
|
||||
@end
|
||||
98
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyTransport.m
generated
Normal file
98
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSSLSecurityPolicyTransport.m
generated
Normal file
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// MQTTSSLSecurityPolicyTransport.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 06.12.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTSSLSecurityPolicyTransport.h"
|
||||
#import "MQTTSSLSecurityPolicyEncoder.h"
|
||||
#import "MQTTSSLSecurityPolicyDecoder.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTSSLSecurityPolicyTransport()
|
||||
@property (strong, nonatomic) MQTTSSLSecurityPolicyEncoder *encoder;
|
||||
@property (strong, nonatomic) MQTTSSLSecurityPolicyDecoder *decoder;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTSSLSecurityPolicyTransport
|
||||
@synthesize state;
|
||||
@synthesize delegate;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.securityPolicy = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)open {
|
||||
DDLogVerbose(@"[MQTTSSLSecurityPolicyTransport] open");
|
||||
self.state = MQTTTransportOpening;
|
||||
|
||||
NSError* connectError;
|
||||
|
||||
CFReadStreamRef readStream;
|
||||
CFWriteStreamRef writeStream;
|
||||
|
||||
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.host, self.port, &readStream, &writeStream);
|
||||
|
||||
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
|
||||
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
|
||||
|
||||
if (self.tls) {
|
||||
NSMutableDictionary *sslOptions = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// Delegate certificates verify operation to our secure policy.
|
||||
// by disabling chain validation, it becomes our responsibility to verify that the host at the other end can be trusted.
|
||||
// the server's certificates will be verified during MQTT encoder/decoder processing.
|
||||
sslOptions[(NSString *)kCFStreamSSLValidatesCertificateChain] = @NO;
|
||||
sslOptions[(NSString *)kCFStreamSSLLevel] = self.streamSSLLevel;
|
||||
|
||||
if (self.certificates) {
|
||||
sslOptions[(NSString *)kCFStreamSSLCertificates] = self.certificates;
|
||||
}
|
||||
|
||||
if (!CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(sslOptions))){
|
||||
connectError = [NSError errorWithDomain:@"MQTT"
|
||||
code:errSSLInternal
|
||||
userInfo:@{NSLocalizedDescriptionKey : @"Fail to init ssl input stream!"}];
|
||||
}
|
||||
if (!CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(sslOptions))){
|
||||
connectError = [NSError errorWithDomain:@"MQTT"
|
||||
code:errSSLInternal
|
||||
userInfo:@{NSLocalizedDescriptionKey : @"Fail to init ssl output stream!"}];
|
||||
}
|
||||
}
|
||||
|
||||
if (!connectError) {
|
||||
self.encoder = [[MQTTSSLSecurityPolicyEncoder alloc] init];
|
||||
CFWriteStreamSetDispatchQueue(writeStream, self.queue);
|
||||
self.encoder.stream = CFBridgingRelease(writeStream);
|
||||
self.encoder.securityPolicy = self.tls ? self.securityPolicy : nil;
|
||||
self.encoder.securityDomain = self.tls ? self.host : nil;
|
||||
self.encoder.delegate = self;
|
||||
if (self.voip) {
|
||||
[self.encoder.stream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
|
||||
}
|
||||
[self.encoder open];
|
||||
|
||||
self.decoder = [[MQTTSSLSecurityPolicyDecoder alloc] init];
|
||||
CFReadStreamSetDispatchQueue(readStream, self.queue);
|
||||
self.decoder.stream = CFBridgingRelease(readStream);
|
||||
self.decoder.securityPolicy = self.tls ? self.securityPolicy : nil;
|
||||
self.decoder.securityDomain = self.tls ? self.host : nil;
|
||||
self.decoder.delegate = self;
|
||||
if (self.voip) {
|
||||
[self.decoder.stream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
|
||||
}
|
||||
[self.decoder open];
|
||||
|
||||
} else {
|
||||
[self close];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
886
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSession.h
generated
Normal file
886
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSession.h
generated
Normal file
@@ -0,0 +1,886 @@
|
||||
//
|
||||
// MQTTSession.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
|
||||
/**
|
||||
Using MQTT in your Objective-C application
|
||||
|
||||
@author Christoph Krey c@ckrey.de
|
||||
@copyright Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
|
||||
based on Copyright (c) 2011, 2013, 2lemetry LLC
|
||||
All rights reserved. This program and the accompanying materials
|
||||
are made available under the terms of the Eclipse Public License v1.0
|
||||
which accompanies this distribution, and is available at
|
||||
http://www.eclipse.org/legal/epl-v10.html
|
||||
|
||||
@see http://mqtt.org
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "MQTTMessage.h"
|
||||
#import "MQTTPersistence.h"
|
||||
#import "MQTTTransport.h"
|
||||
|
||||
@class MQTTSession;
|
||||
@class MQTTSSLSecurityPolicy;
|
||||
|
||||
/**
|
||||
Enumeration of MQTTSession states
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, MQTTSessionStatus) {
|
||||
MQTTSessionStatusCreated,
|
||||
MQTTSessionStatusConnecting,
|
||||
MQTTSessionStatusConnected,
|
||||
MQTTSessionStatusDisconnecting,
|
||||
MQTTSessionStatusClosed,
|
||||
MQTTSessionStatusError
|
||||
};
|
||||
|
||||
/**
|
||||
Enumeration of MQTTSession events
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, MQTTSessionEvent) {
|
||||
MQTTSessionEventConnected,
|
||||
MQTTSessionEventConnectionRefused,
|
||||
MQTTSessionEventConnectionClosed,
|
||||
MQTTSessionEventConnectionError,
|
||||
MQTTSessionEventProtocolError,
|
||||
MQTTSessionEventConnectionClosedByBroker
|
||||
};
|
||||
|
||||
/**
|
||||
The error domain used for all errors created by MQTTSession
|
||||
*/
|
||||
extern NSString * const MQTTSessionErrorDomain;
|
||||
|
||||
/**
|
||||
The error codes used for all errors created by MQTTSession
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, MQTTSessionError) {
|
||||
MQTTSessionErrorConnectionRefused = -8, // Sent if the server closes the connection without sending an appropriate error CONNACK
|
||||
MQTTSessionErrorIllegalMessageReceived = -7,
|
||||
MQTTSessionErrorDroppingOutgoingMessage = -6, // For some reason the value is the same as for MQTTSessionErrorNoResponse
|
||||
MQTTSessionErrorNoResponse = -6, // For some reason the value is the same as for MQTTSessionErrorDroppingOutgoingMessage
|
||||
MQTTSessionErrorEncoderNotReady = -5,
|
||||
MQTTSessionErrorInvalidConnackReceived = -2, // Sent if the message received from server was an invalid connack message
|
||||
MQTTSessionErrorNoConnackReceived = -1, // Sent if first message received from server was no connack message
|
||||
|
||||
MQTTSessionErrorConnackUnacceptableProtocolVersion = 1, // Value as defined by MQTT Protocol
|
||||
MQTTSessionErrorConnackIdentifierRejected = 2, // Value as defined by MQTT Protocol
|
||||
MQTTSessionErrorConnackServeUnavailable = 3, // Value as defined by MQTT Protocol
|
||||
MQTTSessionErrorConnackBadUsernameOrPassword = 4, // Value as defined by MQTT Protocol
|
||||
MQTTSessionErrorConnackNotAuthorized = 5, // Value as defined by MQTT Protocol
|
||||
MQTTSessionErrorConnackReserved = 6, // Should be value 6-255, as defined by MQTT Protocol
|
||||
};
|
||||
|
||||
/** Session delegate gives your application control over the MQTTSession
|
||||
@note all callback methods are optional
|
||||
*/
|
||||
|
||||
@protocol MQTTSessionDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/** gets called when a new message was received
|
||||
@param session the MQTTSession reporting the new message
|
||||
@param data the data received, might be zero length
|
||||
@param topic the topic the data was published to
|
||||
@param qos the qos of the message
|
||||
@param retained indicates if the data retransmitted from server storage
|
||||
@param mid the Message Identifier of the message if qos = 1 or 2, zero otherwise
|
||||
*/
|
||||
- (void)newMessage:(MQTTSession *)session
|
||||
data:(NSData *)data
|
||||
onTopic:(NSString *)topic
|
||||
qos:(MQTTQosLevel)qos
|
||||
retained:(BOOL)retained
|
||||
mid:(unsigned int)mid;
|
||||
|
||||
/** gets called when a new message was received
|
||||
@param session the MQTTSession reporting the new message
|
||||
@param data the data received, might be zero length
|
||||
@param topic the topic the data was published to
|
||||
@param qos the qos of the message
|
||||
@param retained indicates if the data retransmitted from server storage
|
||||
@param mid the Message Identifier of the message if qos = 1 or 2, zero otherwise
|
||||
@return true if the message was or will be processed, false if the message shall not be ack-ed
|
||||
*/
|
||||
- (BOOL)newMessageWithFeedback:(MQTTSession *)session
|
||||
data:(NSData *)data
|
||||
onTopic:(NSString *)topic
|
||||
qos:(MQTTQosLevel)qos
|
||||
retained:(BOOL)retained
|
||||
mid:(unsigned int)mid;
|
||||
|
||||
/** for mqttio-OBJC backward compatibility
|
||||
@param session see newMessage for description
|
||||
@param data see newMessage for description
|
||||
@param topic see newMessage for description
|
||||
*/
|
||||
- (void)session:(MQTTSession*)session newMessage:(NSData*)data onTopic:(NSString*)topic;
|
||||
|
||||
/** gets called when a connection is established, closed or a problem occurred
|
||||
@param session the MQTTSession reporting the event
|
||||
@param eventCode the code of the event
|
||||
@param error an optional additional error object with additional information
|
||||
*/
|
||||
- (void)handleEvent:(MQTTSession *)session event:(MQTTSessionEvent)eventCode error:(NSError *)error;
|
||||
|
||||
/** for mqttio-OBJC backward compatibility
|
||||
@param session the MQTTSession reporting the event
|
||||
@param eventCode the code of the event
|
||||
*/
|
||||
- (void)session:(MQTTSession*)session handleEvent:(MQTTSessionEvent)eventCode;
|
||||
|
||||
/** gets called when a connection has been successfully established
|
||||
@param session the MQTTSession reporting the connect
|
||||
|
||||
*/
|
||||
- (void)connected:(MQTTSession *)session;
|
||||
|
||||
/** gets called when a connection has been successfully established
|
||||
@param session the MQTTSession reporting the connect
|
||||
@param sessionPresent represents the Session Present flag sent by the broker
|
||||
|
||||
*/
|
||||
- (void)connected:(MQTTSession *)session sessionPresent:(BOOL)sessionPresent;
|
||||
|
||||
/** gets called when a connection has been refused
|
||||
@param session the MQTTSession reporting the refusal
|
||||
@param error an optional additional error object with additional information
|
||||
*/
|
||||
- (void)connectionRefused:(MQTTSession *)session error:(NSError *)error;
|
||||
|
||||
/** gets called when a connection has been closed
|
||||
@param session the MQTTSession reporting the close
|
||||
|
||||
*/
|
||||
- (void)connectionClosed:(MQTTSession *)session;
|
||||
|
||||
/** gets called when a connection error happened
|
||||
@param session the MQTTSession reporting the connect error
|
||||
@param error an optional additional error object with additional information
|
||||
*/
|
||||
- (void)connectionError:(MQTTSession *)session error:(NSError *)error;
|
||||
|
||||
/** gets called when an MQTT protocol error happened
|
||||
@param session the MQTTSession reporting the protocol error
|
||||
@param error an optional additional error object with additional information
|
||||
*/
|
||||
- (void)protocolError:(MQTTSession *)session error:(NSError *)error;
|
||||
|
||||
/** gets called when a published message was actually delivered
|
||||
@param session the MQTTSession reporting the delivery
|
||||
@param msgID the Message Identifier of the delivered message
|
||||
@note this method is called after a publish with qos 1 or 2 only
|
||||
*/
|
||||
- (void)messageDelivered:(MQTTSession *)session msgID:(UInt16)msgID;
|
||||
|
||||
/** gets called when a published message was actually delivered
|
||||
@param session the MQTTSession reporting the delivery
|
||||
@param msgID the Message Identifier of the delivered message
|
||||
@param topic the topic of the delivered message
|
||||
@param data the data Identifier of the delivered message
|
||||
@param qos the QoS level of the delivered message
|
||||
@param retainFlag the retain Flag of the delivered message
|
||||
@note this method is called after a publish with qos 1 or 2 only
|
||||
*/
|
||||
- (void)messageDelivered:(MQTTSession *)session
|
||||
msgID:(UInt16)msgID
|
||||
topic:(NSString *)topic
|
||||
data:(NSData *)data
|
||||
qos:(MQTTQosLevel)qos
|
||||
retainFlag:(BOOL)retainFlag;
|
||||
|
||||
/** gets called when a subscription is acknowledged by the MQTT broker
|
||||
@param session the MQTTSession reporting the acknowledge
|
||||
@param msgID the Message Identifier of the SUBSCRIBE message
|
||||
@param qoss an array containing the granted QoS(s) related to the SUBSCRIBE message
|
||||
(see subscribeTopic, subscribeTopics)
|
||||
*/
|
||||
- (void)subAckReceived:(MQTTSession *)session msgID:(UInt16)msgID grantedQoss:(NSArray<NSNumber *> *)qoss;
|
||||
|
||||
/** gets called when an unsubscribe is acknowledged by the MQTT broker
|
||||
@param session the MQTTSession reporting the acknowledge
|
||||
@param msgID the Message Identifier of the UNSUBSCRIBE message
|
||||
*/
|
||||
- (void)unsubAckReceived:(MQTTSession *)session msgID:(UInt16)msgID;
|
||||
|
||||
/** gets called when a command is sent to the MQTT broker
|
||||
use this for low level monitoring of the MQTT connection
|
||||
@param session the MQTTSession reporting the sent command
|
||||
@param type the MQTT command type
|
||||
@param qos the Quality of Service of the command
|
||||
@param retained the retained status of the command
|
||||
@param duped the duplication status of the command
|
||||
@param mid the Message Identifier of the command
|
||||
@param data the payload data of the command if any, might be zero length
|
||||
*/
|
||||
- (void)sending:(MQTTSession *)session type:(MQTTCommandType)type qos:(MQTTQosLevel)qos retained:(BOOL)retained duped:(BOOL)duped mid:(UInt16)mid data:(NSData *)data;
|
||||
|
||||
/** gets called when a command is received from the MQTT broker
|
||||
use this for low level monitoring of the MQTT connection
|
||||
@param session the MQTTSession reporting the received command
|
||||
@param type the MQTT command type
|
||||
@param qos the Quality of Service of the command
|
||||
@param retained the retained status of the command
|
||||
@param duped the duplication status of the command
|
||||
@param mid the Message Identifier of the command
|
||||
@param data the payload data of the command if any, might be zero length
|
||||
*/
|
||||
- (void)received:(MQTTSession *)session type:(MQTTCommandType)type qos:(MQTTQosLevel)qos retained:(BOOL)retained duped:(BOOL)duped mid:(UInt16)mid data:(NSData *)data;
|
||||
|
||||
/** gets called when a command is received from the MQTT broker
|
||||
use this for low level control of the MQTT connection
|
||||
@param session the MQTTSession reporting the received command
|
||||
@param type the MQTT command type
|
||||
@param qos the Quality of Service of the command
|
||||
@param retained the retained status of the command
|
||||
@param duped the duplication status of the command
|
||||
@param mid the Message Identifier of the command
|
||||
@param data the payload data of the command if any, might be zero length
|
||||
@return true if the sessionmanager should ignore the received message
|
||||
*/
|
||||
- (BOOL)ignoreReceived:(MQTTSession *)session type:(MQTTCommandType)type qos:(MQTTQosLevel)qos retained:(BOOL)retained duped:(BOOL)duped mid:(UInt16)mid data:(NSData *)data;
|
||||
|
||||
/** gets called when the content of MQTTClients internal buffers change
|
||||
use for monitoring the completion of transmitted and received messages
|
||||
@param session the MQTTSession reporting the change
|
||||
@param queued for backward compatibility only: MQTTClient does not queue messages anymore except during QoS protocol
|
||||
@param flowingIn the number of incoming messages not acknowledged by the MQTTClient yet
|
||||
@param flowingOut the number of outgoing messages not yet acknowledged by the MQTT broker
|
||||
*/
|
||||
- (void)buffered:(MQTTSession *)session
|
||||
queued:(NSUInteger)queued
|
||||
flowingIn:(NSUInteger)flowingIn
|
||||
flowingOut:(NSUInteger)flowingOut;
|
||||
|
||||
/** gets called when the content of MQTTClients internal buffers change
|
||||
use for monitoring the completion of transmitted and received messages
|
||||
@param session the MQTTSession reporting the change
|
||||
@param flowingIn the number of incoming messages not acknowledged by the MQTTClient yet
|
||||
@param flowingOut the number of outgoing messages not yet acknowledged by the MQTT broker
|
||||
*/
|
||||
- (void)buffered:(MQTTSession *)session
|
||||
flowingIn:(NSUInteger)flowingIn
|
||||
flowingOut:(NSUInteger)flowingOut;
|
||||
|
||||
@end
|
||||
|
||||
typedef void (^MQTTConnectHandler)(NSError *error);
|
||||
typedef void (^MQTTDisconnectHandler)(NSError *error);
|
||||
typedef void (^MQTTSubscribeHandler)(NSError *error, NSArray<NSNumber *> *gQoss);
|
||||
typedef void (^MQTTUnsubscribeHandler)(NSError *error);
|
||||
typedef void (^MQTTPublishHandler)(NSError *error);
|
||||
|
||||
/** Session implements the MQTT protocol for your application
|
||||
*
|
||||
*/
|
||||
|
||||
@interface MQTTSession : NSObject
|
||||
|
||||
/** set this member variable to receive delegate messages
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
@interface MyClass : NSObject <MQTTSessionDelegate>
|
||||
...
|
||||
@end
|
||||
|
||||
...
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
session.delegate = self;
|
||||
...
|
||||
- (void)handleEvent:(MQTTSession *)session
|
||||
event:(MQTTSessionEvent)eventCode
|
||||
error:(NSError *)error {
|
||||
...
|
||||
}
|
||||
- (void)newMessage:(MQTTSession *)session
|
||||
data:(NSData *)data
|
||||
onTopic:(NSString *)topic
|
||||
qos:(MQTTQosLevel)qos
|
||||
retained:(BOOL)retained
|
||||
mid:(unsigned int)mid {
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
|
||||
*/
|
||||
@property (weak, nonatomic) id<MQTTSessionDelegate> delegate;
|
||||
|
||||
/** Control MQTT persistence by setting the properties of persistence before connecting to an MQTT broker.
|
||||
The settings are specific to a clientId.
|
||||
|
||||
persistence.persistent = YES or NO (default) to establish file or in memory persistence. IMPORTANT: set immediately after creating the MQTTSession before calling any other method. Otherwise the default value (NO) will be used
|
||||
for this session.
|
||||
|
||||
persistence.maxWindowSize (a positive number, default is 16) to control the number of messages sent before waiting for acknowledgement in Qos 1 or 2. Additional messages are
|
||||
stored and transmitted later.
|
||||
|
||||
persistence.maxSize (a positive number of bytes, default is 64 MB) to limit the size of the persistence file. Messages published after the limit is reached are dropped.
|
||||
|
||||
persistence.maxMessages (a positive number, default is 1024) to limit the number of messages stored. Additional messages published are dropped.
|
||||
|
||||
Messages are deleted after they have been acknowledged.
|
||||
*/
|
||||
@property (strong, nonatomic) id<MQTTPersistence> persistence;
|
||||
|
||||
/** block called once when connection is established
|
||||
*/
|
||||
@property (copy, nonatomic) MQTTConnectHandler connectHandler;
|
||||
|
||||
/** block called when connection is established
|
||||
*/
|
||||
@property (strong) void (^connectionHandler)(MQTTSessionEvent event);
|
||||
|
||||
/** block called when message is received
|
||||
*/
|
||||
@property (strong) void (^messageHandler)(NSData* message, NSString* topic);
|
||||
|
||||
/** Session status
|
||||
*/
|
||||
@property (nonatomic, readonly) MQTTSessionStatus status;
|
||||
|
||||
/** Indicates if the broker found a persistent session when connecting with cleanSession:FALSE
|
||||
*/
|
||||
@property (nonatomic, readonly) BOOL sessionPresent;
|
||||
|
||||
/** streamSSLLevel an NSString containing the security level for read and write streams
|
||||
* For list of possible values see:
|
||||
* https://developer.apple.com/documentation/corefoundation/cfstream/cfstream_socket_security_level_constants
|
||||
* Please also note that kCFStreamSocketSecurityLevelTLSv1_2 is not in a list
|
||||
* and cannot be used as constant, but you can use it as a string value
|
||||
* defaults to kCFStreamSocketSecurityLevelNegotiatedSSL
|
||||
*/
|
||||
@property (strong, nonatomic) NSString *streamSSLLevel;
|
||||
|
||||
/** host an NSString containing the hostName or IP address of the Server
|
||||
*/
|
||||
@property (readonly) NSString *host;
|
||||
|
||||
/** port an unsigned 32 bit integer containing the IP port number of the Server
|
||||
*/
|
||||
@property (readonly) UInt32 port;
|
||||
|
||||
/** The Client Identifier identifies the Client to the Server. If nil, a random clientId is generated.
|
||||
*/
|
||||
@property (strong, nonatomic) NSString *clientId;
|
||||
|
||||
/** see userName an NSString object containing the user's name (or ID) for authentication. May be nil. */
|
||||
@property (strong, nonatomic) NSString *userName;
|
||||
|
||||
/** see password an NSString object containing the user's password. If userName is nil, password must be nil as well.*/
|
||||
@property (strong, nonatomic) NSString *password;
|
||||
|
||||
/** see keepAliveInterval The Keep Alive is a time interval measured in seconds.
|
||||
* The MQTTClient ensures that the interval between Control Packets being sent does not exceed
|
||||
* the Keep Alive value. In the absence of sending any other Control Packets, the Client sends a PINGREQ Packet.
|
||||
*/
|
||||
@property (nonatomic) UInt16 keepAliveInterval;
|
||||
|
||||
/** The serverKeepAlive is a time interval measured in seconds.
|
||||
* This value may be set by the broker and overrides keepAliveInterval if present
|
||||
* Zero means the broker does not perform any keep alive checks
|
||||
*/
|
||||
@property (readonly, strong, nonatomic) NSNumber *serverKeepAlive;
|
||||
|
||||
/** effectiveKeepAlive is a time interval measured in seconds
|
||||
* It indicates the effective keep alive interval after a successfull connect
|
||||
* where keepAliveInterval might have been overridden by the broker.
|
||||
*/
|
||||
@property (readonly, nonatomic) UInt16 effectiveKeepAlive;
|
||||
|
||||
|
||||
/**
|
||||
* dupTimeout If PUBACK or PUBREC not received, message will be resent after this interval
|
||||
*/
|
||||
@property (nonatomic) double dupTimeout;
|
||||
|
||||
/** leanSessionFlag specifies if the server should discard previous session information. */
|
||||
@property (nonatomic) BOOL cleanSessionFlag;
|
||||
|
||||
/** willFlag If the Will Flag is set to YES this indicates that
|
||||
* a Will Message MUST be published by the Server when the Server detects
|
||||
* that the Client is disconnected for any reason other than the Client flowing a DISCONNECT Packet.
|
||||
*/
|
||||
@property (nonatomic) BOOL willFlag;
|
||||
|
||||
/** willTopic If the Will Flag is set to YES, the Will Topic is a string, nil otherwise. */
|
||||
@property (strong, nonatomic) NSString *willTopic;
|
||||
|
||||
/** willMsg If the Will Flag is set to YES the Will Message must be specified, nil otherwise. */
|
||||
@property (strong, nonatomic) NSData *willMsg;
|
||||
|
||||
/** willQoS specifies the QoS level to be used when publishing the Will Message.
|
||||
* If the Will Flag is set to NO, then the Will QoS MUST be set to 0.
|
||||
* If the Will Flag is set to YES, the Will QoS MUST be a valid MQTTQosLevel.
|
||||
*/
|
||||
@property (nonatomic) MQTTQosLevel willQoS;
|
||||
|
||||
/** willRetainFlag indicates if the server should publish the Will Messages with retainFlag.
|
||||
* If the Will Flag is set to NO, then the Will Retain Flag MUST be set to NO .
|
||||
* If the Will Flag is set to YES: If Will Retain is set to NO, the Serve
|
||||
* MUST publish the Will Message as a non-retained publication [MQTT-3.1.2-14].
|
||||
* If Will Retain is set to YES, the Server MUST publish the Will Message as a retained publication [MQTT-3.1.2-15].
|
||||
*/
|
||||
@property (nonatomic) BOOL willRetainFlag;
|
||||
|
||||
/** protocolLevel specifies the protocol to be used */
|
||||
@property (nonatomic) MQTTProtocolVersion protocolLevel;
|
||||
|
||||
/** sessionExpiryInterval specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSNumber *sessionExpiryInterval;
|
||||
|
||||
/** authMethod specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSString *authMethod;
|
||||
|
||||
/** authData specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSData *authData;
|
||||
|
||||
/** requestProblemInformation specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSNumber *requestProblemInformation;
|
||||
|
||||
/** willDelayInterval specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSNumber *willDelayInterval;
|
||||
|
||||
/** requestResponseInformation specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSNumber *requestResponseInformation;
|
||||
|
||||
/** receiveMaximum specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSNumber *receiveMaximum;
|
||||
|
||||
/** topicAliasMaximum specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSNumber *topicAliasMaximum;
|
||||
|
||||
/** topicAliasMaximum specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSDictionary <NSString *, NSString*> *userProperty;
|
||||
|
||||
/** maximumPacketSize specifies the number of seconds after which a session should expire MQTT v5.0*/
|
||||
@property (strong, nonatomic) NSNumber *maximumPacketSize;
|
||||
|
||||
/** queue The queue where the streams are scheduled. */
|
||||
@property (strong, nonatomic) dispatch_queue_t queue;
|
||||
|
||||
|
||||
/** for mqttio-OBJC backward compatibility
|
||||
the connect message used is stored here
|
||||
*/
|
||||
@property (strong, nonatomic) MQTTMessage *connectMessage;
|
||||
|
||||
/** the transport provider for MQTTClient
|
||||
*
|
||||
* assign an in instance of a class implementing the MQTTTransport protocol e.g.
|
||||
* MQTTCFSocketTransport before connecting.
|
||||
*/
|
||||
@property (strong, nonatomic) id <MQTTTransport> transport;
|
||||
|
||||
/** certificates an NSArray holding client certificates or nil */
|
||||
@property (strong, nonatomic) NSArray *certificates;
|
||||
|
||||
/** Require for VoIP background service
|
||||
* defaults to NO
|
||||
*/
|
||||
@property (nonatomic) BOOL voip;
|
||||
|
||||
/** connect to the given host through the given transport with the given
|
||||
* MQTT session parameters asynchronously
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
- (void)connect;
|
||||
|
||||
/** connects to the specified MQTT server
|
||||
|
||||
@param connectHandler identifies a block which is executed on successfull or unsuccessfull connect. Might be nil
|
||||
error is nil in the case of a successful connect
|
||||
sessionPresent indicates in MQTT 3.1.1 if persistent session data was present at the server
|
||||
returns nothing and returns immediately. To check the connect results, register as an MQTTSessionDelegate and
|
||||
- watch for events
|
||||
- watch for connect or connectionRefused messages
|
||||
- watch for error messages
|
||||
or use the connectHandler block
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connectWithConnectHandler:^(NSError *error, BOOL sessionPresent) {
|
||||
if (error) {
|
||||
NSLog(@"Error Connect %@", error.localizedDescription);
|
||||
} else {
|
||||
NSLog(@"Connected sessionPresent:%d", sessionPresent);
|
||||
}
|
||||
}];
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
- (void)connectWithConnectHandler:(MQTTConnectHandler)connectHandler;
|
||||
|
||||
|
||||
/** disconnect gracefully
|
||||
*
|
||||
*/
|
||||
- (void)disconnect;
|
||||
|
||||
/** disconnect V5
|
||||
* @param returnCode the returncode send to the broker
|
||||
* @param sessionExpiryInterval the time in seconds before the session can be deleted
|
||||
* @param reasonString a string explaining the reason
|
||||
* @param userProperty additional dictionary of user key/value combinations
|
||||
*/
|
||||
- (void)disconnectWithReturnCode:(MQTTReturnCode)returnCode
|
||||
sessionExpiryInterval:(NSNumber *)sessionExpiryInterval
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty;
|
||||
|
||||
|
||||
/** initialises the MQTT session with default values
|
||||
@return the initialised MQTTSession object
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
@endcode
|
||||
*/
|
||||
- (MQTTSession *)init;
|
||||
|
||||
|
||||
|
||||
/** subscribes to a topic at a specific QoS level
|
||||
|
||||
@param topic see subscribeToTopic:atLevel:subscribeHandler: for description
|
||||
@param qosLevel see subscribeToTopic:atLevel:subscribeHandler: for description
|
||||
@return the Message Identifier of the SUBSCRIBE message.
|
||||
|
||||
@note returns immediately. To check results, register as an MQTTSessionDelegate and watch for events.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
...
|
||||
[session subscribeToTopic:@"example/#" atLevel:2];
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
- (UInt16)subscribeToTopic:(NSString *)topic
|
||||
atLevel:(MQTTQosLevel)qosLevel;
|
||||
/** subscribes to a topic at a specific QoS level
|
||||
|
||||
@param topic the Topic Filter to subscribe to.
|
||||
|
||||
@param qosLevel specifies the QoS Level of the subscription.
|
||||
qosLevel can be 0, 1, or 2.
|
||||
@param subscribeHandler identifies a block which is executed on successfull or unsuccessfull subscription.
|
||||
Might be nil. error is nil in the case of a successful subscription. In this case gQoss represents an
|
||||
array of grantes Qos
|
||||
|
||||
|
||||
@return the Message Identifier of the SUBSCRIBE message.
|
||||
|
||||
@note returns immediately. To check results, register as an MQTTSessionDelegate and watch for events.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
...
|
||||
[session subscribeToTopic:@"example/#" atLevel:2 subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss){
|
||||
if (error) {
|
||||
NSLog(@"Subscription failed %@", error.localizedDescription);
|
||||
} else {
|
||||
NSLog(@"Subscription sucessfull! Granted Qos: %@", gQoss);
|
||||
}
|
||||
}];
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
- (UInt16)subscribeToTopic:(NSString *)topic
|
||||
atLevel:(MQTTQosLevel)qosLevel
|
||||
subscribeHandler:(MQTTSubscribeHandler)subscribeHandler;
|
||||
|
||||
/** subscribes a number of topics
|
||||
|
||||
@param topics an NSDictionary<NSString *, NSNumber *> containing the Topic Filters to subscribe to as keys and
|
||||
the corresponding QoS as NSNumber values
|
||||
|
||||
@return the Message Identifier of the SUBSCRIBE message.
|
||||
|
||||
@note returns immediately. To check results, register as an MQTTSessionDelegate and watch for events.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
|
||||
[session subscribeToTopics:@{
|
||||
@"example/#": @(0),
|
||||
@"example/status": @(2),
|
||||
@"other/#": @(1)
|
||||
}];
|
||||
|
||||
@endcode
|
||||
*/
|
||||
|
||||
|
||||
- (UInt16)subscribeToTopics:(NSDictionary<NSString *, NSNumber *> *)topics;
|
||||
|
||||
/** subscribes a number of topics
|
||||
|
||||
@param topics an NSDictionary<NSString *, NSNumber *> containing the Topic Filters to subscribe to as keys and
|
||||
the corresponding QoS as NSNumber values
|
||||
@param subscribeHandler identifies a block which is executed on successfull or unsuccessfull subscription.
|
||||
Might be nil. error is nil in the case of a successful subscription. In this case gQoss represents an
|
||||
array of grantes Qos
|
||||
|
||||
@return the Message Identifier of the SUBSCRIBE message.
|
||||
|
||||
@note returns immediately. To check results, register as an MQTTSessionDelegate and watch for events.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
|
||||
[session subscribeToTopics:@{
|
||||
@"example/#": @(0),
|
||||
@"example/status": @(2),
|
||||
@"other/#": @(1)
|
||||
} subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss){
|
||||
if (error) {
|
||||
NSLog(@"Subscription failed %@", error.localizedDescription);
|
||||
} else {
|
||||
NSLog(@"Subscription sucessfull! Granted Qos: %@", gQoss);
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
@endcode
|
||||
*/
|
||||
|
||||
|
||||
- (UInt16)subscribeToTopics:(NSDictionary<NSString *, NSNumber *> *)topics
|
||||
subscribeHandler:(MQTTSubscribeHandler)subscribeHandler;
|
||||
|
||||
/** unsubscribes from a topic
|
||||
|
||||
@param topic the Topic Filter to unsubscribe from.
|
||||
|
||||
@return the Message Identifier of the UNSUBSCRIBE message.
|
||||
|
||||
@note returns immediately. To check results, register as an MQTTSessionDelegate and watch for events.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
|
||||
[session unsubscribeTopic:@"example/#"];
|
||||
|
||||
@endcode
|
||||
*/
|
||||
|
||||
- (UInt16)unsubscribeTopic:(NSString *)topic;
|
||||
|
||||
/** unsubscribes from a topic
|
||||
|
||||
@param topic the Topic Filter to unsubscribe from.
|
||||
@param unsubscribeHandler identifies a block which is executed on successfull or unsuccessfull subscription.
|
||||
Might be nil. error is nil in the case of a successful subscription. In this case gQoss represents an
|
||||
array of grantes Qos
|
||||
|
||||
@return the Message Identifier of the UNSUBSCRIBE message.
|
||||
|
||||
@note returns immediately.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
- (UInt16)unsubscribeTopic:(NSString *)topic
|
||||
unsubscribeHandler:(MQTTUnsubscribeHandler)unsubscribeHandler;
|
||||
|
||||
/** unsubscribes from a number of topics
|
||||
|
||||
@param topics an NSArray<NSString *> of topics to unsubscribe from
|
||||
|
||||
@return the Message Identifier of the UNSUBSCRIBE message.
|
||||
|
||||
@note returns immediately. To check results, register as an MQTTSessionDelegate and watch for events.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
|
||||
[session unsubscribeTopics:@[
|
||||
@"example/#",
|
||||
@"example/status",
|
||||
@"other/#"
|
||||
]];
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
- (UInt16)unsubscribeTopics:(NSArray<NSString *> *)topics;
|
||||
|
||||
/** unsubscribes from a number of topics
|
||||
|
||||
@param topics an NSArray<NSString *> of topics to unsubscribe from
|
||||
|
||||
@param unsubscribeHandler identifies a block which is executed on successfull or unsuccessfull subscription.
|
||||
Might be nil. error is nil in the case of a successful subscription. In this case gQoss represents an
|
||||
array of grantes Qos
|
||||
|
||||
@return the Message Identifier of the UNSUBSCRIBE message.
|
||||
|
||||
@note returns immediately.
|
||||
|
||||
*/
|
||||
- (UInt16)unsubscribeTopics:(NSArray<NSString *> *)topics
|
||||
unsubscribeHandler:(MQTTUnsubscribeHandler)unsubscribeHandler;
|
||||
|
||||
/** publishes data on a given topic at a specified QoS level and retain flag
|
||||
|
||||
@param data the data to be sent. length may range from 0 to 268,435,455 - 4 - _lengthof-topic_ bytes. Defaults to length 0.
|
||||
@param topic the Topic to identify the data
|
||||
@param retainFlag if YES, data is stored on the MQTT broker until overwritten by the next publish with retainFlag = YES
|
||||
@param qos specifies the Quality of Service for the publish
|
||||
qos can be 0, 1, or 2.
|
||||
@return the Message Identifier of the PUBLISH message. Zero if qos 0. If qos 1 or 2, zero if message was dropped
|
||||
|
||||
@note returns immediately. To check results, register as an MQTTSessionDelegate and watch for events.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
|
||||
[session publishData:[@"Sample Data" dataUsingEncoding:NSUTF8StringEncoding]
|
||||
topic:@"example/data"
|
||||
retain:YES
|
||||
qos:1];
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
- (UInt16)publishData:(NSData *)data
|
||||
onTopic:(NSString *)topic
|
||||
retain:(BOOL)retainFlag
|
||||
qos:(MQTTQosLevel)qos;
|
||||
|
||||
/** publishes data on a given topic at a specified QoS level and retain flag
|
||||
|
||||
@param data the data to be sent. length may range from 0 to 268,435,455 - 4 - _lengthof-topic_ bytes. Defaults to length 0.
|
||||
@param topic the Topic to identify the data
|
||||
@param retainFlag if YES, data is stored on the MQTT broker until overwritten by the next publish with retainFlag = YES
|
||||
@param qos specifies the Quality of Service for the publish
|
||||
qos can be 0, 1, or 2.
|
||||
|
||||
|
||||
@param publishHandler identifies a block which is executed on successfull or unsuccessfull publsh. Might be nil
|
||||
error is nil in the case of a successful connect
|
||||
sessionPresent indicates in MQTT 3.1.1 if persistent session data was present at the server
|
||||
|
||||
|
||||
@return the Message Identifier of the PUBLISH message. Zero if qos 0. If qos 1 or 2, zero if message was dropped
|
||||
|
||||
@note returns immediately. To check results, register as an MQTTSessionDelegate and watch for events.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
|
||||
[session publishData:[@"Sample Data" dataUsingEncoding:NSUTF8StringEncoding]
|
||||
topic:@"example/data"
|
||||
retain:YES
|
||||
qos:1
|
||||
publishHandler:^(NSError *error){
|
||||
if (error) {
|
||||
DDLogVerbose(@"error: %@ %@", error.localizedDescription, payload);
|
||||
} else {
|
||||
DDLogVerbose(@"delivered:%@", payload);
|
||||
delivered++;
|
||||
}
|
||||
}];
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
- (UInt16)publishData:(NSData *)data
|
||||
onTopic:(NSString *)topic
|
||||
retain:(BOOL)retainFlag
|
||||
qos:(MQTTQosLevel)qos
|
||||
publishHandler:(MQTTPublishHandler)publishHandler;
|
||||
|
||||
/** closes an MQTTSession gracefully
|
||||
|
||||
If the connection was successfully established before, a DISCONNECT is sent.
|
||||
|
||||
@param disconnectHandler identifies a block which is executed on successfull or unsuccessfull disconnect. Might be nil. error is nil in the case of a successful disconnect
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
...
|
||||
[session connect];
|
||||
|
||||
...
|
||||
|
||||
[session closeWithDisconnectHandler^(NSError *error) {
|
||||
if (error) {
|
||||
NSLog(@"Error Disconnect %@", error.localizedDescription);
|
||||
}
|
||||
NSLog(@"Session closed");
|
||||
}];
|
||||
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
||||
- (void)closeWithDisconnectHandler:(MQTTDisconnectHandler)disconnectHandler;
|
||||
|
||||
/** close V5
|
||||
* @param returnCode the returncode send to the broker
|
||||
* @param sessionExpiryInterval the time in seconds before the session can be deleted
|
||||
* @param reasonString a string explaining the reason
|
||||
* @param userProperty additional dictionary of user key/value combinations
|
||||
* @param disconnectHandler will be called when the disconnect finished
|
||||
*/
|
||||
- (void)closeWithReturnCode:(MQTTReturnCode)returnCode
|
||||
sessionExpiryInterval:(NSNumber *)sessionExpiryInterval
|
||||
reasonString:(NSString *)reasonString
|
||||
userProperty:(NSDictionary <NSString *, NSString *> *)userProperty
|
||||
disconnectHandler:(MQTTDisconnectHandler)disconnectHandler;
|
||||
|
||||
@end
|
||||
1641
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSession.m
generated
Normal file
1641
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSession.m
generated
Normal file
File diff suppressed because it is too large
Load Diff
112
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionLegacy.h
generated
Normal file
112
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionLegacy.h
generated
Normal file
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// MQTTSessionLegacy.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
|
||||
/**
|
||||
Using MQTT in your Objective-C application
|
||||
This file contains definitions for mqttio-OBJC backward compatibility
|
||||
|
||||
@author Christoph Krey c@ckrey.de
|
||||
@copyright Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
|
||||
based on Copyright (c) 2011, 2013, 2lemetry LLC
|
||||
All rights reserved. This program and the accompanying materials
|
||||
are made available under the terms of the Eclipse Public License v1.0
|
||||
which accompanies this distribution, and is available at
|
||||
http://www.eclipse.org/legal/epl-v10.html
|
||||
|
||||
@see http://mqtt.org
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MQTTSession.h"
|
||||
|
||||
@interface MQTTSession(Create)
|
||||
|
||||
/** initialises the MQTT session
|
||||
*
|
||||
* this constructor can specifies SSL securityPolicy. the default value of securityPolicy is nil(which do nothing).
|
||||
*
|
||||
* if SSL is enabled, by default it only evaluate server's certificates using CA infrastructure, and for most case, this type of check is enough.
|
||||
* However, if your app using security model which require pinning SSL certificates to helps prevent man-in-the-middle attacks
|
||||
* and other vulnerabilities. you may need to set securityPolicy to properly value(see MQTTSSLSecurityPolicy.h for more detail).
|
||||
*
|
||||
* NOTE: about self-signed server certificates:
|
||||
* In CA infrastructure, you may establish a SSL/TLS connection with server which using self-signed certificates
|
||||
* by install the certificates into OS keychain(either programmatically or manually). however, this method has some disadvantages:
|
||||
* 1. every socket you app created will trust certificates you added.
|
||||
* 2. if user choice to remove certificates from keychain, you app need to handling certificates re-adding.
|
||||
*
|
||||
* If you only want to verify the cert for the socket you are creating and for no other sockets in your app, you need to use
|
||||
* MQTTSSLSecurityPolicy.
|
||||
* And if you use self-signed server certificates, your need to set property: MQTTSSLSecurityPolicy.allowInvalidCertificates=YES
|
||||
* (see MQTTSSLSecurityPolicy.h for more detail).
|
||||
*
|
||||
* @param clientId The Client Identifier identifies the Client to the Server. If nil, a random clientId is generated.
|
||||
* @param userName an NSString object containing the user's name (or ID) for authentication. May be nil.
|
||||
* @param password an NSString object containing the user's password. If userName is nil, password must be nil as well.
|
||||
* @param keepAliveInterval The Keep Alive is a time interval measured in seconds. The MQTTClient ensures that the interval between Control Packets being sent does not exceed the Keep Alive value. In the absence of sending any other Control Packets, the Client sends a PINGREQ Packet.
|
||||
* @param cleanSessionFlag specifies if the server should discard previous session information.
|
||||
* @param willFlag If the Will Flag is set to YES this indicates that a Will Message MUST be published by the Server when the Server detects that the Client is disconnected for any reason other than the Client flowing a DISCONNECT Packet.
|
||||
* @param willTopic If the Will Flag is set to YES, the Will Topic is a string, nil otherwise.
|
||||
* @param willMsg If the Will Flag is set to YES the Will Message must be specified, nil otherwise.
|
||||
* @param willQoS specifies the QoS level to be used when publishing the Will Message. If the Will Flag is set to NO, then the Will QoS MUST be set to 0. If the Will Flag is set to YES, the value of Will QoS can be 0 (0x00), 1 (0x01), or 2 (0x02).
|
||||
* @param willRetainFlag indicates if the server should publish the Will Messages with retainFlag. If the Will Flag is set to NO, then the Will Retain Flag MUST be set to NO . If the Will Flag is set to YES: If Will Retain is set to NO, the Server MUST publish the Will Message as a non-retained publication [MQTT-3.1.2-14]. If Will Retain is set to YES, the Server MUST publish the Will Message as a retained publication [MQTT-3.1.2-15].
|
||||
* @param protocolLevel specifies the protocol to be used. The value of the Protocol Level field for the version 3.1.1 of the protocol is 4. The value for the version 3.1 is 3.
|
||||
* @param queue The queue where the streams are scheduled.
|
||||
* @param securityPolicy The security policy used to evaluate server trust for secure connections.
|
||||
* @param certificates An identity certificate used to reply to a server requiring client certificates according to the description given for SSLSetCertificate(). You may build the certificates array yourself or use the sundry method clientCertFromP12
|
||||
* @return the initialised MQTTSession object
|
||||
*
|
||||
* @code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
NSString* certificate = [[NSBundle bundleForClass:[MQTTSession class]] pathForResource:@"certificate" ofType:@"cer"];
|
||||
MQTTSSLSecurityPolicy *securityPolicy = [MQTTSSLSecurityPolicy policyWithPinningMode:MQTTSSLPinningModeCertificate];
|
||||
securityPolicy.pinnedCertificates = @[ [NSData dataWithContentsOfFile:certificate] ];
|
||||
securityPolicy.allowInvalidCertificates = YES; // if your certificate is self-signed(which didn't coupled with CA infrastructure)
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc]
|
||||
initWithClientId:@"example-1234"
|
||||
userName:@"user"
|
||||
password:@"secret"
|
||||
keepAlive:60
|
||||
cleanSession:YES
|
||||
will:YES
|
||||
willTopic:@"example/status"
|
||||
willMsg:[[@"Client off-line"] dataUsingEncoding:NSUTF8StringEncoding]
|
||||
willQoS:2
|
||||
willRetainFlag:YES
|
||||
protocolLevel:4
|
||||
queue:dispatch_queue_get_main_queue()
|
||||
securityPolicy:securityPolicy
|
||||
certificates:certificates];
|
||||
|
||||
[session connectToHost:@"example-1234" port:1883 usingSSL:YES];
|
||||
@endcode
|
||||
*/
|
||||
- (MQTTSession *)initWithClientId:(NSString *)clientId
|
||||
userName:(NSString *)userName
|
||||
password:(NSString *)password
|
||||
keepAlive:(UInt16)keepAliveInterval
|
||||
connectMessage:(MQTTMessage *)theConnectMessage
|
||||
cleanSession:(BOOL)cleanSessionFlag
|
||||
will:(BOOL)willFlag
|
||||
willTopic:(NSString *)willTopic
|
||||
willMsg:(NSData *)willMsg
|
||||
willQoS:(MQTTQosLevel)willQoS
|
||||
willRetainFlag:(BOOL)willRetainFlag
|
||||
protocolLevel:(UInt8)protocolLevel
|
||||
queue:(dispatch_queue_t)queue
|
||||
securityPolicy:(MQTTSSLSecurityPolicy *) securityPolicy
|
||||
certificates:(NSArray *)certificates;
|
||||
|
||||
/** for mqttio-OBJC backward compatibility
|
||||
@param payload JSON payload is converted to NSData and then send. See publishData for description
|
||||
@param theTopic see publishData for description
|
||||
*/
|
||||
- (void)publishJson:(id)payload onTopic:(NSString *)theTopic;
|
||||
|
||||
@end
|
||||
86
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionLegacy.m
generated
Normal file
86
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionLegacy.m
generated
Normal file
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// MQTTSessionLegacy.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
// based on
|
||||
//
|
||||
// Copyright (c) 2011, 2013, 2lemetry LLC
|
||||
//
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// which accompanies this distribution, and is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// Contributors:
|
||||
// Kyle Roche - initial API and implementation and/or initial documentation
|
||||
//
|
||||
|
||||
/**
|
||||
Using MQTT in your Objective-C application
|
||||
|
||||
This file contains implementation for mqttio-OBJC backward compatibility
|
||||
|
||||
@author Christoph Krey c@ckrey.de
|
||||
@see http://mqtt.org
|
||||
*/
|
||||
|
||||
#import "MQTTSession.h"
|
||||
#import "MQTTSessionLegacy.h"
|
||||
#import "MQTTCFSocketTransport.h"
|
||||
#import "MQTTSSLSecurityPolicyTransport.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTSession()
|
||||
@property (strong, nonatomic) MQTTSSLSecurityPolicy *securityPolicy;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTSession(Legacy)
|
||||
|
||||
- (MQTTSession *)initWithClientId:(NSString *)clientId
|
||||
userName:(NSString *)userName
|
||||
password:(NSString *)password
|
||||
keepAlive:(UInt16)keepAliveInterval
|
||||
connectMessage:(MQTTMessage *)theConnectMessage
|
||||
cleanSession:(BOOL)cleanSessionFlag
|
||||
will:(BOOL)willFlag
|
||||
willTopic:(NSString *)willTopic
|
||||
willMsg:(NSData *)willMsg
|
||||
willQoS:(MQTTQosLevel)willQoS
|
||||
willRetainFlag:(BOOL)willRetainFlag
|
||||
protocolLevel:(UInt8)protocolLevel
|
||||
queue:(dispatch_queue_t)queue
|
||||
securityPolicy:(MQTTSSLSecurityPolicy *)securityPolicy
|
||||
certificates:(NSArray *)certificates {
|
||||
DDLogVerbose(@"[MQTTSessionLegacy] initWithClientId:%@ ", clientId);
|
||||
|
||||
self = [self init];
|
||||
self.connectMessage = theConnectMessage;
|
||||
self.clientId = clientId;
|
||||
self.userName = userName;
|
||||
self.password = password;
|
||||
self.keepAliveInterval = keepAliveInterval;
|
||||
self.cleanSessionFlag = cleanSessionFlag;
|
||||
self.willFlag = willFlag;
|
||||
self.willTopic = willTopic;
|
||||
self.willMsg = willMsg;
|
||||
self.willQoS = willQoS;
|
||||
self.willRetainFlag = willRetainFlag;
|
||||
self.protocolLevel = protocolLevel;
|
||||
self.queue = queue;
|
||||
self.securityPolicy = securityPolicy;
|
||||
self.certificates = certificates;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)publishJson:(id)payload onTopic:(NSString*)theTopic {
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
|
||||
if (data) {
|
||||
[self publishData:data onTopic:theTopic retain:FALSE qos:MQTTQosLevelAtLeastOnce];
|
||||
}
|
||||
}
|
||||
@end
|
||||
237
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionManager.h
generated
Normal file
237
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionManager.h
generated
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// MQTTSessionManager.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 09.07.14.
|
||||
// Copyright © 2013-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#if TARGET_OS_IPHONE == 1
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
#import "MQTTSession.h"
|
||||
#import "MQTTSessionLegacy.h"
|
||||
#import "MQTTSSLSecurityPolicy.h"
|
||||
|
||||
@class MQTTSessionManager;
|
||||
|
||||
/** delegate gives your application access to received messages
|
||||
*/
|
||||
@protocol MQTTSessionManagerDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Enumeration of MQTTSessionManagerState values
|
||||
*/
|
||||
typedef NS_ENUM(int, MQTTSessionManagerState) {
|
||||
MQTTSessionManagerStateStarting,
|
||||
MQTTSessionManagerStateConnecting,
|
||||
MQTTSessionManagerStateError,
|
||||
MQTTSessionManagerStateConnected,
|
||||
MQTTSessionManagerStateClosing,
|
||||
MQTTSessionManagerStateClosed
|
||||
};
|
||||
|
||||
@optional
|
||||
|
||||
/** gets called when a new message was received
|
||||
|
||||
@param data the data received, might be zero length
|
||||
@param topic the topic the data was published to
|
||||
@param retained indicates if the data retransmitted from server storage
|
||||
*/
|
||||
- (void)handleMessage:(NSData *)data onTopic:(NSString *)topic retained:(BOOL)retained;
|
||||
|
||||
/** gets called when a new message was received
|
||||
@param sessionManager the instance of MQTTSessionManager whose state changed
|
||||
@param data the data received, might be zero length
|
||||
@param topic the topic the data was published to
|
||||
@param retained indicates if the data retransmitted from server storage
|
||||
*/
|
||||
- (void)sessionManager:(MQTTSessionManager *)sessionManager
|
||||
didReceiveMessage:(NSData *)data
|
||||
onTopic:(NSString *)topic
|
||||
retained:(BOOL)retained;
|
||||
|
||||
/** gets called when a published message was actually delivered
|
||||
@param msgID the Message Identifier of the delivered message
|
||||
@note this method is called after a publish with qos 1 or 2 only
|
||||
*/
|
||||
- (void)messageDelivered:(UInt16)msgID;
|
||||
|
||||
/** gets called when a published message was actually delivered
|
||||
@param sessionManager the instance of MQTTSessionManager whose state changed
|
||||
@param msgID the Message Identifier of the delivered message
|
||||
@note this method is called after a publish with qos 1 or 2 only
|
||||
*/
|
||||
- (void)sessionManager:(MQTTSessionManager *)sessionManager didDeliverMessage:(UInt16)msgID;
|
||||
|
||||
/** gets called when the connection status changes
|
||||
@param sessionManager the instance of MQTTSessionManager whose state changed
|
||||
@param newState the new connection state of the sessionManager. This will be identical to `sessionManager.state`.
|
||||
*/
|
||||
- (void)sessionManager:(MQTTSessionManager *)sessionManager didChangeState:(MQTTSessionManagerState)newState;
|
||||
|
||||
@end
|
||||
|
||||
/** SessionManager handles the MQTT session for your application
|
||||
*/
|
||||
@interface MQTTSessionManager : NSObject <MQTTSessionDelegate>
|
||||
|
||||
/** Underlying MQTTSession currently in use.
|
||||
*/
|
||||
@property (strong, nonatomic, readonly) MQTTSession *session;
|
||||
|
||||
/** host an NSString containing the hostName or IP address of the Server
|
||||
*/
|
||||
@property (readonly) NSString *host;
|
||||
|
||||
/** port an unsigned 32 bit integer containing the IP port number of the Server
|
||||
*/
|
||||
@property (readonly) UInt32 port;
|
||||
|
||||
/** the delegate receiving incoming messages
|
||||
*/
|
||||
@property (weak, nonatomic) id<MQTTSessionManagerDelegate> delegate;
|
||||
|
||||
/** indicates if manager requires tear down
|
||||
*/
|
||||
@property (readonly) BOOL requiresTearDown;
|
||||
|
||||
/** subscriptions is a dictionary of NSNumber instances indicating the MQTTQoSLevel.
|
||||
* The keys are topic filters.
|
||||
* The SessionManager subscribes to the given subscriptions after successfull (re-)connect
|
||||
* according to the cleansession parameter and the state of the session as indicated by the broker.
|
||||
* Setting a new subscriptions dictionary initiates SUBSCRIBE or UNSUBSCRIBE messages by SessionManager
|
||||
* by comparing the old and new subscriptions.
|
||||
*/
|
||||
@property (strong, nonatomic) NSDictionary<NSString *, NSNumber *> *subscriptions;
|
||||
|
||||
/** effectiveSubscriptions s a dictionary of NSNumber instances indicating the granted MQTTQoSLevel, or 0x80 for subscription failure.
|
||||
* The keys are topic filters.
|
||||
* effectiveSubscriptions is observable and is updated everytime subscriptions change
|
||||
* @code
|
||||
...
|
||||
MQTTSessionManager *manager = [[MQTTSessionManager alloc] init];
|
||||
manager.delegate = self;
|
||||
|
||||
[manager addObserver:self
|
||||
forKeyPath:@"effectiveSubscriptions"
|
||||
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
|
||||
context:nil];
|
||||
manager.subscriptions = [@{@"#": @(0)} mutableCopy];
|
||||
[manager connectTo: ...
|
||||
...
|
||||
[manager removeObserver:self forKeyPath:@"effectiveSubscriptions"];
|
||||
...
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary<NSString *,id> *)change
|
||||
context:(void *)context {
|
||||
if ([keyPath isEqualToString:@"effectiveSubscriptions"]) {
|
||||
MQTTSessionManager *manager = (MQTTSessionManager *)object;
|
||||
DDLogVerbose(@"effectiveSubscriptions changed: %@", manager.effectiveSubscriptions);
|
||||
}
|
||||
}
|
||||
* @endcode
|
||||
*/
|
||||
@property (readonly, strong, nonatomic) NSDictionary<NSString *, NSNumber *> *effectiveSubscriptions;
|
||||
|
||||
/** SessionManager status
|
||||
*/
|
||||
@property (nonatomic, readonly) MQTTSessionManagerState state;
|
||||
|
||||
/** SessionManager last error code when state equals MQTTSessionManagerStateError
|
||||
*/
|
||||
@property (nonatomic, readonly) NSError *lastErrorCode;
|
||||
|
||||
/** initWithPersistence sets the MQTTPersistence properties other than default
|
||||
* @param persistent YES or NO (default) to establish file or in memory persistence.
|
||||
* @param maxWindowSize (a positive number, default is 16) to control the number of messages sent before waiting for acknowledgement in Qos 1 or 2. Additional messages are stored and transmitted later.
|
||||
* @param maxSize (a positive number of bytes, default is 64 MB) to limit the size of the persistence file. Messages published after the limit is reached are dropped.
|
||||
* @param maxMessages (a positive number, default is 1024) to limit the number of messages stored. Additional messages published are dropped.
|
||||
* @param maxRetryInterval The duration at which the connection-retry timer should be capped. When MQTTSessionManager receives a ClosedByBroker or an Error
|
||||
event, it will attempt to reconnect to the broker. The time in between connection attempts is doubled each time, until it remains at maxRetryInterval.
|
||||
Defaults to 64 seconds.
|
||||
* @param connectInForeground Whether or not to connect the MQTTSession when the app enters the foreground, and disconnect when it becomes inactive. When NO, the caller is responsible for calling -connectTo: and -disconnect. Defaults to YES.
|
||||
* @param streamSSLLevel an NSString containing the security level for read and write streams
|
||||
* For list of possible values see:
|
||||
* https://developer.apple.com/documentation/corefoundation/cfstream/cfstream_socket_security_level_constants
|
||||
* Please also note that kCFStreamSocketSecurityLevelTLSv1_2 is not in a list
|
||||
* and cannot be used as constant, but you can use it as a string value
|
||||
* defaults to kCFStreamSocketSecurityLevelNegotiatedSSL
|
||||
* @param queue Queue for MQTTSession.
|
||||
* @return the initialized MQTTSessionManager object
|
||||
*/
|
||||
|
||||
- (MQTTSessionManager *)initWithPersistence:(BOOL)persistent
|
||||
maxWindowSize:(NSUInteger)maxWindowSize
|
||||
maxMessages:(NSUInteger)maxMessages
|
||||
maxSize:(NSUInteger)maxSize
|
||||
maxConnectionRetryInterval:(NSTimeInterval)maxRetryInterval
|
||||
connectInForeground:(BOOL)connectInForeground
|
||||
streamSSLLevel:(NSString *)streamSSLLevel
|
||||
queue:(dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** Connects to the MQTT broker and stores the parameters for subsequent reconnects
|
||||
* @param host specifies the hostname or ip address to connect to. Defaults to @"localhost".
|
||||
* @param port specifies the port to connect to
|
||||
* @param tls specifies whether to use SSL or not
|
||||
* @param keepalive The Keep Alive is a time interval measured in seconds. The MQTTClient ensures that the interval between Control Packets being sent does not exceed the Keep Alive value. In the absence of sending any other Control Packets, the Client sends a PINGREQ Packet.
|
||||
* @param clean specifies if the server should discard previous session information.
|
||||
* @param auth specifies the user and pass parameters should be used for authenthication
|
||||
* @param user an NSString object containing the user's name (or ID) for authentication. May be nil.
|
||||
* @param pass an NSString object containing the user's password. If userName is nil, password must be nil as well.
|
||||
* @param will indicates whether a will shall be sent
|
||||
* @param willTopic the Will Topic is a string, may be nil
|
||||
* @param willMsg the Will Message, might be zero length or nil
|
||||
* @param willQos specifies the QoS level to be used when publishing the Will Message.
|
||||
* @param willRetainFlag indicates if the server should publish the Will Messages with retainFlag.
|
||||
* @param clientId The Client Identifier identifies the Client to the Server. If nil, a random clientId is generated.
|
||||
* @param securityPolicy A custom SSL security policy or nil.
|
||||
* @param certificates An NSArray of the pinned certificates to use or nil.
|
||||
* @param protocolLevel Protocol version of the connection.
|
||||
* @param connectHandler Called when first connected or if error occurred. It is not called on subsequent internal reconnects.
|
||||
*/
|
||||
|
||||
- (void)connectTo:(NSString *)host
|
||||
port:(NSInteger)port
|
||||
tls:(BOOL)tls
|
||||
keepalive:(NSInteger)keepalive
|
||||
clean:(BOOL)clean
|
||||
auth:(BOOL)auth
|
||||
user:(NSString *)user
|
||||
pass:(NSString *)pass
|
||||
will:(BOOL)will
|
||||
willTopic:(NSString *)willTopic
|
||||
willMsg:(NSData *)willMsg
|
||||
willQos:(MQTTQosLevel)willQos
|
||||
willRetainFlag:(BOOL)willRetainFlag
|
||||
withClientId:(NSString *)clientId
|
||||
securityPolicy:(MQTTSSLSecurityPolicy *)securityPolicy
|
||||
certificates:(NSArray *)certificates
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
connectHandler:(MQTTConnectHandler)connectHandler;
|
||||
|
||||
/** Re-Connects to the MQTT broker using the parameters for given in the connectTo method
|
||||
*/
|
||||
- (void)connectToLast:(MQTTConnectHandler)connectHandler;
|
||||
|
||||
/** publishes data on a given topic at a specified QoS level and retain flag
|
||||
|
||||
@param data the data to be sent. length may range from 0 to 268,435,455 - 4 - _lengthof-topic_ bytes. Defaults to length 0.
|
||||
@param topic the Topic to identify the data
|
||||
@param retainFlag if YES, data is stored on the MQTT broker until overwritten by the next publish with retainFlag = YES
|
||||
@param qos specifies the Quality of Service for the publish
|
||||
qos can be 0, 1, or 2.
|
||||
@return the Message Identifier of the PUBLISH message. Zero if qos 0. If qos 1 or 2, zero if message was dropped
|
||||
@note returns immediately.
|
||||
*/
|
||||
- (UInt16)sendData:(NSData *)data topic:(NSString *)topic qos:(MQTTQosLevel)qos retain:(BOOL)retainFlag;
|
||||
|
||||
/** Disconnects gracefully from the MQTT broker
|
||||
*/
|
||||
- (void)disconnectWithDisconnectHandler:(MQTTDisconnectHandler)disconnectHandler;
|
||||
|
||||
@end
|
||||
420
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionManager.m
generated
Normal file
420
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionManager.m
generated
Normal file
@@ -0,0 +1,420 @@
|
||||
//
|
||||
// MQTTSessionManager.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 09.07.14.
|
||||
// Copyright © 2013-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTSessionManager.h"
|
||||
#import "MQTTCoreDataPersistence.h"
|
||||
#import "MQTTLog.h"
|
||||
#import "ReconnectTimer.h"
|
||||
#import "ForegroundReconnection.h"
|
||||
#import "MQTTSSLSecurityPolicyTransport.h"
|
||||
|
||||
@interface MQTTSessionManager()
|
||||
|
||||
@property (nonatomic, readwrite) MQTTSessionManagerState state;
|
||||
@property (nonatomic, readwrite) NSError *lastErrorCode;
|
||||
|
||||
@property (strong, nonatomic) ReconnectTimer *reconnectTimer;
|
||||
@property (nonatomic) BOOL reconnectFlag;
|
||||
|
||||
@property (strong, nonatomic) MQTTSession *session;
|
||||
|
||||
@property (strong, nonatomic) NSString *host;
|
||||
@property (nonatomic) UInt32 port;
|
||||
@property (nonatomic) BOOL tls;
|
||||
@property (nonatomic) NSInteger keepalive;
|
||||
@property (nonatomic) BOOL clean;
|
||||
@property (nonatomic) BOOL auth;
|
||||
@property (nonatomic) BOOL will;
|
||||
@property (strong, nonatomic) NSString *user;
|
||||
@property (strong, nonatomic) NSString *pass;
|
||||
@property (strong, nonatomic) NSString *willTopic;
|
||||
@property (strong, nonatomic) NSData *willMsg;
|
||||
@property (nonatomic) NSInteger willQos;
|
||||
@property (nonatomic) BOOL willRetainFlag;
|
||||
@property (strong, nonatomic) NSString *clientId;
|
||||
@property (strong, nonatomic) dispatch_queue_t queue;
|
||||
@property (strong, nonatomic) MQTTSSLSecurityPolicy *securityPolicy;
|
||||
@property (strong, nonatomic) NSArray *certificates;
|
||||
@property (nonatomic) MQTTProtocolVersion protocolLevel;
|
||||
|
||||
#if TARGET_OS_IPHONE == 1
|
||||
@property (strong, nonatomic) ForegroundReconnection *foregroundReconnection;
|
||||
#endif
|
||||
|
||||
@property (nonatomic) BOOL persistent;
|
||||
@property (nonatomic) NSUInteger maxWindowSize;
|
||||
@property (nonatomic) NSUInteger maxSize;
|
||||
@property (nonatomic) NSUInteger maxMessages;
|
||||
@property (strong, nonatomic) NSString *streamSSLLevel;
|
||||
|
||||
@property (strong, nonatomic) NSDictionary<NSString *, NSNumber *> *internalSubscriptions;
|
||||
@property (strong, nonatomic) NSDictionary<NSString *, NSNumber *> *effectiveSubscriptions;
|
||||
@property (strong, nonatomic) NSLock *subscriptionLock;
|
||||
|
||||
@end
|
||||
|
||||
#define RECONNECT_TIMER 1.0
|
||||
#define RECONNECT_TIMER_MAX_DEFAULT 64.0
|
||||
|
||||
@implementation MQTTSessionManager
|
||||
|
||||
- (instancetype)init {
|
||||
self = [self initWithPersistence:MQTT_PERSISTENT
|
||||
maxWindowSize:MQTT_MAX_WINDOW_SIZE
|
||||
maxMessages:MQTT_MAX_MESSAGES
|
||||
maxSize:MQTT_MAX_SIZE
|
||||
maxConnectionRetryInterval:RECONNECT_TIMER_MAX_DEFAULT
|
||||
connectInForeground:YES
|
||||
streamSSLLevel:(NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL
|
||||
queue:dispatch_get_main_queue()];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (MQTTSessionManager *)initWithPersistence:(BOOL)persistent
|
||||
maxWindowSize:(NSUInteger)maxWindowSize
|
||||
maxMessages:(NSUInteger)maxMessages
|
||||
maxSize:(NSUInteger)maxSize
|
||||
maxConnectionRetryInterval:(NSTimeInterval)maxRetryInterval
|
||||
connectInForeground:(BOOL)connectInForeground
|
||||
streamSSLLevel:(NSString *)streamSSLLevel
|
||||
queue:(dispatch_queue_t)queue {
|
||||
self = [super init];
|
||||
self.streamSSLLevel = streamSSLLevel;
|
||||
self.queue = queue;
|
||||
[self updateState:MQTTSessionManagerStateStarting];
|
||||
self.internalSubscriptions = [[NSMutableDictionary alloc] init];
|
||||
self.effectiveSubscriptions = [[NSMutableDictionary alloc] init];
|
||||
self.persistent = persistent;
|
||||
self.maxWindowSize = maxWindowSize;
|
||||
self.maxSize = maxSize;
|
||||
self.maxMessages = maxMessages;
|
||||
|
||||
__weak MQTTSessionManager *weakSelf = self;
|
||||
self.reconnectTimer = [[ReconnectTimer alloc] initWithRetryInterval:RECONNECT_TIMER
|
||||
maxRetryInterval:maxRetryInterval
|
||||
queue:self.queue
|
||||
reconnectBlock:^{
|
||||
[weakSelf reconnect:nil];
|
||||
}];
|
||||
#if TARGET_OS_IPHONE == 1
|
||||
if (connectInForeground) {
|
||||
self.foregroundReconnection = [[ForegroundReconnection alloc] initWithMQTTSessionManager:self];
|
||||
}
|
||||
#endif
|
||||
self.subscriptionLock = [[NSLock alloc] init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)connectTo:(NSString *)host
|
||||
port:(NSInteger)port
|
||||
tls:(BOOL)tls
|
||||
keepalive:(NSInteger)keepalive
|
||||
clean:(BOOL)clean
|
||||
auth:(BOOL)auth
|
||||
user:(NSString *)user
|
||||
pass:(NSString *)pass
|
||||
will:(BOOL)will
|
||||
willTopic:(NSString *)willTopic
|
||||
willMsg:(NSData *)willMsg
|
||||
willQos:(MQTTQosLevel)willQos
|
||||
willRetainFlag:(BOOL)willRetainFlag
|
||||
withClientId:(NSString *)clientId
|
||||
securityPolicy:(MQTTSSLSecurityPolicy *)securityPolicy
|
||||
certificates:(NSArray *)certificates
|
||||
protocolLevel:(MQTTProtocolVersion)protocolLevel
|
||||
connectHandler:(MQTTConnectHandler)connectHandler {
|
||||
DDLogVerbose(@"MQTTSessionManager connectTo:%@", host);
|
||||
BOOL shouldReconnect = self.session != nil;
|
||||
if (!self.session ||
|
||||
![host isEqualToString:self.host] ||
|
||||
port != self.port ||
|
||||
tls != self.tls ||
|
||||
keepalive != self.keepalive ||
|
||||
clean != self.clean ||
|
||||
auth != self.auth ||
|
||||
![user isEqualToString:self.user] ||
|
||||
![pass isEqualToString:self.pass] ||
|
||||
![willTopic isEqualToString:self.willTopic] ||
|
||||
![willMsg isEqualToData:self.willMsg] ||
|
||||
willQos != self.willQos ||
|
||||
willRetainFlag != self.willRetainFlag ||
|
||||
![clientId isEqualToString:self.clientId] ||
|
||||
securityPolicy != self.securityPolicy ||
|
||||
certificates != self.certificates) {
|
||||
self.host = host;
|
||||
self.port = (int)port;
|
||||
self.tls = tls;
|
||||
self.keepalive = keepalive;
|
||||
self.clean = clean;
|
||||
self.auth = auth;
|
||||
self.user = user;
|
||||
self.pass = pass;
|
||||
self.will = will;
|
||||
self.willTopic = willTopic;
|
||||
self.willMsg = willMsg;
|
||||
self.willQos = willQos;
|
||||
self.willRetainFlag = willRetainFlag;
|
||||
self.clientId = clientId;
|
||||
self.securityPolicy = securityPolicy;
|
||||
self.certificates = certificates;
|
||||
self.protocolLevel = protocolLevel;
|
||||
|
||||
self.session = [[MQTTSession alloc] initWithClientId:clientId
|
||||
userName:auth ? user : nil
|
||||
password:auth ? pass : nil
|
||||
keepAlive:keepalive
|
||||
connectMessage:nil
|
||||
cleanSession:clean
|
||||
will:will
|
||||
willTopic:willTopic
|
||||
willMsg:willMsg
|
||||
willQoS:willQos
|
||||
willRetainFlag:willRetainFlag
|
||||
protocolLevel:protocolLevel
|
||||
queue:self.queue
|
||||
securityPolicy:securityPolicy
|
||||
certificates:certificates];
|
||||
self.session.streamSSLLevel = self.streamSSLLevel;
|
||||
MQTTCoreDataPersistence *persistence = [[MQTTCoreDataPersistence alloc] init];
|
||||
|
||||
persistence.persistent = self.persistent;
|
||||
persistence.maxWindowSize = self.maxWindowSize;
|
||||
persistence.maxSize = self.maxSize;
|
||||
persistence.maxMessages = self.maxMessages;
|
||||
|
||||
self.session.persistence = persistence;
|
||||
|
||||
self.session.delegate = self;
|
||||
self.reconnectFlag = FALSE;
|
||||
}
|
||||
if (shouldReconnect) {
|
||||
DDLogVerbose(@"[MQTTSessionManager] reconnecting");
|
||||
[self disconnectWithDisconnectHandler:nil];
|
||||
[self reconnect:connectHandler];
|
||||
} else {
|
||||
DDLogVerbose(@"[MQTTSessionManager] connecting");
|
||||
[self connectToInternal:connectHandler];
|
||||
}
|
||||
}
|
||||
|
||||
- (UInt16)sendData:(NSData *)data topic:(NSString *)topic qos:(MQTTQosLevel)qos retain:(BOOL)retainFlag {
|
||||
if (self.state != MQTTSessionManagerStateConnected) {
|
||||
[self connectToLast:nil];
|
||||
}
|
||||
UInt16 msgId = [self.session publishData:data
|
||||
onTopic:topic
|
||||
retain:retainFlag
|
||||
qos:qos];
|
||||
return msgId;
|
||||
}
|
||||
|
||||
- (void)disconnectWithDisconnectHandler:(MQTTDisconnectHandler)disconnectHandler {
|
||||
[self updateState:MQTTSessionManagerStateClosing];
|
||||
[self.session closeWithDisconnectHandler:disconnectHandler];
|
||||
[self.reconnectTimer stop];
|
||||
}
|
||||
|
||||
- (BOOL)requiresTearDown {
|
||||
return (self.state != MQTTSessionManagerStateClosed &&
|
||||
self.state != MQTTSessionManagerStateStarting);
|
||||
}
|
||||
|
||||
- (void)updateState:(MQTTSessionManagerState)newState {
|
||||
self.state = newState;
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(sessionManager:didChangeState:)]) {
|
||||
[self.delegate sessionManager:self didChangeState:newState];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - MQTT Callback methods
|
||||
|
||||
- (void)handleEvent:(MQTTSession *)session event:(MQTTSessionEvent)eventCode error:(NSError *)error {
|
||||
#ifdef DEBUG
|
||||
__unused const NSDictionary *events = @{
|
||||
@(MQTTSessionEventConnected): @"connected",
|
||||
@(MQTTSessionEventConnectionRefused): @"connection refused",
|
||||
@(MQTTSessionEventConnectionClosed): @"connection closed",
|
||||
@(MQTTSessionEventConnectionError): @"connection error",
|
||||
@(MQTTSessionEventProtocolError): @"protocoll error",
|
||||
@(MQTTSessionEventConnectionClosedByBroker): @"connection closed by broker"
|
||||
};
|
||||
DDLogVerbose(@"[MQTTSessionManager] eventCode: %@ (%ld) %@", events[@(eventCode)], (long)eventCode, error);
|
||||
#endif
|
||||
switch (eventCode) {
|
||||
case MQTTSessionEventConnected:
|
||||
self.lastErrorCode = nil;
|
||||
[self updateState:MQTTSessionManagerStateConnected];
|
||||
[self.reconnectTimer resetRetryInterval];
|
||||
break;
|
||||
|
||||
case MQTTSessionEventConnectionClosed:
|
||||
[self updateState:MQTTSessionManagerStateClosed];
|
||||
break;
|
||||
|
||||
case MQTTSessionEventConnectionClosedByBroker:
|
||||
if (self.state != MQTTSessionManagerStateClosing) {
|
||||
[self triggerDelayedReconnect];
|
||||
}
|
||||
[self updateState:MQTTSessionManagerStateClosed];
|
||||
break;
|
||||
|
||||
case MQTTSessionEventProtocolError:
|
||||
case MQTTSessionEventConnectionRefused:
|
||||
case MQTTSessionEventConnectionError:
|
||||
[self triggerDelayedReconnect];
|
||||
self.lastErrorCode = error;
|
||||
[self updateState:MQTTSessionManagerStateError];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)newMessage:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid {
|
||||
if (self.delegate) {
|
||||
if ([self.delegate respondsToSelector:@selector(sessionManager:didReceiveMessage:onTopic:retained:)]) {
|
||||
[self.delegate sessionManager:self didReceiveMessage:data onTopic:topic retained:retained];
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(handleMessage:onTopic:retained:)]) {
|
||||
[self.delegate handleMessage:data onTopic:topic retained:retained];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connected:(MQTTSession *)session sessionPresent:(BOOL)sessionPresent {
|
||||
if (self.clean || !self.reconnectFlag || !sessionPresent) {
|
||||
NSDictionary *subscriptions = [self.internalSubscriptions copy];
|
||||
[self.subscriptionLock lock];
|
||||
self.effectiveSubscriptions = [[NSMutableDictionary alloc] init];
|
||||
[self.subscriptionLock unlock];
|
||||
if (subscriptions.count) {
|
||||
__weak MQTTSessionManager *weakSelf = self;
|
||||
[self.session subscribeToTopics:subscriptions subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss) {
|
||||
MQTTSessionManager *strongSelf = weakSelf;
|
||||
if (!error) {
|
||||
NSArray<NSString *> *allTopics = subscriptions.allKeys;
|
||||
for (int i = 0; i < allTopics.count; i++) {
|
||||
NSString *topic = allTopics[i];
|
||||
NSNumber *gQos = gQoss[i];
|
||||
[strongSelf.subscriptionLock lock];
|
||||
NSMutableDictionary *newEffectiveSubscriptions = [strongSelf.subscriptions mutableCopy];
|
||||
newEffectiveSubscriptions[topic] = gQos;
|
||||
strongSelf.effectiveSubscriptions = newEffectiveSubscriptions;
|
||||
[strongSelf.subscriptionLock unlock];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
self.reconnectFlag = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)messageDelivered:(MQTTSession *)session msgID:(UInt16)msgID {
|
||||
if (self.delegate) {
|
||||
if ([self.delegate respondsToSelector:@selector(sessionManager:didDeliverMessage:)]) {
|
||||
[self.delegate sessionManager:self didDeliverMessage:msgID];
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(messageDelivered:)]) {
|
||||
[self.delegate messageDelivered:msgID];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)connectToInternal:(MQTTConnectHandler)connectHandler {
|
||||
if (self.session && self.state == MQTTSessionManagerStateStarting) {
|
||||
[self updateState:MQTTSessionManagerStateConnecting];
|
||||
MQTTCFSocketTransport *transport;
|
||||
if (self.securityPolicy) {
|
||||
transport = [[MQTTSSLSecurityPolicyTransport alloc] init];
|
||||
((MQTTSSLSecurityPolicyTransport *)transport).securityPolicy = self.securityPolicy;
|
||||
} else {
|
||||
transport = [[MQTTCFSocketTransport alloc] init];
|
||||
}
|
||||
transport.host = self.host;
|
||||
transport.port = self.port;
|
||||
transport.tls = self.tls;
|
||||
transport.certificates = self.certificates;
|
||||
transport.voip = self.session.voip;
|
||||
transport.queue = self.queue;
|
||||
transport.streamSSLLevel = self.streamSSLLevel;
|
||||
self.session.transport = transport;
|
||||
[self.session connectWithConnectHandler:connectHandler];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reconnect:(MQTTConnectHandler)connectHandler {
|
||||
[self updateState:MQTTSessionManagerStateStarting];
|
||||
[self connectToInternal:connectHandler];
|
||||
}
|
||||
|
||||
- (void)connectToLast:(MQTTConnectHandler)connectHandler {
|
||||
if (self.state == MQTTSessionManagerStateConnected) {
|
||||
return;
|
||||
}
|
||||
[self.reconnectTimer resetRetryInterval];
|
||||
[self reconnect:connectHandler];
|
||||
}
|
||||
|
||||
- (void)triggerDelayedReconnect {
|
||||
[self.reconnectTimer schedule];
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, NSNumber *> *)subscriptions {
|
||||
return self.internalSubscriptions;
|
||||
}
|
||||
|
||||
- (void)setSubscriptions:(NSDictionary<NSString *, NSNumber *> *)newSubscriptions {
|
||||
if (self.state == MQTTSessionManagerStateConnected) {
|
||||
NSDictionary *currentSubscriptions = [self.effectiveSubscriptions copy];
|
||||
|
||||
for (NSString *topicFilter in currentSubscriptions) {
|
||||
if (!newSubscriptions[topicFilter]) {
|
||||
__weak MQTTSessionManager *weakSelf = self;
|
||||
[self.session unsubscribeTopic:topicFilter unsubscribeHandler:^(NSError *error) {
|
||||
MQTTSessionManager *strongSelf = weakSelf;
|
||||
if (!error) {
|
||||
[strongSelf.subscriptionLock lock];
|
||||
NSMutableDictionary *newEffectiveSubscriptions = [strongSelf.subscriptions mutableCopy];
|
||||
[newEffectiveSubscriptions removeObjectForKey:topicFilter];
|
||||
strongSelf.effectiveSubscriptions = newEffectiveSubscriptions;
|
||||
[strongSelf.subscriptionLock unlock];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
for (NSString *topicFilter in newSubscriptions) {
|
||||
if (!currentSubscriptions[topicFilter]) {
|
||||
NSNumber *number = newSubscriptions[topicFilter];
|
||||
MQTTQosLevel qos = number.unsignedIntValue;
|
||||
__weak MQTTSessionManager *weakSelf = self;
|
||||
[self.session subscribeToTopic:topicFilter atLevel:qos subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss) {
|
||||
MQTTSessionManager *strongSelf = weakSelf;
|
||||
if (!error) {
|
||||
NSNumber *gQos = gQoss[0];
|
||||
[strongSelf.subscriptionLock lock];
|
||||
NSMutableDictionary *newEffectiveSubscriptions = [strongSelf.subscriptions mutableCopy];
|
||||
newEffectiveSubscriptions[topicFilter] = gQos;
|
||||
strongSelf.effectiveSubscriptions = newEffectiveSubscriptions;
|
||||
[strongSelf.subscriptionLock unlock];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
self.internalSubscriptions = newSubscriptions;
|
||||
DDLogVerbose(@"MQTTSessionManager internalSubscriptions: %@", self.internalSubscriptions);
|
||||
}
|
||||
|
||||
@end
|
||||
194
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionSynchron.h
generated
Normal file
194
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionSynchron.h
generated
Normal file
@@ -0,0 +1,194 @@
|
||||
//
|
||||
// MQTTSessionSynchron.h
|
||||
// MQTTClient.framework
|
||||
//
|
||||
|
||||
/**
|
||||
Synchronous API
|
||||
|
||||
@author Christoph Krey c@ckrey.de
|
||||
@copyright Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MQTTSession.h"
|
||||
|
||||
@interface MQTTSession(Synchron)
|
||||
|
||||
/** connects to the specified MQTT server synchronously
|
||||
|
||||
@param timeout defines the maximum time to wait. Defaults to 0 for no timeout.
|
||||
|
||||
@return true if the connection was established
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
|
||||
[session connectAndWaitTimeout:30];
|
||||
@endcode
|
||||
|
||||
*/
|
||||
- (BOOL)connectAndWaitTimeout:(NSTimeInterval)timeout;
|
||||
|
||||
/** subscribes to a topic at a specific QoS level synchronously
|
||||
|
||||
@param topic the Topic Filter to subscribe to.
|
||||
|
||||
@param qosLevel specifies the QoS Level of the subscription.
|
||||
qosLevel can be 0, 1, or 2.
|
||||
@param timeout defines the maximum time to wait
|
||||
|
||||
@return TRUE if successfully subscribed
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
|
||||
[session connectAndWaitTimeout:30];
|
||||
|
||||
[session subscribeAndWaitToTopic:@"example/#" atLevel:2 timeout:10];
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
||||
- (BOOL)subscribeAndWaitToTopic:(NSString *)topic
|
||||
atLevel:(MQTTQosLevel)qosLevel
|
||||
timeout:(NSTimeInterval)timeout;
|
||||
|
||||
/** subscribes a number of topics
|
||||
|
||||
@param topics an NSDictionary<NSString *, NSNumber *> containing the Topic Filters to subscribe to as keys and
|
||||
the corresponding QoS as NSNumber values
|
||||
@param timeout defines the maximum time to wait
|
||||
|
||||
@return TRUE if the subscribe was succesfull
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
|
||||
[session connectAndWaitTimeout:30];
|
||||
|
||||
[session subscribeAndWaitToTopics:@{
|
||||
@"example/#": @(0),
|
||||
@"example/status": @(2),
|
||||
@"other/#": @(1)
|
||||
}
|
||||
timeout:10];
|
||||
|
||||
@endcode
|
||||
*/
|
||||
- (BOOL)subscribeAndWaitToTopics:(NSDictionary<NSString *, NSNumber *> *)topics
|
||||
timeout:(NSTimeInterval)timeout;
|
||||
|
||||
/** unsubscribes from a topic synchronously
|
||||
|
||||
@param topic the Topic Filter to unsubscribe from.
|
||||
@param timeout defines the maximum time to wait
|
||||
|
||||
@return TRUE if sucessfully unsubscribed
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
|
||||
[session connectAndWaitTimeout:30];
|
||||
|
||||
...
|
||||
|
||||
[session unsubscribeAndWaitTopic:@"example/#" timeout:10];
|
||||
|
||||
@endcode
|
||||
*/
|
||||
- (BOOL)unsubscribeAndWaitTopic:(NSString *)topic
|
||||
timeout:(NSTimeInterval)timeout;
|
||||
|
||||
/** unsubscribes from a number of topics synchronously
|
||||
|
||||
@param topics an NSArray<NSString *> of topics to unsubscribe from
|
||||
@param timeout defines the maximum time to wait
|
||||
|
||||
@return TRUE if the unsubscribe was successful
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
|
||||
[session connectAndWaitTimeout:30];
|
||||
|
||||
...
|
||||
|
||||
[session unsubscribeAndWaitTopics:@[
|
||||
@"example/#",
|
||||
@"example/status",
|
||||
@"other/#"
|
||||
]
|
||||
timeout:10];
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
||||
- (BOOL)unsubscribeAndWaitTopics:(NSArray<NSString *> *)topics
|
||||
timeout:(NSTimeInterval)timeout;
|
||||
|
||||
/** publishes synchronously data on a given topic at a specified QoS level and retain flag
|
||||
|
||||
@param data the data to be sent. length may range from 0 to 268,435,455 - 4 - _lengthof-topic_ bytes. Defaults to length 0.
|
||||
@param topic the Topic to identify the data
|
||||
@param retainFlag if YES, data is stored on the MQTT broker until overwritten by the next publish with retainFlag = YES
|
||||
@param qos specifies the Quality of Service for the publish
|
||||
qos can be 0, 1, or 2.
|
||||
@param timeout defines the maximum time to wait
|
||||
@returns TRUE if the publish was successful
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
|
||||
[session connectAndWaitTimeout:30];
|
||||
|
||||
[session publishAndWaitData:[@"Sample Data" dataUsingEncoding:NSUTF8StringEncoding]
|
||||
topic:@"example/data"
|
||||
retain:YES
|
||||
qos:1
|
||||
timeout:10];
|
||||
@endcode
|
||||
|
||||
*/
|
||||
- (BOOL)publishAndWaitData:(NSData *)data
|
||||
onTopic:(NSString *)topic
|
||||
retain:(BOOL)retainFlag
|
||||
qos:(MQTTQosLevel)qos
|
||||
timeout:(NSTimeInterval)timeout;
|
||||
|
||||
/** closes an MQTTSession gracefully synchronously
|
||||
@param timeout defines the maximum time to wait
|
||||
|
||||
If the connection was successfully established before, a DISCONNECT is sent.
|
||||
|
||||
@code
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
|
||||
[session connectAndWaitTimeout:30];
|
||||
|
||||
...
|
||||
|
||||
[session closeAndWait:10];
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
||||
- (void)closeAndWait:(NSTimeInterval)timeout;
|
||||
|
||||
@end
|
||||
192
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionSynchron.m
generated
Normal file
192
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTSessionSynchron.m
generated
Normal file
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// MQTTSessionSynchron.m
|
||||
// MQTTClient.framework
|
||||
//
|
||||
// Copyright © 2013-2017, Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
/**
|
||||
Synchronous API
|
||||
|
||||
@author Christoph Krey c@ckrey.de
|
||||
@see http://mqtt.org
|
||||
*/
|
||||
|
||||
#import "MQTTSession.h"
|
||||
#import "MQTTSessionLegacy.h"
|
||||
#import "MQTTSessionSynchron.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@interface MQTTSession()
|
||||
@property (nonatomic) BOOL synchronPub;
|
||||
@property (nonatomic) UInt16 synchronPubMid;
|
||||
@property (nonatomic) BOOL synchronUnsub;
|
||||
@property (nonatomic) UInt16 synchronUnsubMid;
|
||||
@property (nonatomic) BOOL synchronSub;
|
||||
@property (nonatomic) UInt16 synchronSubMid;
|
||||
@property (nonatomic) BOOL synchronConnect;
|
||||
@property (nonatomic) BOOL synchronDisconnect;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MQTTSession(Synchron)
|
||||
|
||||
/** Synchron connect
|
||||
*
|
||||
*/
|
||||
- (BOOL)connectAndWaitTimeout:(NSTimeInterval)timeout {
|
||||
NSDate *started = [NSDate date];
|
||||
self.synchronConnect = TRUE;
|
||||
|
||||
[self connect];
|
||||
|
||||
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
|
||||
|
||||
while (self.synchronConnect && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] waiting for connect");
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
|
||||
}
|
||||
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] end connect");
|
||||
|
||||
return (self.status == MQTTSessionStatusConnected);
|
||||
}
|
||||
|
||||
- (BOOL)subscribeAndWaitToTopic:(NSString *)topic atLevel:(MQTTQosLevel)qosLevel timeout:(NSTimeInterval)timeout {
|
||||
NSDate *started = [NSDate date];
|
||||
self.synchronSub = TRUE;
|
||||
UInt16 mid = [self subscribeToTopic:topic atLevel:qosLevel];
|
||||
self.synchronSubMid = mid;
|
||||
|
||||
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
|
||||
|
||||
while (self.synchronSub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] waiting for suback %d", mid);
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
|
||||
}
|
||||
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] end subscribe");
|
||||
|
||||
if (self.synchronSub || self.synchronSubMid != mid) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)subscribeAndWaitToTopics:(NSDictionary<NSString *, NSNumber *> *)topics timeout:(NSTimeInterval)timeout {
|
||||
NSDate *started = [NSDate date];
|
||||
self.synchronSub = TRUE;
|
||||
UInt16 mid = [self subscribeToTopics:topics];
|
||||
self.synchronSubMid = mid;
|
||||
|
||||
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
|
||||
|
||||
while (self.synchronSub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] waiting for suback %d", mid);
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
|
||||
}
|
||||
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] end subscribe");
|
||||
|
||||
if (self.synchronSub || self.synchronSubMid != mid) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)unsubscribeAndWaitTopic:(NSString *)theTopic timeout:(NSTimeInterval)timeout {
|
||||
NSDate *started = [NSDate date];
|
||||
|
||||
self.synchronUnsub = TRUE;
|
||||
UInt16 mid = [self unsubscribeTopic:theTopic];
|
||||
self.synchronUnsubMid = mid;
|
||||
|
||||
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
|
||||
|
||||
while (self.synchronUnsub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] waiting for unsuback %d", mid);
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
|
||||
}
|
||||
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] end unsubscribe");
|
||||
|
||||
if (self.synchronUnsub || self.synchronUnsubMid != mid) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)unsubscribeAndWaitTopics:(NSArray<NSString *> *)topics timeout:(NSTimeInterval)timeout {
|
||||
NSDate *started = [NSDate date];
|
||||
self.synchronUnsub = TRUE;
|
||||
UInt16 mid = [self unsubscribeTopics:topics];
|
||||
self.synchronUnsubMid = mid;
|
||||
|
||||
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
|
||||
|
||||
while (self.synchronUnsub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] waiting for unsuback %d", mid);
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
|
||||
}
|
||||
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] end unsubscribe");
|
||||
|
||||
if (self.synchronUnsub || self.synchronUnsubMid != mid) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)publishAndWaitData:(NSData*)data
|
||||
onTopic:(NSString*)topic
|
||||
retain:(BOOL)retainFlag
|
||||
qos:(MQTTQosLevel)qos
|
||||
timeout:(NSTimeInterval)timeout {
|
||||
NSDate *started = [NSDate date];
|
||||
|
||||
if (qos != MQTTQosLevelAtMostOnce) {
|
||||
self.synchronPub = TRUE;
|
||||
}
|
||||
|
||||
UInt16 mid = self.synchronPubMid = [self publishData:data onTopic:topic retain:retainFlag qos:qos];
|
||||
if (qos == MQTTQosLevelAtMostOnce) {
|
||||
return TRUE;
|
||||
} else {
|
||||
|
||||
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
|
||||
|
||||
while (self.synchronPub && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] waiting for mid %d", mid);
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
|
||||
}
|
||||
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] end publish");
|
||||
|
||||
if (self.synchronPub || self.synchronPubMid != mid) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeAndWait:(NSTimeInterval)timeout {
|
||||
NSDate *started = [NSDate date];
|
||||
self.synchronDisconnect = TRUE;
|
||||
[self closeWithDisconnectHandler:nil];
|
||||
|
||||
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
|
||||
|
||||
while (self.synchronDisconnect && (timeout == 0 || started.timeIntervalSince1970 + timeout > [NSDate date].timeIntervalSince1970)) {
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] waiting for close");
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
|
||||
}
|
||||
DDLogVerbose(@"[MQTTSessionSynchron] end close");
|
||||
}
|
||||
|
||||
@end
|
||||
33
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTStrict.h
generated
Normal file
33
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTStrict.h
generated
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// MQTTStrict.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 24.07.17.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/** MQTTStrict controls the behaviour of MQTTClient with regards to parameter checking
|
||||
* If strict is true, all parameters passed by the caller are checked before
|
||||
* the corresponding message is send (CONNECT, PUBLISH, SUBSCRIBE, UNSUBSCRIBE)
|
||||
* and an exception is thrown if any invalid values or inconsistencies are detected
|
||||
*
|
||||
* If strict is false, parameters are used as passed by the caller.
|
||||
* Messages will be sent "incorrectly" and
|
||||
* parameter checking will be done on the broker end.
|
||||
*
|
||||
*/
|
||||
@interface MQTTStrict : NSObject
|
||||
|
||||
/** strict returns the current strict flag
|
||||
* @return the strict flag
|
||||
*/
|
||||
+ (BOOL)strict;
|
||||
|
||||
/** setString sets the global strict flag
|
||||
* @param strict the new strict flag
|
||||
*/
|
||||
+ (void)setStrict:(BOOL)strict;
|
||||
|
||||
@end
|
||||
22
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTStrict.m
generated
Normal file
22
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTStrict.m
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MQTTStrict.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 24.07.17.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTStrict.h"
|
||||
|
||||
@implementation MQTTStrict
|
||||
static BOOL internalStrict = false;
|
||||
|
||||
+ (BOOL)strict {
|
||||
return internalStrict;
|
||||
}
|
||||
|
||||
+ (void)setStrict:(BOOL)strict {
|
||||
internalStrict = strict;
|
||||
}
|
||||
|
||||
@end
|
||||
114
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTTransport.h
generated
Normal file
114
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTTransport.h
generated
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// MQTTTransport.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 06.12.15.
|
||||
// Copyright © 2015-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol MQTTTransportDelegate;
|
||||
|
||||
/** MQTTTransport is a protocol abstracting the underlying transport level for MQTTClient
|
||||
*
|
||||
*/
|
||||
@protocol MQTTTransport <NSObject>
|
||||
|
||||
/** MQTTTransport state defines the possible state of an abstract transport
|
||||
*
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, MQTTTransportState) {
|
||||
|
||||
/** MQTTTransportCreated indicates an initialized transport */
|
||||
MQTTTransportCreated = 0,
|
||||
|
||||
/** MQTTTransportOpening indicates a transport in the process of opening a connection */
|
||||
MQTTTransportOpening,
|
||||
|
||||
/** MQTTTransportCreated indicates a transport opened ready for communication */
|
||||
MQTTTransportOpen,
|
||||
|
||||
/** MQTTTransportCreated indicates a transport in the process of closing */
|
||||
MQTTTransportClosing,
|
||||
|
||||
/** MQTTTransportCreated indicates a closed transport */
|
||||
MQTTTransportClosed
|
||||
};
|
||||
|
||||
/** queue The queue where the streams are scheduled. */
|
||||
@property (strong, nonatomic, nonnull) dispatch_queue_t queue;
|
||||
|
||||
/** streamSSLLevel an NSString containing the security level for read and write streams
|
||||
* For list of possible values see:
|
||||
* https://developer.apple.com/documentation/corefoundation/cfstream/cfstream_socket_security_level_constants
|
||||
* Please also note that kCFStreamSocketSecurityLevelTLSv1_2 is not in a list
|
||||
* and cannot be used as constant, but you can use it as a string value
|
||||
* defaults to kCFStreamSocketSecurityLevelNegotiatedSSL
|
||||
*/
|
||||
@property (strong, nonatomic, nonnull) NSString *streamSSLLevel;
|
||||
|
||||
/** host an NSString containing the hostName or IP address of the host to connect to */
|
||||
@property (strong, nonatomic, nonnull) NSString *host;
|
||||
|
||||
/** port an unsigned 32 bit integer containing the IP port number to connect to */
|
||||
@property (nonatomic) UInt32 port;
|
||||
|
||||
/** MQTTTransportDelegate needs to be set to a class implementing th MQTTTransportDelegate protocol
|
||||
* to receive delegate messages.
|
||||
*/
|
||||
@property (weak, nonatomic) _Nullable id<MQTTTransportDelegate> delegate;
|
||||
|
||||
/** state contains the current MQTTTransportState of the transport */
|
||||
@property (nonatomic) MQTTTransportState state;
|
||||
|
||||
/** open opens the transport and prepares it for communication
|
||||
* actual transports may require additional parameters to be set before opening
|
||||
*/
|
||||
- (void)open;
|
||||
|
||||
/** send transmits a data message
|
||||
* @param data data to be send, might be zero length
|
||||
* @result a boolean indicating if the data could be send or not
|
||||
*/
|
||||
- (BOOL)send:(nonnull NSData *)data;
|
||||
|
||||
/** close closes the transport */
|
||||
- (void)close;
|
||||
|
||||
@end
|
||||
|
||||
/** MQTTTransportDelegate protocol
|
||||
* Note: the implementation of the didReceiveMessage method is mandatory, the others are optional
|
||||
*/
|
||||
@protocol MQTTTransportDelegate <NSObject>
|
||||
|
||||
/** didReceiveMessage gets called when a message was received
|
||||
* @param mqttTransport the transport on which the message was received
|
||||
* @param message the data received which may be zero length
|
||||
*/
|
||||
- (void)mqttTransport:(nonnull id<MQTTTransport>)mqttTransport didReceiveMessage:(nonnull NSData *)message;
|
||||
|
||||
@optional
|
||||
|
||||
/** mqttTransportDidOpen gets called when a transport is successfully opened
|
||||
* @param mqttTransport the transport which was successfully opened
|
||||
*/
|
||||
- (void)mqttTransportDidOpen:(_Nonnull id<MQTTTransport>)mqttTransport;
|
||||
|
||||
/** didFailWithError gets called when an error was detected on the transport
|
||||
* @param mqttTransport the transport which detected the error
|
||||
* @param error available error information, might be nil
|
||||
*/
|
||||
- (void)mqttTransport:(_Nonnull id<MQTTTransport>)mqttTransport didFailWithError:(nullable NSError *)error;
|
||||
|
||||
/** mqttTransportDidClose gets called when the transport closed
|
||||
* @param mqttTransport the transport which was closed
|
||||
*/
|
||||
- (void)mqttTransportDidClose:(_Nonnull id<MQTTTransport>)mqttTransport;
|
||||
|
||||
@end
|
||||
|
||||
@interface MQTTTransport : NSObject <MQTTTransport>
|
||||
@end
|
||||
|
||||
40
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTTransport.m
generated
Normal file
40
Pods/MQTTClient/MQTTClient/MQTTClient/MQTTTransport.m
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// MQTTTransport.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Christoph Krey on 05.01.16.
|
||||
// Copyright © 2016-2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MQTTTransport.h"
|
||||
|
||||
#import "MQTTLog.h"
|
||||
|
||||
@implementation MQTTTransport
|
||||
@synthesize state;
|
||||
@synthesize queue;
|
||||
@synthesize streamSSLLevel;
|
||||
@synthesize delegate;
|
||||
@synthesize host;
|
||||
@synthesize port;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.state = MQTTTransportCreated;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)open {
|
||||
DDLogError(@"MQTTTransport is abstract class");
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
DDLogError(@"MQTTTransport is abstract class");
|
||||
}
|
||||
|
||||
- (BOOL)send:(NSData *)data {
|
||||
DDLogError(@"MQTTTransport is abstract class");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@end
|
||||
21
Pods/MQTTClient/MQTTClient/MQTTClient/ReconnectTimer.h
generated
Normal file
21
Pods/MQTTClient/MQTTClient/MQTTClient/ReconnectTimer.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// ReconnectTimer.h
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Josip Cavar on 22/08/2017.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ReconnectTimer : NSObject
|
||||
|
||||
- (instancetype)initWithRetryInterval:(NSTimeInterval)retryInterval
|
||||
maxRetryInterval:(NSTimeInterval)maxRetryInterval
|
||||
queue:(dispatch_queue_t)queue
|
||||
reconnectBlock:(void (^)(void))block;
|
||||
- (void)schedule;
|
||||
- (void)stop;
|
||||
- (void)resetRetryInterval;
|
||||
|
||||
@end
|
||||
67
Pods/MQTTClient/MQTTClient/MQTTClient/ReconnectTimer.m
generated
Normal file
67
Pods/MQTTClient/MQTTClient/MQTTClient/ReconnectTimer.m
generated
Normal file
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// ReconnectTimer.m
|
||||
// MQTTClient
|
||||
//
|
||||
// Created by Josip Cavar on 22/08/2017.
|
||||
// Copyright © 2017 Christoph Krey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ReconnectTimer.h"
|
||||
#import "GCDTimer.h"
|
||||
|
||||
@interface ReconnectTimer()
|
||||
|
||||
@property (strong, nonatomic) GCDTimer *timer;
|
||||
@property (assign, nonatomic) NSTimeInterval retryInterval;
|
||||
@property (assign, nonatomic) NSTimeInterval currentRetryInterval;
|
||||
@property (assign, nonatomic) NSTimeInterval maxRetryInterval;
|
||||
@property (strong, nonatomic) dispatch_queue_t queue;
|
||||
@property (copy, nonatomic) void (^reconnectBlock)(void);
|
||||
|
||||
@end
|
||||
|
||||
@implementation ReconnectTimer
|
||||
|
||||
- (instancetype)initWithRetryInterval:(NSTimeInterval)retryInterval
|
||||
maxRetryInterval:(NSTimeInterval)maxRetryInterval
|
||||
queue:(dispatch_queue_t)queue
|
||||
reconnectBlock:(void (^)(void))block {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.retryInterval = retryInterval;
|
||||
self.currentRetryInterval = retryInterval;
|
||||
self.maxRetryInterval = maxRetryInterval;
|
||||
self.reconnectBlock = block;
|
||||
self.queue = queue;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)schedule {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.timer = [GCDTimer scheduledTimerWithTimeInterval:self.currentRetryInterval
|
||||
repeats:NO
|
||||
queue:self.queue
|
||||
block:^{
|
||||
[weakSelf reconnect];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
[self.timer invalidate];
|
||||
self.timer = nil;
|
||||
}
|
||||
|
||||
- (void)resetRetryInterval {
|
||||
self.currentRetryInterval = self.retryInterval;
|
||||
}
|
||||
|
||||
- (void)reconnect {
|
||||
[self stop];
|
||||
if (self.currentRetryInterval < self.maxRetryInterval) {
|
||||
self.currentRetryInterval *= 2;
|
||||
}
|
||||
self.reconnectBlock();
|
||||
}
|
||||
|
||||
@end
|
||||
153
Pods/MQTTClient/README.md
generated
Normal file
153
Pods/MQTTClient/README.md
generated
Normal file
@@ -0,0 +1,153 @@
|
||||
# MQTT-Client-Framework
|
||||
|
||||
[](https://travis-ci.org/novastone-media/MQTT-Client-Framework)
|
||||
[](https://codecov.io/gh/novastone-media/MQTT-Client-Framework)
|
||||
[](https://img.shields.io/cocoapods/v/MQTTClient.svg)
|
||||
[](https://img.shields.io/cocoapods/p/MQTTClient.svg?style=flat)
|
||||
|
||||
**Welcome to MQTT-Client-Framework**
|
||||
|
||||
MQTT-Client-Framework is a native Objective-C iOS library. It uses `CFNetwork` for networking and `CoreData` for persistence. It is a complete implementation of MQTT 3.1.1 and supports TLS.
|
||||
|
||||
You can read [introduction](http://www.hivemq.com/blog/mqtt-client-library-encyclopedia-mqtt-client-framework) to learn more about framework.
|
||||
|
||||
MQTT-Client-Framework is tested with a long list of brokers:
|
||||
|
||||
* mosquitto
|
||||
* paho
|
||||
* rabbitmq
|
||||
* hivemq
|
||||
* rsmb
|
||||
* mosca
|
||||
* vernemq
|
||||
* emqtt
|
||||
* moquette
|
||||
* ActiveMQ
|
||||
* Apollo
|
||||
* CloudMQTT
|
||||
* aws
|
||||
* hbmqtt (MQTTv311 only, limitations)
|
||||
* [aedes](https://github.com/mcollina/aedes)
|
||||
* [flespi](https://flespi.com/mqtt-broker)
|
||||
|
||||
## Usage
|
||||
|
||||
Create a new client and connect to a broker:
|
||||
|
||||
```objective-c
|
||||
#import "MQTTClient.h"
|
||||
|
||||
MQTTCFSocketTransport *transport = [[MQTTCFSocketTransport alloc] init];
|
||||
transport.host = @"test.mosquitto.org";
|
||||
transport.port = 1883;
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
session.transport = transport;
|
||||
[session connectWithConnectHandler:^(NSError *error) {
|
||||
// Do some work
|
||||
}];
|
||||
```
|
||||
|
||||
Subscribe to a topic:
|
||||
|
||||
```objective-c
|
||||
[session subscribeToTopic:@"example/#" atLevel:MQTTQosLevelExactlyOnce subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss) {
|
||||
if (error) {
|
||||
NSLog(@"Subscription failed %@", error.localizedDescription);
|
||||
} else {
|
||||
NSLog(@"Subscription sucessfull! Granted Qos: %@", gQoss);
|
||||
}
|
||||
}];
|
||||
|
||||
```
|
||||
|
||||
In your `MQTTSession` delegate, add the following to receive messages for the subscribed topics:
|
||||
|
||||
```objective-c
|
||||
- (void)newMessage:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid {
|
||||
// New message received in topic
|
||||
}
|
||||
```
|
||||
|
||||
Publish a message to a topic:
|
||||
|
||||
```objective-c
|
||||
[session publishData:someData onTopic:@"example/#" retain:NO qos:MQTTQosLevelAtMostOnce publishHandler:^(NSError *error) {
|
||||
}];
|
||||
```
|
||||
|
||||
If you already have a self signed URL from broker like AWS IoT endpoint, use the `url` property of `MQTTWebsocketTransport`:
|
||||
```objective-c
|
||||
MQTTWebsocketTransport *transport = [[MQTTWebsocketTransport alloc] init];
|
||||
transport.url = @"wss://aws.iot-amazonaws.com/mqtt?expiry='2018-05-01T23:12:32.950Z'"
|
||||
|
||||
MQTTSession *session = [[MQTTSession alloc] init];
|
||||
session.transport = transport;
|
||||
[session connectWithConnectHandler:^(NSError *error) {
|
||||
// Do some work
|
||||
}];
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### CocoaPods
|
||||
|
||||
Add this to your Podfile:
|
||||
|
||||
```
|
||||
pod 'MQTTClient'
|
||||
```
|
||||
which is a short for:
|
||||
|
||||
```
|
||||
pod 'MQTTClient/Min'
|
||||
pod 'MQTTClient/Manager'
|
||||
```
|
||||
|
||||
The Manager subspec includes the `MQTTSessionManager` class.
|
||||
|
||||
If you want to use MQTT over Websockets:
|
||||
|
||||
```
|
||||
pod 'MQTTClient/Websocket'
|
||||
```
|
||||
|
||||
If you want to do your logging with CocoaLumberjack (recommended):
|
||||
|
||||
```
|
||||
pod 'MQTTClient/MinL'
|
||||
pod 'MQTTClient/ManagerL'
|
||||
pod 'MQTTClient/WebsocketL'
|
||||
```
|
||||
|
||||
### Carthage
|
||||
|
||||
In your Cartfile:
|
||||
|
||||
```
|
||||
github "novastone-media/MQTT-Client-Framework"
|
||||
```
|
||||
|
||||
### Manually
|
||||
|
||||
#### Git submodule
|
||||
|
||||
1. Add MQTT-Client-Framework as a git submodule into your top-level project directory or simply copy whole folder
|
||||
2. Find MQTTClient.xcodeproj and drag it into the file navigator of your app project.
|
||||
3. In Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar.
|
||||
4. Under "General" panel go to "Linked Frameworks and Libraries" and add MQTTClient.framework
|
||||
|
||||
#### Framework
|
||||
|
||||
1. Download MQTT-Client-Framework
|
||||
2. Build it and you should find MQTTClient.framework under "Products" group.
|
||||
3. Right click on it and select "Show in Finder" option.
|
||||
4. Just drag and drop MQTTClient.framework to your project
|
||||
|
||||
## Security Disclosure
|
||||
|
||||
If you believe you have identified a security vulnerability with MQTT-Client-Framework, please report it to ios@novastonemedia.com and do not post it to a public issue tracker.
|
||||
|
||||
## Thanks
|
||||
|
||||
This project was originally written by [Christoph Krey](https://github.com/ckrey).
|
||||
Reference in New Issue
Block a user