Compare commits

...

No commits in common. 'main' and 'master' have entirely different histories.
main ... master

88
.gitignore vendored

@ -0,0 +1,88 @@
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
.DS_Store

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -1,3 +1,128 @@
# MQTT # Android Mqtt Client
基于[mosquitto](https://github.com/eclipse/mosquitto)的android mqtt客户端
* 使用[mosquitto/lib](https://github.com/eclipse/mosquitto/tree/master/lib)的代码用c++封装接口
* 对外提供java接口简单易用
* 支持OpenSSL加密
* 实现订阅、解除订阅、发布消息等功能
MQTT安卓客户端 ## 接口
MqttClient.java
```java
/**
* 获取单例
*/
public static MqttClient getInstance()
```
```java
/**
* 启动mqtt服务
*/
public void start(@NonNull String host, int port, @NonNull String uuid, boolean clearSession)
public void start(@NonNull String host, int port, @NonNull String uuid, boolean clearSession, @NonNull String caFilePath, @NonNull String username, @NonNull String password)
```
```java
/**
* 重连服务
*/
public void reconnect()
```
```java
/**
* 订阅主题
*/
public void subscribe(String topic)
public void subscribe(String topic, int qos)
public void subscribe(List<String> topicList)
public void subscribe(List<String> topicList, List<Integer> qosList)
```
```java
/**
* 解除订阅
*/
public void unsubscribe(String topic)
public void unsubscribe(List<String> topicList)
```
```java
/**
* 发布消息
*/
public void publish(String topic, String message)
public void publish(String topic, String message, int qos)
```
```java
/**
* mqtt事件回调接口
*/
public interface MqttCallback {
@WorkerThread
void onMessage(@NonNull String topic, @NonNull String message);
@WorkerThread
void onLog(String str);
}
```
```java
/**
* 设置mqtt事件回调
*/
public void setOnMqttCallback(@Nullable MqttCallback callback)
```
## 使用
```
- 导入Module: Android Studio -> File -> New -> Import Module -> 选中“library”文件夹
- 修改app/build.gradle文件添加ndk abiFilters设置
android {
...
defaultConfig {
...
ndk {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
}
- 添加依赖
dependencies {
implementation project(':library')
}
```
## 编译问题
因为默认支持OpenSSL依赖静态库只添加了三个abi版本所以项目编译默认只支持```'armeabi-v7a', 'x86', 'arm64-v8a'```如需支持其他cpu abi请自行编译OpenSSL静态库
* 把```libssl.a libcrypto.a```文件添加到```/library/src/main/jni/lib```目录下并用文件夹区分abi类型
* 修改```library/build.gradle```添加对应的abi类型
* 修改```app/build.gradle```添加对应的abi类型
## 禁用OpenSSL
修改```/library/src/main/jni/Android.mk```文件,注释掉下面的代码
```
...
# ssl lib
#include $(CLEAR_VARS)
#LOCAL_MODULE := ssl
#LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libssl.a
#include $(PREBUILT_STATIC_LIBRARY)
# crypto lib
#include $(CLEAR_VARS)
#LOCAL_MODULE := ssl_crypto
#LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libcrypto.a
#include $(PREBUILT_STATIC_LIBRARY)
...
#LOCAL_CFLAGS += -DWITH_TLS_PSK
#LOCAL_CFLAGS += -DWITH_TLS
...
# link lib
#LOCAL_STATIC_LIBRARIES := ssl ssl_crypto
...
```
## 例子
app module只实现了简单的调用相关日志输出到了logcat

1
app/.gitignore vendored

@ -0,0 +1 @@
/build

@ -0,0 +1,51 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
ndkVersion '22.1.7171670'
// ndkVersion '16.1.4479499'
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "moe.key.yao.mqtt.client"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.guava:guava:28.0-android'
implementation project(':library')
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,24 @@
package moe.key.yao.mqtt.client
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("moe.key.yao.mqtt.client", appContext.packageName)
}
}

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="moe.key.yao.mqtt.client">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

@ -0,0 +1,115 @@
package moe.key.yao.mqtt.client
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.common.io.ByteStreams
import kotlinx.android.synthetic.main.activity_main.*
import moe.key.yao.mqtt.library.MqttClient
import java.io.File
import java.io.FileOutputStream
class MainActivity : AppCompatActivity(), MqttClient.MqttCallback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MqttClient.getInstance().setOnMqttCallback(this)
initLayout()
initPermission()
}
override fun onDestroy() {
super.onDestroy()
MqttClient.getInstance().setOnMqttCallback(null)
}
private fun initLayout() {
btn_start.setOnClickListener {
startMqttService()
}
btn_subscribe.setOnClickListener {
subscribeTopic()
}
btn_unsubscribe.setOnClickListener {
unsubscribeTopic()
}
btn_publish.setOnClickListener {
publishMessage()
}
}
private fun initPermission() {
val hasPermission: Int = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
if (hasPermission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
return
}
}
private fun startMqttService() {
// copy ca file to sdcard
val caFilePath = "${getExternalFilesDir(null)}/ca.crt"
val file = File(caFilePath)
if (!file.exists()) {
file.createNewFile()
val inStream = assets.open("ca.crt")
val outStream = FileOutputStream(file)
ByteStreams.copy(inStream, outStream)
inStream.close()
outStream.close()
}
// MqttClient.getInstance().start("192.168.1.190", 1883, "mqtt_android_client", false)
MqttClient.getInstance().start("192.168.1.190", 8883, "mqtt_android_client", false, caFilePath, "test", "123456")
}
private fun subscribeTopic() {
val topic = "/android/test/topicA"
MqttClient.getInstance().subscribe(topic)
}
private fun unsubscribeTopic() {
val topic = "/android/test/topicA"
MqttClient.getInstance().unsubscribe(topic)
}
private fun publishMessage() {
val topic = "/android/test/topicA"
val message = "test message"
MqttClient.getInstance().publish(topic, message)
}
override fun onMessage(topic: String, message: String) {
println("on message: $topic | $message")
}
override fun onLog(str: String?) {
println("on log: $str")
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 1) {
var flag = false
for (i in permissions.indices) {
if (permissions[i] == Manifest.permission.READ_EXTERNAL_STORAGE && grantResults[i] == PackageManager.PERMISSION_GRANTED) {
flag = true
break
}
}
if (!flag) {
Toast.makeText(this, "获取权限失败", Toast.LENGTH_SHORT).show()
}
}
}
}

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_start"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="start service"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/btn_subscribe"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="subscribe"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_start"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/btn_unsubscribe"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="unsubscribe"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_subscribe"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/btn_publish"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="publish"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_unsubscribe"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>

@ -0,0 +1,3 @@
<resources>
<string name="app_name">mqtt_client_android</string>
</resources>

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

@ -0,0 +1,17 @@
package moe.key.yao.mqtt.client
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

@ -0,0 +1,28 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.61'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

@ -0,0 +1,22 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.enableR8=false

Binary file not shown.

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
gradlew vendored

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

@ -0,0 +1 @@
/build

@ -0,0 +1,53 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
ndkVersion '22.1.7171670'
// ndkVersion '16.1.4479499'
defaultConfig {
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
ndk {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
externalNativeBuild {
ndkBuild {
arguments "NDK_APPLICATION_MK:=src/main/jni/Application.mk"
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
ndkBuild {
path file('src/main/jni/Android.mk')
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,27 @@
package moe.key.yao.mqtt.library;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("moe.key.yao.mqtt.library.test", appContext.getPackageName());
}
}

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="moe.key.yao.mqtt.library" />

@ -0,0 +1,288 @@
package moe.key.yao.mqtt.library;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import java.util.List;
/**
* Created by key on 2020-02-20.
*
* @author key
*/
public class MqttClient {
static {
System.loadLibrary("mqtt");
}
public interface MqttCallback {
@WorkerThread
void onMessage(@NonNull String topic, @NonNull String message);
@WorkerThread
void onLog(String str);
}
private static MqttClient mInstance;
public static MqttClient getInstance() {
if (mInstance == null) {
synchronized (MqttClient.class) {
mInstance = new MqttClient();
}
}
return mInstance;
}
private MqttClient() {
}
private long mPtr = 0;
private MqttCallback mCallback = null;
/**
*
*/
public void setOnMqttCallback(@Nullable MqttCallback callback) {
mCallback = callback;
}
/**
* mqtt
* @param host
* @param port
* @param uuid uuid
* @param clearSession
*/
public void start(@NonNull String host, int port, @NonNull String uuid, boolean clearSession) {
startInternal(host, port, uuid, clearSession, false, null, null, null);
}
/**
* mqtt使ssl
* @param host
* @param port
* @param uuid uuid
* @param clearSession
* @param caFilePath
* @param username
* @param password
*/
public void start(@NonNull String host, int port, @NonNull String uuid, boolean clearSession, @NonNull String caFilePath, @NonNull String username, @NonNull String password) {
startInternal(host, port, uuid, clearSession, true, caFilePath, username, password);
}
private void startInternal(@NonNull String host, int port, @NonNull String uuid, boolean clearSession, boolean isTLS, @Nullable String caFilePath, @Nullable String username, @Nullable String password) {
if (mPtr == 0) {
mPtr = _init(host,
port,
uuid,
clearSession,
isTLS,
caFilePath == null ? "" : caFilePath,
username == null ? "" : username,
password == null ? "" : password);
}
_start(mPtr);
}
/**
*
*/
public void reconnect() {
if (mPtr == 0) {
return;
}
_reconnect(mPtr);
}
/**
* topic
*/
public void subscribe(String topic) {
if (mPtr == 0) {
return;
}
_subscribe(mPtr, new String[]{topic}, new int[]{2});
}
/**
* topic
*/
public void subscribe(String topic, int qos) {
if (mPtr == 0) {
return;
}
_subscribe(mPtr, new String[]{topic}, new int[]{qos});
}
/**
* topic
*/
public void subscribe(List<String> topicList) {
if (mPtr == 0) {
return;
}
String[] topicArray = new String[topicList.size()];
int[] qosArray = new int[topicList.size()];
for (int i = 0; i < topicList.size(); i++) {
topicArray[i] = topicList.get(i);
qosArray[i] = 2;
}
_subscribe(mPtr, topicArray, qosArray);
}
/**
* topic
*/
public void subscribe(List<String> topicList, List<Integer> qosList) {
if (mPtr == 0) {
return;
}
String[] topicArray = new String[topicList.size()];
int[] qosArray = new int[topicList.size()];
for (int i = 0; i < topicList.size(); i++) {
topicArray[i] = topicList.get(i);
qosArray[i] = qosList.get(i);
}
_subscribe(mPtr, topicArray, qosArray);
}
/**
* topic
*/
public void unsubscribe(String topic) {
if (mPtr == 0) {
return;
}
_unsubscribe(mPtr, new String[]{topic});
}
/**
* topic
*/
public void unsubscribe(List<String> topicList) {
if (mPtr == 0) {
return;
}
String[] topicArray = new String[topicList.size()];
for (int i = 0; i < topicList.size(); i++) {
topicArray[i] = topicList.get(i);
}
_unsubscribe(mPtr, topicArray);
}
/**
*
* @param topic topic
* @param message
*/
public void publish(String topic, String message) {
if (mPtr == 0) {
return;
}
_publish(mPtr, topic, message, 0);
}
/**
*
* @param topic topic
* @param message
* @param qos qos
*/
public void publish(String topic, String message, int qos) {
if (mPtr == 0) {
return;
}
_publish(mPtr, topic, message, qos);
}
// ==== callback
@WorkerThread
private void onMessage(String topic, String payload) {
if (mCallback != null) {
mCallback.onLog("Mqtt onMessage: " + topic + " | " + payload);
mCallback.onMessage(topic, payload);
}
}
@WorkerThread
private void onConnect() {
if (mCallback != null) {
mCallback.onLog("Mqtt onConnect");
}
}
@WorkerThread
private void onConnectWithFlag(int flags) {
if (mCallback != null) {
mCallback.onLog("Mqtt onConnect with flags: " + flags);
}
}
@WorkerThread
private void onDisconnect() {
if (mCallback != null) {
mCallback.onLog("Mqtt onDisconnect");
}
}
@WorkerThread
private void onPublish() {
if (mCallback != null) {
mCallback.onLog("Mqtt onPublish");
}
}
@WorkerThread
private void onSubscribe() {
if (mCallback != null) {
mCallback.onLog("Mqtt onSubscribe");
}
}
@WorkerThread
private void onUnsubscribe() {
if (mCallback != null) {
mCallback.onLog("Mqtt onUnsubscribe");
}
}
@WorkerThread
private void onLog(int level, String str) {
/*
#define MOSQ_LOG_NONE 0
#define MOSQ_LOG_INFO (1<<0)
#define MOSQ_LOG_NOTICE (1<<1)
#define MOSQ_LOG_WARNING (1<<2)
#define MOSQ_LOG_ERR (1<<3)
#define MOSQ_LOG_DEBUG (1<<4)
#define MOSQ_LOG_SUBSCRIBE (1<<5)
#define MOSQ_LOG_UNSUBSCRIBE (1<<6)
#define MOSQ_LOG_WEBSOCKETS (1<<7)
#define MOSQ_LOG_INTERNAL 0x80000000
#define MOSQ_LOG_ALL 0x7FFFFFFF
*/
if (mCallback != null) {
mCallback.onLog("Mqtt onLog: " + str + " | level: " + level);
}
}
// ==== native
private native long _init(String host, int port, String uuid, boolean clearSession, boolean isTLS, String caFilePath, String userName, String password);
private native void _start(long ptr);
private native void _reconnect(long ptr);
private native void _subscribe(long ptr, String[] topicArray, int[] qosArray);
private native void _unsubscribe(long ptr, String[] topArray);
private native void _publish(long ptr, String topic, String message, int qos);
}

@ -0,0 +1,104 @@
LOCAL_PATH := $(call my-dir)
# ssl lib
include $(CLEAR_VARS)
LOCAL_MODULE := ssl
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libssl.a
include $(PREBUILT_STATIC_LIBRARY)
# crypto lib
include $(CLEAR_VARS)
LOCAL_MODULE := ssl_crypto
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libcrypto.a
include $(PREBUILT_STATIC_LIBRARY)
# build flag
include $(CLEAR_VARS)
LOCAL_CFLAGS += -fexceptions
LOCAL_CPPFLAGS += -std=c++11
LOCAL_LDLIBS += -llog -landroid
# include
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/openssl/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/uthash/src
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mosquitto
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mosquitto/lib
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mosquitto/lib/cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_C_INCLUDES += $(LOCAL_PATH)/jsoncpp/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/jsoncpp/src/lib_json
# mosquitto lib
LOCAL_CFLAGS += -DWITH_SOCKS
LOCAL_CFLAGS += -DWITH_EC
LOCAL_CFLAGS += -DWITH_UUID
LOCAL_CFLAGS += -DWITH_SYS_TREE
LOCAL_CFLAGS += -DWITH_MENORY_TRACKING
LOCAL_CFLAGS += -DWITH_PERSISTENCE
LOCAL_CFLAGS += -DWITH_BRIDGE
LOCAL_CFLAGS += -DWITH_THREADING
LOCAL_CFLAGS += -DWITH_TLS_PSK
LOCAL_CFLAGS += -DWITH_TLS
LOCAL_SRC_FILES += jsoncpp/src/lib_json/json_reader.cpp
LOCAL_SRC_FILES += jsoncpp/src/lib_json/json_valueiterator.inl
LOCAL_SRC_FILES += jsoncpp/src/lib_json/json_value.cpp
LOCAL_SRC_FILES += jsoncpp/src/lib_json/json_writer.cpp
LOCAL_SRC_FILES += jsoncpp/version.in
LOCAL_SRC_FILES += mosquitto/lib/actions.c
LOCAL_SRC_FILES += mosquitto/lib/alias_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/callbacks.c
LOCAL_SRC_FILES += mosquitto/lib/connect.c
LOCAL_SRC_FILES += mosquitto/lib/handle_auth.c
LOCAL_SRC_FILES += mosquitto/lib/handle_connack.c
LOCAL_SRC_FILES += mosquitto/lib/handle_disconnect.c
LOCAL_SRC_FILES += mosquitto/lib/handle_ping.c
LOCAL_SRC_FILES += mosquitto/lib/handle_pubackcomp.c
LOCAL_SRC_FILES += mosquitto/lib/handle_publish.c
LOCAL_SRC_FILES += mosquitto/lib/handle_pubrec.c
LOCAL_SRC_FILES += mosquitto/lib/handle_pubrel.c
LOCAL_SRC_FILES += mosquitto/lib/handle_suback.c
LOCAL_SRC_FILES += mosquitto/lib/handle_unsuback.c
LOCAL_SRC_FILES += mosquitto/lib/helpers.c
LOCAL_SRC_FILES += mosquitto/lib/logging_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/loop.c
LOCAL_SRC_FILES += mosquitto/lib/memory_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/messages_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/mosquitto.c
LOCAL_SRC_FILES += mosquitto/lib/net_mosq_ocsp.c
LOCAL_SRC_FILES += mosquitto/lib/net_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/options.c
LOCAL_SRC_FILES += mosquitto/lib/packet_datatypes.c
LOCAL_SRC_FILES += mosquitto/lib/packet_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/property_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/read_handle.c
LOCAL_SRC_FILES += mosquitto/lib/send_connect.c
LOCAL_SRC_FILES += mosquitto/lib/send_disconnect.c
LOCAL_SRC_FILES += mosquitto/lib/send_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/send_publish.c
LOCAL_SRC_FILES += mosquitto/lib/send_subscribe.c
LOCAL_SRC_FILES += mosquitto/lib/send_unsubscribe.c
LOCAL_SRC_FILES += mosquitto/lib/socks_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/srv_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/thread_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/time_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/tls_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/utf8_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/util_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/util_topic.c
LOCAL_SRC_FILES += mosquitto/lib/will_mosq.c
LOCAL_SRC_FILES += mosquitto_wrapper.cpp
LOCAL_SRC_FILES += JNIEnvHandler.cpp
LOCAL_SRC_FILES += MqttClient.cpp
LOCAL_SRC_FILES += RespJson.cpp
# link lib
LOCAL_STATIC_LIBRARIES := ssl ssl_crypto
# module name
LOCAL_MODULE := mqtt
include $(BUILD_SHARED_LIBRARY)

@ -0,0 +1,5 @@
APP_OPTIM := release
APP_ABI := armeabi-v7a x86 arm64-v8a
APP_STL := c++_static
APP_PLATFORM := android-23
APP_PIE := false

@ -0,0 +1,117 @@
//
// Created by Key.Yao on 2020-02-20.
//
#include "JNIEnvHandler.h"
using namespace std;
struct ThreadMessage {
ThreadMessage(int i, void* m, bool flag) {
what = i;
msg = m;
exit = flag;
}
int what;
void* msg;
bool exit;
};
JNIEnvHandler::JNIEnvHandler(const char *threadName) {
_threadName = threadName;
_thread = nullptr;
_exitFlag = false;
_vm = nullptr;
_callback = nullptr;
}
JNIEnvHandler::~JNIEnvHandler() {
_thread = nullptr;
_callback = nullptr;
_instance = nullptr;
_vm = nullptr;
}
bool JNIEnvHandler::init(JavaVM *vm, jobject instance, handleMessage callback) {
if (!_thread) {
_thread = new thread(&JNIEnvHandler::process, this);
}
_vm = vm;
_instance = instance;
_callback = callback;
return true;
}
void JNIEnvHandler::exit() {
if (!_thread) {
return;
}
auto *msg = new ThreadMessage(-1, nullptr, true);
{
lock_guard<mutex> lock(_mutex);
_queue.push(msg);
_cv.notify_one();
}
_thread->join();
delete _thread;
_thread = nullptr;
_exitFlag = true;
}
std::thread::id JNIEnvHandler::getThreadId() {
return _thread->get_id();
}
void JNIEnvHandler::post(int what, void *obj) {
auto *msg = new ThreadMessage(what, obj, false);
unique_lock<mutex> lock(_mutex);
_queue.push(msg);
_cv.notify_one();
}
void JNIEnvHandler::process() {
_exitFlag = false;
JNIEnv *env = nullptr;
if (_vm) {
_vm->AttachCurrentThread(&env, nullptr);
}
while (true) {
ThreadMessage *msg = nullptr;
{
unique_lock<mutex> lock(_mutex);
while (_queue.empty()) {
_cv.wait(lock);
}
if (_queue.empty()) {
continue;
}
msg = _queue.front();
_queue.pop();
}
if (msg->exit) {
delete msg;
break;
}
if (env && _instance) {
_callback(msg->what, msg->msg, env, _instance);
}
delete msg;
}
env->DeleteGlobalRef(_instance);
if (_vm) {
_vm->DetachCurrentThread();
}
}

@ -0,0 +1,54 @@
//
// Created by Key.Yao on 2020-02-20.
//
#ifndef MQTT_CLIENT_ANDROID_JNIENVHANDLER_H
#define MQTT_CLIENT_ANDROID_JNIENVHANDLER_H
#include <jni.h>
#include <thread>
#include <queue>
#include <atomic>
#include <condition_variable>
typedef void(*handleMessage)(int what, void* obj, JNIEnv *env, jobject instance);
struct ThreadMessage;
class JNIEnvHandler {
public:
JNIEnvHandler(const char* threadName);
~JNIEnvHandler();
bool init(JavaVM *vm, jobject instance, handleMessage callback);
void exit();
std::thread::id getThreadId();
void post(int what, void *obj = nullptr);
private:
JNIEnvHandler(const JNIEnvHandler&);
JNIEnvHandler&operator=(const JNIEnvHandler&);
void process();
JavaVM *_vm;
jobject _instance;
std::thread *_thread;
std::queue<ThreadMessage*> _queue;
std::mutex _mutex;
std::condition_variable _cv;
std::atomic<bool> _exitFlag;
handleMessage _callback;
const char* _threadName;
};
#endif //MQTT_CLIENT_ANDROID_JNIENVHANDLER_H

@ -0,0 +1,635 @@
//
// Created by Key.Yao on 2020-02-20.
//
#include "MqttClient.h"
#include <jni.h>
#include <android/log.h>
#include <pthread.h>
#include <unistd.h>
#include "mosquitto_wrapper.h"
#include "JNIEnvHandler.h"
#define ALOG(...) ((void)__android_log_print(ANDROID_LOG_INFO, "MqttClientJNI", __VA_ARGS__))
#ifndef UNUSED
#define UNUSED(A) (void)(A)
#endif
#ifndef NELEM
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif
#ifndef MQTTCLIENT_JNICALL
#define MQTTCLIENT_JNICALL
#endif
#define TYPE_ON_MESSAGE 1
#define TYPE_ON_CONNECT 2
#define TYPE_ON_CONNECT_WITH_FLAG 3
#define TYPE_ON_DISCONNECT 4
#define TYPE_ON_PUBLISH 5
#define TYPE_ON_SUBSCRIBE 6
#define TYPE_ON_UNSUBSCRIBE 7
#define TYPE_ON_LOG 8
using namespace std;
using namespace mqttclient;
#ifdef __cplusplus
extern "C" {
#endif
// thread callback method define
void* mqtt_thread(void *p);
void* mqtt_reconnect_thread(void *p);
// mosquitto callback method define
void mqtt_on_message(void *instance, const struct mosquitto_message *message);
void mqtt_on_connect(void *instance, int rc);
void mqtt_on_connect_with_flag(void *instance, int rc, int flags);
void mqtt_on_disconnect(void *instance, int rc);
void mqtt_on_publish(void *instance, int rc);
void mqtt_on_subscribe(void *instance, int mid, int qos_count, const int *granted_qos);
void mqtt_on_unsubscribe(void *instance, int rc);
void mqtt_on_log(void *instance, int level, const char *str);
// callback to java define
void callback2Java(int what, void* obj, JNIEnv *env, jobject instance);
// jni method define
jlong MQTTCLIENT_JNICALL mqtt_native_init(JNIEnv *env, jobject instance, jstring hostString, jint portInt, jstring uuidString, jboolean clearSession, jboolean isTLS, jstring caFilePathString, jstring usernameString, jstring passwordString);
void MQTTCLIENT_JNICALL mqtt_native_start(JNIEnv *env, jobject instance, jlong ptr);
void MQTTCLIENT_JNICALL mqtt_native_reconnect(JNIEnv *env, jobject instance, jlong ptr);
void MQTTCLIENT_JNICALL mqtt_native_subscribe(JNIEnv *env, jobject instance, jlong ptr, jobjectArray topicJArray, jintArray qosJArray);
void MQTTCLIENT_JNICALL mqtt_native_unsubscribe(JNIEnv *env, jobject instance, jlong ptr, jobjectArray topicJArray);
void MQTTCLIENT_JNICALL mqtt_native_publish(JNIEnv *env, jobject instance, jlong ptr, jstring topicString, jstring messageString, jint qosInt);
JNINativeMethod mqtt_methods[] = {
{
"_init",
"(Ljava/lang/String;ILjava/lang/String;ZZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
(void*) mqtt_native_init
},
{
"_start",
"(J)V",
(void*) mqtt_native_start
},
{
"_reconnect",
"(J)V",
(void*) mqtt_native_reconnect
},
{
"_subscribe",
"(J[Ljava/lang/String;[I)V",
(void*) mqtt_native_subscribe
},
{
"_unsubscribe",
"(J[Ljava/lang/String;)V",
(void*) mqtt_native_unsubscribe
},
{
"_publish",
"(JLjava/lang/String;Ljava/lang/String;I)V",
(void*) mqtt_native_publish
}
};
#ifdef __cplusplus
}
#endif
class ExtraData {
public:
string *host = nullptr;
int port = 0;
JNIEnvHandler *handler = nullptr;
JavaVM *javaVM = nullptr;
bool exitFlag = false;
bool running = false;
pthread_mutex_t mutexReconnect;
};
class MqttMsg {
public:
string *topic = nullptr;
string *payload = nullptr;
};
class MqttConnectFlag {
public:
int flags = 0;
};
class MqttLog {
public:
string *str = nullptr;
int level = 0;
};
static JavaVM *mVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
UNUSED(reserved);
mVM = vm;
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jclass clazz = env->FindClass("moe/key/yao/mqtt/library/MqttClient");
if (clazz == nullptr) {
return JNI_ERR;
}
if (env->RegisterNatives(clazz, mqtt_methods, NELEM(mqtt_methods)) < 0) {
env->DeleteLocalRef(clazz);
return JNI_ERR;
} else {
env->DeleteLocalRef(clazz);
}
// lib init
mqttclient::lib_init();
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
UNUSED(vm);
UNUSED(reserved);
// lib clean up
mqttclient::lib_cleanup();
}
// jni method
jlong MQTTCLIENT_JNICALL mqtt_native_init(JNIEnv *env, jobject instance, jstring hostString, jint portInt, jstring uuidString, jboolean clearSession, jboolean isTLS, jstring caFilePathString, jstring usernameString, jstring passwordString) {
const char *host = env->GetStringUTFChars(hostString, nullptr);
const char *uuid = env->GetStringUTFChars(uuidString, nullptr);
const char *caFilePath = env->GetStringUTFChars(caFilePathString, nullptr);
const char *username = env->GetStringUTFChars(usernameString, nullptr);
const char *password = env->GetStringUTFChars(passwordString, nullptr);
bool sessionFlag = clearSession != 0;
auto *mosq = new mosquitto_wrapper(uuid, sessionFlag);
mosq->on_connect_callback = mqtt_on_connect;
mosq->on_connect_with_flag_callback = mqtt_on_connect_with_flag;
mosq->on_disconnect_callback = mqtt_on_disconnect;
mosq->on_publish_callback = mqtt_on_publish;
mosq->on_message_callback = mqtt_on_message;
mosq->on_subscribe_callback = mqtt_on_subscribe;
mosq->on_unsubscribe_callback = mqtt_on_unsubscribe;
mosq->on_log_callback = mqtt_on_log;
if (isTLS) {
mosq->tls_insecure_set(true);
mosq->tls_opts_set(1, "tlsv1", nullptr);
if (strlen(caFilePath) != 0) {
mosq->tls_set(caFilePath);
}
if (strlen(username) != 0 && strlen(password) != 0) {
mosq->username_pw_set(username, password);
}
}
auto *extra = new ExtraData();
extra->host = new string(host);
extra->port = portInt;
extra->javaVM = mVM;
extra->handler = new JNIEnvHandler("callback");
extra->handler->init(mVM, env->NewGlobalRef(instance), callback2Java);
pthread_mutex_init(&extra->mutexReconnect, nullptr);
mosq->extra = extra;
env->ReleaseStringUTFChars(hostString, host);
env->ReleaseStringUTFChars(caFilePathString, caFilePath);
env->ReleaseStringUTFChars(uuidString, uuid);
return reinterpret_cast<jlong>(mosq);
}
void MQTTCLIENT_JNICALL mqtt_native_start(JNIEnv *env, jobject instance, jlong ptr) {
UNUSED(env);
UNUSED(instance);
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
if (!mosq) {
return;
}
auto *extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->running) {
pthread_t tid;
pthread_create(&tid, nullptr, mqtt_thread, mosq);
pthread_detach(tid);
} else {
// running
ALOG("mqtt thread running");
}
}
void MQTTCLIENT_JNICALL mqtt_native_reconnect(JNIEnv *env, jobject instance, jlong ptr) {
UNUSED(env);
UNUSED(instance);
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
if (!mosq) {
return;
}
pthread_t tid;
pthread_create(&tid, nullptr, mqtt_reconnect_thread, mosq);
pthread_detach(tid);
}
void MQTTCLIENT_JNICALL mqtt_native_subscribe(JNIEnv *env, jobject instance, jlong ptr, jobjectArray topicJArray, jintArray qosJArray) {
UNUSED(instance);
jint arrayLen = env->GetArrayLength(topicJArray);
jint* qosList = env->GetIntArrayElements(qosJArray, nullptr);
string *topics[arrayLen];
for (int i = 0; i < arrayLen; i++) {
auto itemString = (jstring) env->GetObjectArrayElement(topicJArray, i);
const char *item = env->GetStringUTFChars(itemString, nullptr);
topics[i] = new string(item);
env->ReleaseStringUTFChars(itemString, item);
}
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
for (int i = 0 ; i < arrayLen; i ++) {
int mid;
mosq->subscribe(&mid, topics[i]->c_str(), qosList[i]);
delete topics[i];
topics[i] = nullptr;
}
}
void MQTTCLIENT_JNICALL mqtt_native_unsubscribe(JNIEnv *env, jobject instance, jlong ptr, jobjectArray topicJArray) {
UNUSED(instance);
jint arrayLen = env->GetArrayLength(topicJArray);
string *topics[arrayLen];
for (int i = 0; i < arrayLen; i++) {
auto itemString = (jstring) env->GetObjectArrayElement(topicJArray, i);
const char *item = env->GetStringUTFChars(itemString, nullptr);
topics[i] = new string(item);
env->ReleaseStringUTFChars(itemString, item);
}
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
for (int i = 0 ; i < arrayLen; i ++) {
int mid;
mosq->unsubscribe(&mid, topics[i]->c_str());
delete topics[i];
topics[i] = nullptr;
}
}
void MQTTCLIENT_JNICALL mqtt_native_publish(JNIEnv *env, jobject instance, jlong ptr, jstring topicString, jstring messageString, jint qosInt) {
UNUSED(instance);
const char *topic = env->GetStringUTFChars(topicString, nullptr);
const char *message = env->GetStringUTFChars(messageString, nullptr);
// publish
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
mosq->publish(nullptr, topic, (int) strlen(message), message, qosInt);
// release string
env->ReleaseStringUTFChars(topicString, topic);
env->ReleaseStringUTFChars(messageString, message);
}
// thread callback method
void* mqtt_thread(void *p) {
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(p);
if (!mosq) {
return nullptr;
}
auto *extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return nullptr;
}
JNIEnv *env;
extra->javaVM->AttachCurrentThread(&env, nullptr);
extra->running = true;
mosq->connect(extra->host->c_str(), extra->port);
while (true) {
int rc = mosq->loop();
if (extra->exitFlag) {
break;
}
if (rc != MOSQ_ERR_SUCCESS) {
// error
const char *errorMsg = mqttclient::strerror(rc);
ALOG("mqtt connect error ========>> %s", errorMsg);
}
if (rc) {
sleep(10);
mosq->reconnect();
}
}
extra->javaVM->DetachCurrentThread();
extra->running = false;
pthread_mutex_destroy(&extra->mutexReconnect);
extra->javaVM = nullptr;
delete extra->host;
extra->handler->exit();
delete extra->handler;
extra->handler = nullptr;
delete extra;
mosq->extra = nullptr;
delete mosq;
return nullptr;
}
void* mqtt_reconnect_thread(void *p) {
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(p);
if (!mosq) {
return nullptr;
}
auto *extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return nullptr;
}
pthread_mutex_lock(&extra->mutexReconnect);
mosq->disconnect();
pthread_mutex_unlock(&extra->mutexReconnect);
return nullptr;
}
// mosquitto callback method define
void mqtt_on_message(void *instance, const struct mosquitto_message *message) {
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
auto msg = new MqttMsg();
msg->topic = new string(message->topic);
msg->payload = new string(reinterpret_cast<char*>(message->payload));
extra->handler->post(TYPE_ON_MESSAGE, msg);
}
void mqtt_on_connect(void *instance, int rc) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_CONNECT);
}
void mqtt_on_connect_with_flag(void *instance, int rc, int flags) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
auto connectFlag = new MqttConnectFlag();
connectFlag->flags = flags;
extra->handler->post(TYPE_ON_CONNECT_WITH_FLAG, connectFlag);
}
void mqtt_on_disconnect(void *instance, int rc) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_DISCONNECT);
}
void mqtt_on_publish(void *instance, int rc) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_PUBLISH);
}
void mqtt_on_subscribe(void *instance, int mid, int qos_count, const int *granted_qos) {
UNUSED(mid);
UNUSED(qos_count);
UNUSED(granted_qos);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_SUBSCRIBE);
}
void mqtt_on_unsubscribe(void *instance, int rc) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_UNSUBSCRIBE);
}
void mqtt_on_log(void *instance, int level, const char *str) {
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
auto log = new MqttLog();
log->level = level;
log->str = new string(str);
extra->handler->post(TYPE_ON_LOG, log);
}
// callback to java
void callback2Java(int what, void* obj, JNIEnv *env, jobject instance) {
auto clazz = env->GetObjectClass(instance);
jmethodID methodId = nullptr;
switch (what) {
case TYPE_ON_MESSAGE: {
if (!obj) {
break;
}
methodId = env->GetMethodID(clazz, "onMessage", "(Ljava/lang/String;Ljava/lang/String;)V");
auto msg = reinterpret_cast<MqttMsg*>(obj);
jstring topicString = env->NewStringUTF(msg->topic->c_str());
jstring payloadString = env->NewStringUTF(msg->payload->c_str());
env->CallVoidMethod(instance, methodId, topicString, payloadString);
env->DeleteLocalRef(topicString);
env->DeleteLocalRef(payloadString);
delete msg->topic;
delete msg->payload;
delete msg;
break;
};
case TYPE_ON_CONNECT: {
methodId = env->GetMethodID(clazz, "onConnect", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_CONNECT_WITH_FLAG: {
methodId = env->GetMethodID(clazz, "onConnectWithFlag", "(I)V");
auto log = reinterpret_cast<MqttConnectFlag*>(obj);
int flags = log->flags;
env->CallVoidMethod(instance, methodId, flags);
delete log;
break;
}
case TYPE_ON_DISCONNECT: {
methodId = env->GetMethodID(clazz, "onDisconnect", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_PUBLISH: {
methodId = env->GetMethodID(clazz, "onPublish", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_SUBSCRIBE: {
methodId = env->GetMethodID(clazz, "onSubscribe", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_UNSUBSCRIBE: {
methodId = env->GetMethodID(clazz, "onUnsubscribe", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_LOG: {
if (!obj) {
return;
}
methodId = env->GetMethodID(clazz, "onLog", "(ILjava/lang/String;)V");
auto log = reinterpret_cast<MqttLog*>(obj);
jstring msgString = env->NewStringUTF(log->str->c_str());
env->CallVoidMethod(instance, methodId, log->level, msgString);
env->DeleteLocalRef(msgString);
delete log->str;
delete log;
break;
}
default:
break;
}
env->DeleteLocalRef(clazz);
}

@ -0,0 +1,14 @@
//
// Created by Key.Yao on 2020-02-20.
//
#ifndef MQTT_CLIENT_ANDROID_MQTTCLIENT_H
#define MQTT_CLIENT_ANDROID_MQTTCLIENT_H
class MqttClient {
};
#endif //MQTT_CLIENT_ANDROID_MQTTCLIENT_H

@ -0,0 +1,331 @@
#include <RespJson.h>
bool RespJson::RespManage(char* str, string jsonStr)
{
Json::Reader reader;
Json::Value root;
string serviceId;
if (reader.parse(str, root))
{
serviceId = root["serviceId"].asString();
}
if(serviceId == "platformupgradeDevices" || serviceId == "platformLsuueConfigureFile" || serviceId == "getDeviceLogFile"
|| serviceId == "getDeviceData" || serviceId == "setDeviceTime" || serviceId == "setTransOnlineMonitConfig"
|| serviceId == "setFunctionConfiguration" || serviceId == "setIcingOnlineAlarmMonitConfig" || serviceId == "setPhotoParams"
|| serviceId == "SetAcceptCameraPhoto" || serviceId == "setCaptureTimesTaskParameter" || serviceId == "actionRestart"
|| serviceId == "cameraRemoteControl" || serviceId == "manualCapturePicture" || serviceId == "setMicroMeteorologicalAlarm")
{
jsonStr = CommonResp(str);
}else if(serviceId == "getDeviceLogList")
{
// getDeviceLogListResp
}
// else if...
}
string RespJson::CommonResp(char* str)
{
Json::Reader reader;
Json::Value root;
int mid;
if (reader.parse(str, root))
{
mid = root["mid"].asInt();
}
string jsonStr;
CommonJSON(jsonStr, 0, 0, mid);
return jsonStr;
}
bool RespJson::CommonJSON(string jsonStr, int resultCode, int errcode, int mid)
{
/*通用响应JSON*/
Json::Value root;
Json::Value body;
body["resultCode"] = resultCode;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getDeviceLogListJSON(string jsonStr,DeviceLogList_JSON bodyData, int errcode, int mid)
{
/*查询终端设备日志列表*/
Json::Value root;
Json::Value body;
body["resultCode"] = bodyData.resultCode;
body["fileCount"] = bodyData.fileCount;
Json::Value List;
for(int i = 0; i < bodyData.fileCount; i++)
{
Json::Value ListData;
ListData["fileType"] = bodyData.fileList[i].fileType;
ListData["fileName"] = bodyData.fileList[i].fileName;
ListData["time"] = static_cast<Json::Int64>(bodyData.fileList[i].time);
ListData["fileSize"] = bodyData.fileList[i].fileSize;
List.append(ListData);
}
body["fileList"] = List;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getDeviceTimeJSON(string jsonStr,DeviceTime_JSON bodyData, int errcode, int mid)
{
/*平台端查询终端设备的系统时间*/
Json::Value root;
Json::Value body;
body["timingType"] = bodyData.timingType;
body["currentTime"] = static_cast<Json::Int64>(bodyData.currentTime);
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getTransOnlineMonitConfigJSON(string jsonStr,TransOnlineMonitConfig_JSON bodyData, int errcode, int mid)
{
/*获取输电在线监测终端的运行状态参数*/
Json::Value root;
Json::Value body;
body["heartbeatInterval"] = bodyData.heartbeatInterval;
body["sampleInterval"] = bodyData.sampleInterval;
body["sleepDuration"] = bodyData.sleepDuration;
body["onlineInterval"] = bodyData.onlineInterval;
Json::Value List;
for(int i = 0; i < 29; i++)
{
Json::Value ListData;
ListData["day"] = bodyData.restartTime[i].day;
ListData["hour"] = bodyData.restartTime[i].hour;
ListData["minute"] = bodyData.restartTime[i].minute;
List.append(ListData);
}
body["restartTime"] = List;
body["ipAddr"] = bodyData.ipAddr;
body["port"] = bodyData.port;
body["httpIp"] = bodyData.httpIp;
body["httpPort"] = bodyData.httpPort;
body["PwrMode"] = bodyData.PwrMode;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getFunctionConfigurationJSON(string jsonStr,FunctionConfiguration_JSON bodyData, int errcode, int mid)
{
/*获取装置功能配置*/
Json::Value root;
Json::Value body;
Json::Value List;
Json::Value ListData;
ListData["function"] = bodyData.function;
ListData["mode"] = bodyData.mode;
List.append(ListData);
body["configure"] = List;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getIcingOnlineAlarmMonitConfigJSON(string jsonStr,IcingOnlineAlarmMonitConfig_JSON bodyData, int errcode, int mid)
{
/*获取设置覆冰在线监测终端的告警阈*/
Json::Value root;
Json::Value body;
body["resultCode"] = bodyData.resultCode;
body["fileCount"] = bodyData.interval;
body["PullAAlarmThreshold"] = bodyData.PullAAlarmThreshold;
body["windAlarmThreshold"] = bodyData.windAlarmThreshold;
body["rainAlarmThreshold"] = bodyData.rainAlarmThreshold;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getPhotoParamsJSON(string jsonStr,PhotoParams_JSON bodyData, int errcode, int mid)
{
/*平台端查询终端设备图片配置信息*/
Json::Value root;
Json::Value body;
body["channel"] = bodyData.channel;
body["color"] = bodyData.color;
body["brightness"] = bodyData.brightness;
body["contrast"] = bodyData.contrast;
body["saturation"] = bodyData.saturation;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getCaptureTimesTaskInformationJSON(string jsonStr,CaptureTimesTask_JSON bodyData, int errcode, int mid)
{
/*平台端查询终端设备定时抓拍任务信息*/
Json::Value root;
Json::Value body;
body["channel"] = bodyData.channel;
body["color"] = bodyData.groupNumber;
Json::Value array;
body["paramArray"] = array;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getDeviceSolarPowerPropertiesJSON(string jsonStr,DeviceSolarPower_JSON bodyData, int errcode, int mid)
{
/*查询设备太阳能电池板状态数据*/
Json::Value root;
Json::Value body;
body["batteryNumber"] = bodyData.batteryNumber;
body["time"] = static_cast<Json::Int64>(bodyData.time);
body["solarPanelVoltage"] = bodyData.solarPanelVoltage;
body["solarPanelCurrent"] = bodyData.solarPanelCurrent;
body["batteryVoltage"] = bodyData.batteryVoltage;
body["batteryCurrent"] = bodyData.batteryCurrent;
body["loadVoltage"] = bodyData.loadVoltage;
body["loadCurrent"] = bodyData.loadCurrent;
body["remainingBatteryPower"] = bodyData.remainingBatteryPower;
body["batteryTemperature"] = bodyData.batteryTemperature;
body["batteryChargeStatus"] = bodyData.batteryChargeStatus;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getWeatherMonitoringJSON(string jsonStr,Weather_JSON bodyData, int errcode, int mid)
{
/*查询终端微气象数据*/
Json::Value root;
Json::Value body;
body["resultCode"] = bodyData.resultCode;
body["fileCount"] = bodyData.packageNum;
Json::Value List;
for(int i = 0; i < bodyData.packageNum; i++)
{
Json::Value ListData;
ListData["time"] = static_cast<Json::Int64>(bodyData.fileList[i].time);
ListData["Temperature"] = bodyData.fileList[i].Temperature;
ListData["humidity"] = bodyData.fileList[i].humidity;
ListData["airPressure"] = bodyData.fileList[i].airPressure;
ListData["instanWindSpeed"] = bodyData.fileList[i].instanWindSpeed;
ListData["aveWindSspeed1"] = bodyData.fileList[i].aveWindSspeed1;
ListData["aveWindSspeed10"] = bodyData.fileList[i].aveWindSspeed10;
ListData["maxWindSpeed10"] = bodyData.fileList[i].maxWindSpeed10;
ListData["aveMaxWindSpeed10"] = bodyData.fileList[i].aveMaxWindSpeed10;
ListData["extremeWindSpeed"] = bodyData.fileList[i].extremeWindSpeed;
ListData["instanWindDirection"] = bodyData.fileList[i].instanWindDirection;
ListData["aveWindDirection1"] = bodyData.fileList[i].aveWindDirection1;
ListData["aveWindDirection10"] = bodyData.fileList[i].aveWindDirection10;
ListData["extremeWindDirection"] = bodyData.fileList[i].extremeWindDirection;
ListData["radiationIntensity"] = bodyData.fileList[i].radiationIntensity;
ListData["precipitation1"] = bodyData.fileList[i].precipitation1;
ListData["precipitation6"] = bodyData.fileList[i].precipitation6;
ListData["precipitation12"] = bodyData.fileList[i].precipitation12;
ListData["precipitation24"] = bodyData.fileList[i].precipitation24;
List.append(ListData);
}
body["dataList"] = List;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getPullAndAngleMonitoringJSON(string jsonStr,PullAndAngle_JSON bodyData, int errcode, int mid)
{
/*查询终端拉力倾角监测*/
Json::Value root;
Json::Value body;
body["resultCode"] = bodyData.resultCode;
body["type"] = bodyData.type;
body["number"] = bodyData.number;
Json::Value List;
for(int i = 0; i < bodyData.number; i++)
{
Json::Value ListData;
ListData["time"] = static_cast<Json::Int64>(bodyData.fileList[i].time);
ListData["maxPull"] = bodyData.fileList[i].maxPull;
ListData["fileName"] = bodyData.fileList[i].maxPullOblique;
ListData["maxPullOblique"] = bodyData.fileList[i].maxPullWind;
ListData["equalIceThickness"] = bodyData.fileList[i].equalIceThickness;
List.append(ListData);
}
body["dataList"] = List;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}
bool RespJson::getMicroMeteorologicalAlarmJSON(string jsonStr,Alarm_JSON bodyData, int errcode, int mid)
{
/*查询终端设备的微气象告警阈值*/
Json::Value root;
Json::Value body;
body["level"] = bodyData.level;
body["interval"] = bodyData.interval;
body["windSpeedAlarm"] = bodyData.windSpeedAlarm;
body["precipitationAlarm"] = bodyData.precipitationAlarm;
root["body"] = body;
root["errcode"] = errcode;
root["mid"] = mid;
root["msgType"] = "deviceRsp";
Json::StyledWriter writer;
jsonStr = writer.write(root);
return true;
}

@ -0,0 +1,153 @@
#ifndef MQTT_CLIENT_ANDROID_RESPJSON_H
#define MQTT_CLIENT_ANDROID_RESPJSON_H
#include <json/json.h>
#include <string>
using namespace std;
typedef struct DeviceLogList_JSON
{
int resultCode;
int fileCount;
struct file_List
{
int fileType;
string fileName;
time_t time;
int fileSize;
}fileList[1];
};
typedef struct DeviceTime_JSON
{
int timingType;
time_t currentTime;
};
typedef struct TransOnlineMonitConfig_JSON
{
int heartbeatInterval;
int sampleInterval;
int sleepDuration;
int onlineInterval;
struct restart_Time
{
int day;
int hour;
int minute;
}restartTime[29];
string ipAddr;
int port;
string httpIp;
int httpPort;
int PwrMode;
};
typedef struct FunctionConfiguration_JSON
{
string function;
int mode;
};
typedef struct IcingOnlineAlarmMonitConfig_JSON
{
int resultCode;
int interval;
float PullAAlarmThreshold;
float windAlarmThreshold;
float rainAlarmThreshold;
};
typedef struct PhotoParams_JSON
{
int channel;
int color;
int brightness;
int contrast;
int saturation;
};
typedef struct CaptureTimesTask_JSON
{
int channel;
int groupNumber;
};
typedef struct DeviceSolarPower_JSON
{
int batteryNumber;
time_t time;
float solarPanelVoltage;
float solarPanelCurrent;
float batteryVoltage;
float batteryCurrent;
float loadVoltage;
float loadCurrent;
int remainingBatteryPower;
float batteryTemperature;
int batteryChargeStatus;
};
typedef struct Weather_JSON
{
int resultCode;
int packageNum;
struct file_List
{
time_t time;
float Temperature;
float humidity;
float airPressure;
float instanWindSpeed;
float aveWindSspeed1;
float aveWindSspeed10;
float maxWindSpeed10;
float aveMaxWindSpeed10;
float extremeWindSpeed;
float instanWindDirection;
float aveWindDirection1;
float aveWindDirection10;
float extremeWindDirection;
float radiationIntensity;
float precipitation1;
float precipitation6;
float precipitation12;
float precipitation24;
}fileList[1];
};
typedef struct PullAndAngle_JSON
{
int resultCode;
int type;
int number;
struct file_List
{
time_t time;
float maxPull;
float maxPullOblique;
float maxPullWind;
float equalIceThickness;
}fileList[1];
};
typedef struct Alarm_JSON
{
int level;
int interval;
float windSpeedAlarm;
float precipitationAlarm;
};
class RespJson {
bool RespManage(char* str, string jsonStr);
string CommonResp(char* str);
bool CommonJSON(string jsonStr, int resultCode, int errcode, int mid);
bool getDeviceLogListJSON(string jsonStr,DeviceLogList_JSON bodyData, int errcode, int mid);
bool getDeviceTimeJSON(string jsonStr,DeviceTime_JSON bodyData, int errcode, int mid);
bool getTransOnlineMonitConfigJSON(string jsonStr,TransOnlineMonitConfig_JSON bodyData, int errcode, int mid);
bool getFunctionConfigurationJSON(string jsonStr,FunctionConfiguration_JSON bodyData, int errcode, int mid);
bool getIcingOnlineAlarmMonitConfigJSON(string jsonStr,IcingOnlineAlarmMonitConfig_JSON bodyData, int errcode, int mid);
bool getPhotoParamsJSON(string jsonStr,PhotoParams_JSON bodyData, int errcode, int mid);
bool getCaptureTimesTaskInformationJSON(string jsonStr,CaptureTimesTask_JSON bodyData, int errcode, int mid);
bool getDeviceSolarPowerPropertiesJSON(string jsonStr,DeviceSolarPower_JSON bodyData, int errcode, int mid);
bool getWeatherMonitoringJSON(string jsonStr,Weather_JSON bodyData, int errcode, int mid);
bool getPullAndAngleMonitoringJSON(string jsonStr,PullAndAngle_JSON bodyData, int errcode, int mid);
bool getMicroMeteorologicalAlarmJSON(string jsonStr,Alarm_JSON bodyData, int errcode, int mid);
};
#endif //MQTT_CLIENT_ANDROID_RESPJSON_H

@ -0,0 +1,89 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_ALLOCATOR_H_INCLUDED
#define JSON_ALLOCATOR_H_INCLUDED
#include <cstring>
#include <memory>
#pragma pack(push)
#pragma pack()
namespace Json {
template <typename T> class SecureAllocator {
public:
// Type definitions
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
/**
* Allocate memory for N items using the standard allocator.
*/
pointer allocate(size_type n) {
// allocate using "global operator new"
return static_cast<pointer>(::operator new(n * sizeof(T)));
}
/**
* Release memory which was allocated for N items at pointer P.
*
* The memory block is filled with zeroes before being released.
*/
void deallocate(pointer p, size_type n) {
// memset_s is used because memset may be optimized away by the compiler
memset_s(p, n * sizeof(T), 0, n * sizeof(T));
// free using "global operator delete"
::operator delete(p);
}
/**
* Construct an item in-place at pointer P.
*/
template <typename... Args> void construct(pointer p, Args&&... args) {
// construct using "placement new" and "perfect forwarding"
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
}
size_type max_size() const { return size_t(-1) / sizeof(T); }
pointer address(reference x) const { return std::addressof(x); }
const_pointer address(const_reference x) const { return std::addressof(x); }
/**
* Destroy an item in-place at pointer P.
*/
void destroy(pointer p) {
// destroy using "explicit destructor"
p->~T();
}
// Boilerplate
SecureAllocator() {}
template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
template <typename U> struct rebind { using other = SecureAllocator<U>; };
};
template <typename T, typename U>
bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
return true;
}
template <typename T, typename U>
bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
return false;
}
} // namespace Json
#pragma pack(pop)
#endif // JSON_ALLOCATOR_H_INCLUDED

@ -0,0 +1,61 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_ASSERTIONS_H_INCLUDED
#define JSON_ASSERTIONS_H_INCLUDED
#include <cstdlib>
#include <sstream>
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
/** It should not be possible for a maliciously designed file to
* cause an abort() or seg-fault, so these macros are used only
* for pre-condition violations and internal logic errors.
*/
#if JSON_USE_EXCEPTION
// @todo <= add detail about condition in exception
#define JSON_ASSERT(condition) \
do { \
if (!(condition)) { \
Json::throwLogicError("assert json failed"); \
} \
} while (0)
#define JSON_FAIL_MESSAGE(message) \
do { \
OStringStream oss; \
oss << message; \
Json::throwLogicError(oss.str()); \
abort(); \
} while (0)
#else // JSON_USE_EXCEPTION
#define JSON_ASSERT(condition) assert(condition)
// The call to assert() will show the failure message in debug builds. In
// release builds we abort, for a core-dump or debugger.
#define JSON_FAIL_MESSAGE(message) \
{ \
OStringStream oss; \
oss << message; \
assert(false && oss.str().c_str()); \
abort(); \
}
#endif
#define JSON_ASSERT_MESSAGE(condition, message) \
do { \
if (!(condition)) { \
JSON_FAIL_MESSAGE(message); \
} \
} while (0)
#endif // JSON_ASSERTIONS_H_INCLUDED

@ -0,0 +1,150 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
#include <cstddef>
#include <cstdint>
#include <istream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
// Temporary, tracked for removal with issue #982.
#ifndef JSON_USE_NULLREF
#define JSON_USE_NULLREF 1
#endif
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgamated header.
// #define JSON_IS_AMALGAMATION
// Export macros for DLL visibility
#if defined(JSON_DLL_BUILD)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#elif defined(__GNUC__) || defined(__clang__)
#define JSON_API __attribute__((visibility("default")))
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_DLL_BUILD
#if !defined(JSON_API)
#define JSON_API
#endif
#if defined(_MSC_VER) && _MSC_VER < 1800
#error \
"ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
// As recommended at
// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
const char* format, ...);
#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
#else
#define jsoncpp_snprintf std::snprintf
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
// C++11 should be used directly in JSONCPP.
#define JSONCPP_OVERRIDE override
#ifdef __clang__
#if __has_extension(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
#endif // GNUC version
#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates
// MSVC)
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
#endif // __clang__ || __GNUC__ || _MSC_VER
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#if !defined(JSON_IS_AMALGAMATION)
#include "allocator.h"
#include "version.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
using Int = int;
using UInt = unsigned int;
#if defined(JSON_NO_INT64)
using LargestInt = int;
using LargestUInt = unsigned int;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
using Int64 = __int64;
using UInt64 = unsigned __int64;
#else // if defined(_MSC_VER) // Other platforms, use long long
using Int64 = int64_t;
using UInt64 = uint64_t;
#endif // if defined(_MSC_VER)
using LargestInt = Int64;
using LargestUInt = UInt64;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
template <typename T>
using Allocator =
typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using IStringStream =
std::basic_istringstream<String::value_type, String::traits_type,
String::allocator_type>;
using OStringStream =
std::basic_ostringstream<String::value_type, String::traits_type,
String::allocator_type>;
using IStream = std::istream;
using OStream = std::ostream;
} // namespace Json
// Legacy names (formerly macros).
using JSONCPP_STRING = Json::String;
using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
using JSONCPP_ISTREAM = Json::IStream;
using JSONCPP_OSTREAM = Json::OStream;
#endif // JSON_CONFIG_H_INCLUDED

@ -0,0 +1,43 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class StreamWriter;
class StreamWriterBuilder;
class Writer;
class FastWriter;
class StyledWriter;
class StyledStreamWriter;
// reader.h
class Reader;
class CharReader;
class CharReaderBuilder;
// json_features.h
class Features;
// value.h
using ArrayIndex = unsigned int;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED

@ -0,0 +1,15 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_JSON_H_INCLUDED
#define JSON_JSON_H_INCLUDED
#include "config.h"
#include "json_features.h"
#include "reader.h"
#include "value.h"
#include "writer.h"
#endif // JSON_JSON_H_INCLUDED

@ -0,0 +1,62 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FEATURES_H_INCLUDED
#define JSON_FEATURES_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#pragma pack(push)
#pragma pack()
namespace Json {
/** \brief Configuration passed to reader and writer.
* This configuration object can be used to force the Reader or Writer
* to behave in a standard conforming way.
*/
class JSON_API Features {
public:
/** \brief A configuration that allows all features and assumes all strings
* are UTF-8.
* - C & C++ comments are allowed
* - Root object can be any JSON value
* - Assumes Value strings are encoded in UTF-8
*/
static Features all();
/** \brief A configuration that is strictly compatible with the JSON
* specification.
* - Comments are forbidden.
* - Root object must be either an array or an object value.
* - Assumes Value strings are encoded in UTF-8
*/
static Features strictMode();
/** \brief Initialize the configuration like JsonConfig::allFeatures;
*/
Features();
/// \c true if comments are allowed. Default: \c true.
bool allowComments_{true};
/// \c true if root must be either an array or an object value. Default: \c
/// false.
bool strictRoot_{false};
/// \c true if dropped null placeholders are allowed. Default: \c false.
bool allowDroppedNullPlaceholders_{false};
/// \c true if numeric object key are allowed. Default: \c false.
bool allowNumericKeys_{false};
};
} // namespace Json
#pragma pack(pop)
#endif // JSON_FEATURES_H_INCLUDED

@ -0,0 +1,406 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_READER_H_INCLUDED
#define JSON_READER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "json_features.h"
#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <deque>
#include <iosfwd>
#include <istream>
#include <stack>
#include <string>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push)
#pragma pack()
namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
* Value.
*
* \deprecated Use CharReader and CharReaderBuilder.
*/
class JSON_API Reader {
public:
using Char = char;
using Location = const Char*;
/** \brief An error tagged with where in the JSON text it was encountered.
*
* The offsets give the [start, limit) range of bytes within the text. Note
* that this is bytes, not codepoints.
*/
struct StructuredError {
ptrdiff_t offset_start;
ptrdiff_t offset_limit;
String message;
};
/** \brief Constructs a Reader allowing all features for parsing.
* \deprecated Use CharReader and CharReaderBuilder.
*/
Reader();
/** \brief Constructs a Reader allowing the specified feature set for parsing.
* \deprecated Use CharReader and CharReaderBuilder.
*/
Reader(const Features& features);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
*
* \param document UTF-8 encoded string containing the document
* to read.
* \param[out] root Contains the root value of the document if it
* was successfully parsed.
* \param collectComments \c true to collect comment and allow writing
* them back during serialization, \c false to
* discard comments. This parameter is ignored
* if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
bool parse(const std::string& document, Value& root,
bool collectComments = true);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
*
* \param beginDoc Pointer on the beginning of the UTF-8 encoded
* string of the document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string
* of the document to read. Must be >= beginDoc.
* \param[out] root Contains the root value of the document if it
* was successfully parsed.
* \param collectComments \c true to collect comment and allow writing
* them back during serialization, \c false to
* discard comments. This parameter is ignored
* if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
bool parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments = true);
/// \brief Parse from input stream.
/// \see Json::operator>>(std::istream&, Json::Value&).
bool parse(IStream& is, Value& root, bool collectComments = true);
/** \brief Returns a user friendly string that list errors in the parsed
* document.
*
* \return Formatted error message with the list of errors with their
* location in the parsed document. An empty string is returned if no error
* occurred during parsing.
* \deprecated Use getFormattedErrorMessages() instead (typo fix).
*/
JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
String getFormatedErrorMessages() const;
/** \brief Returns a user friendly string that list errors in the parsed
* document.
*
* \return Formatted error message with the list of errors with their
* location in the parsed document. An empty string is returned if no error
* occurred during parsing.
*/
String getFormattedErrorMessages() const;
/** \brief Returns a vector of structured errors encountered while parsing.
*
* \return A (possibly empty) vector of StructuredError objects. Currently
* only one error can be returned, but the caller should tolerate multiple
* errors. This can occur if the parser recovers from a non-fatal parse
* error and then encounters additional errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
/** \brief Add a semantic error message.
*
* \param value JSON Value location associated with the error
* \param message The error message.
* \return \c true if the error was successfully added, \c false if the Value
* offset exceeds the document size.
*/
bool pushError(const Value& value, const String& message);
/** \brief Add a semantic error message with extra context.
*
* \param value JSON Value location associated with the error
* \param message The error message.
* \param extra Additional JSON Value location to contextualize the error
* \return \c true if the error was successfully added, \c false if either
* Value offset exceeds the document size.
*/
bool pushError(const Value& value, const String& message, const Value& extra);
/** \brief Return whether there are any errors.
*
* \return \c true if there are no errors to report \c false if errors have
* occurred.
*/
bool good() const;
private:
enum TokenType {
tokenEndOfStream = 0,
tokenObjectBegin,
tokenObjectEnd,
tokenArrayBegin,
tokenArrayEnd,
tokenString,
tokenNumber,
tokenTrue,
tokenFalse,
tokenNull,
tokenArraySeparator,
tokenMemberSeparator,
tokenComment,
tokenError
};
class Token {
public:
TokenType type_;
Location start_;
Location end_;
};
class ErrorInfo {
public:
Token token_;
String message_;
Location extra_;
};
using Errors = std::deque<ErrorInfo>;
bool readToken(Token& token);
void skipSpaces();
bool match(const Char* pattern, int patternLength);
bool readComment();
bool readCStyleComment();
bool readCppStyleComment();
bool readString();
void readNumber();
bool readValue();
bool readObject(Token& token);
bool readArray(Token& token);
bool decodeNumber(Token& token);
bool decodeNumber(Token& token, Value& decoded);
bool decodeString(Token& token);
bool decodeString(Token& token, String& decoded);
bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded);
bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
unsigned int& unicode);
bool decodeUnicodeEscapeSequence(Token& token, Location& current,
Location end, unsigned int& unicode);
bool addError(const String& message, Token& token, Location extra = nullptr);
bool recoverFromError(TokenType skipUntilToken);
bool addErrorAndRecover(const String& message, Token& token,
TokenType skipUntilToken);
void skipUntilSpace();
Value& currentValue();
Char getNextChar();
void getLocationLineAndColumn(Location location, int& line,
int& column) const;
String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
static bool containsNewLine(Location begin, Location end);
static String normalizeEOL(Location begin, Location end);
using Nodes = std::stack<Value*>;
Nodes nodes_;
Errors errors_;
String document_;
Location begin_{};
Location end_{};
Location current_{};
Location lastValueEnd_{};
Value* lastValue_{};
String commentsBefore_;
Features features_;
bool collectComments_{};
}; // Reader
/** Interface for reading JSON from a char array.
*/
class JSON_API CharReader {
public:
virtual ~CharReader() = default;
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document. The document must be a UTF-8 encoded string containing the
* document to read.
*
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string
* of the document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
* document to read. Must be >= beginDoc.
* \param[out] root Contains the root value of the document if it was
* successfully parsed.
* \param[out] errs Formatted error messages (if not NULL) a user
* friendly string that lists errors in the parsed
* document.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0;
class JSON_API Factory {
public:
virtual ~Factory() = default;
/** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual CharReader* newCharReader() const = 0;
}; // Factory
}; // CharReader
/** \brief Build a CharReader implementation.
*
* Usage:
* \code
* using namespace Json;
* CharReaderBuilder builder;
* builder["collectComments"] = false;
* Value value;
* String errs;
* bool ok = parseFromStream(builder, std::cin, &value, &errs);
* \endcode
*/
class JSON_API CharReaderBuilder : public CharReader::Factory {
public:
// Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump.
/** Configuration of this builder.
* These are case-sensitive.
* Available settings (case-sensitive):
* - `"collectComments": false or true`
* - true to collect comment and allow writing them back during
* serialization, false to discard comments. This parameter is ignored
* if allowComments is false.
* - `"allowComments": false or true`
* - true if comments are allowed.
* - `"allowTrailingCommas": false or true`
* - true if trailing commas in objects and arrays are allowed.
* - `"strictRoot": false or true`
* - true if root must be either an array or an object value
* - `"allowDroppedNullPlaceholders": false or true`
* - true if dropped null placeholders are allowed. (See
* StreamWriterBuilder.)
* - `"allowNumericKeys": false or true`
* - true if numeric object keys are allowed.
* - `"allowSingleQuotes": false or true`
* - true if '' are allowed for strings (both keys and values)
* - `"stackLimit": integer`
* - Exceeding stackLimit (recursive depth of `readValue()`) will cause an
* exception.
* - This is a security issue (seg-faults caused by deeply nested JSON), so
* the default is low.
* - `"failIfExtra": false or true`
* - If true, `parse()` returns false when extra non-whitespace trails the
* JSON value in the input string.
* - `"rejectDupKeys": false or true`
* - If true, `parse()` returns false when a key is duplicated within an
* object.
* - `"allowSpecialFloats": false or true`
* - If true, special float values (NaNs and infinities) are allowed and
* their values are lossfree restorable.
* - `"skipBom": false or true`
* - If true, if the input starts with the Unicode byte order mark (BOM),
* it is skipped.
*
* You can examine 'settings_` yourself to see the defaults. You can also
* write and read them just like any JSON Value.
* \sa setDefaults()
*/
Json::Value settings_;
CharReaderBuilder();
~CharReaderBuilder() override;
CharReader* newCharReader() const override;
/** \return true if 'settings' are legal and consistent;
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](const String& key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
*/
static void setDefaults(Json::Value* settings);
/** Same as old Features::strictMode().
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
*/
static void strictMode(Json::Value* settings);
};
/** Consume entire stream and use its begin/end.
* Someday we might have a real StreamReader, but for now this
* is convenient.
*/
bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
String* errs);
/** \brief Read from 'sin' into 'root'.
*
* Always keep comments from the input JSON.
*
* This can be used to read a file into a particular sub-object.
* For example:
* \code
* Json::Value root;
* cin >> root["dir"]["file"];
* cout << root;
* \endcode
* Result:
* \verbatim
* {
* "dir": {
* "file": {
* // The input stream JSON would be nested here.
* }
* }
* }
* \endverbatim
* \throw std::exception on parse error.
* \see Json::operator<<()
*/
JSON_API IStream& operator>>(IStream&, Value&);
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_READER_H_INCLUDED

@ -0,0 +1,936 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_H_INCLUDED
#define JSON_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
// Conditional NORETURN attribute on the throw functions would:
// a) suppress false positives from static code analysis
// b) possibly improve optimization opportunities.
#if !defined(JSONCPP_NORETURN)
#if defined(_MSC_VER) && _MSC_VER == 1800
#define JSONCPP_NORETURN __declspec(noreturn)
#else
#define JSONCPP_NORETURN [[noreturn]]
#endif
#endif
// Support for '= delete' with template declarations was a late addition
// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2
// even though these declare themselves to be c++11 compilers.
#if !defined(JSONCPP_TEMPLATE_DELETE)
#if defined(__clang__) && defined(__apple_build_version__)
#if __apple_build_version__ <= 8000042
#define JSONCPP_TEMPLATE_DELETE
#endif
#elif defined(__clang__)
#if __clang_major__ == 3 && __clang_minor__ <= 8
#define JSONCPP_TEMPLATE_DELETE
#endif
#endif
#if !defined(JSONCPP_TEMPLATE_DELETE)
#define JSONCPP_TEMPLATE_DELETE = delete
#endif
#endif
#include <array>
#include <exception>
#include <map>
#include <memory>
#include <string>
#include <vector>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push)
#pragma pack()
/** \brief JSON (JavaScript Object Notation).
*/
namespace Json {
#if JSON_USE_EXCEPTION
/** Base class for all exceptions we throw.
*
* We use nothing but these internally. Of course, STL can throw others.
*/
class JSON_API Exception : public std::exception {
public:
Exception(String msg);
~Exception() noexcept override;
char const* what() const noexcept override;
protected:
String msg_;
};
/** Exceptions which the user cannot easily avoid.
*
* E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
*
* \remark derived from Json::Exception
*/
class JSON_API RuntimeError : public Exception {
public:
RuntimeError(String const& msg);
};
/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
*
* These are precondition-violations (user bugs) and internal errors (our bugs).
*
* \remark derived from Json::Exception
*/
class JSON_API LogicError : public Exception {
public:
LogicError(String const& msg);
};
#endif
/// used internally
JSONCPP_NORETURN void throwRuntimeError(String const& msg);
/// used internally
JSONCPP_NORETURN void throwLogicError(String const& msg);
/** \brief Type of the value held by a Value object.
*/
enum ValueType {
nullValue = 0, ///< 'null' value
intValue, ///< signed integer value
uintValue, ///< unsigned integer value
realValue, ///< double value
stringValue, ///< UTF-8 string value
booleanValue, ///< bool value
arrayValue, ///< array value (ordered list)
objectValue ///< object value (collection of name/value pairs).
};
enum CommentPlacement {
commentBefore = 0, ///< a comment placed on the line before a value
commentAfterOnSameLine, ///< a comment just after a value on the same line
commentAfter, ///< a comment on the line after a value (only make sense for
/// root value)
numberOfCommentPlacement
};
/** \brief Type of precision for formatting of real values.
*/
enum PrecisionType {
significantDigits = 0, ///< we set max number of significant digits in string
decimalPlaces ///< we set max number of digits after "." in string
};
/** \brief Lightweight wrapper to tag static string.
*
* Value constructor and objectValue member assignment takes advantage of the
* StaticString and avoid the cost of string duplication when storing the
* string or the member name.
*
* Example of usage:
* \code
* Json::Value aValue( StaticString("some text") );
* Json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
class JSON_API StaticString {
public:
explicit StaticString(const char* czstring) : c_str_(czstring) {}
operator const char*() const { return c_str_; }
const char* c_str() const { return c_str_; }
private:
const char* c_str_;
};
/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
*
* This class is a discriminated union wrapper that can represents a:
* - signed integer [range: Value::minInt - Value::maxInt]
* - unsigned integer (range: 0 - Value::maxUInt)
* - double
* - UTF-8 string
* - boolean
* - 'null'
* - an ordered list of Value
* - collection of name/value pairs (javascript object)
*
* The type of the held value is represented by a #ValueType and
* can be obtained using type().
*
* Values of an #objectValue or #arrayValue can be accessed using operator[]()
* methods.
* Non-const methods will automatically create the a #nullValue element
* if it does not exist.
* The sequence of an #arrayValue will be automatically resized and initialized
* with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
*
* The get() methods can be used to obtain default value in the case the
* required element does not exist.
*
* It is possible to iterate over the list of member keys of an object using
* the getMemberNames() method.
*
* \note #Value string-length fit in size_t, but keys must be < 2^30.
* (The reason is an implementation detail.) A #CharReader will raise an
* exception if a bound is exceeded to avoid security holes in your app,
* but the Value API does *not* check bounds. That is the responsibility
* of the caller.
*/
class JSON_API Value {
friend class ValueIteratorBase;
public:
using Members = std::vector<String>;
using iterator = ValueIterator;
using const_iterator = ValueConstIterator;
using UInt = Json::UInt;
using Int = Json::Int;
#if defined(JSON_HAS_INT64)
using UInt64 = Json::UInt64;
using Int64 = Json::Int64;
#endif // defined(JSON_HAS_INT64)
using LargestInt = Json::LargestInt;
using LargestUInt = Json::LargestUInt;
using ArrayIndex = Json::ArrayIndex;
// Required for boost integration, e. g. BOOST_TEST
using value_type = std::string;
#if JSON_USE_NULLREF
// Binary compatibility kludges, do not use.
static const Value& null;
static const Value& nullRef;
#endif
// null and nullRef are deprecated, use this instead.
static Value const& nullSingleton();
/// Minimum signed integer value that can be stored in a Json::Value.
static constexpr LargestInt minLargestInt =
LargestInt(~(LargestUInt(-1) / 2));
/// Maximum signed integer value that can be stored in a Json::Value.
static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2);
/// Maximum unsigned integer value that can be stored in a Json::Value.
static constexpr LargestUInt maxLargestUInt = LargestUInt(-1);
/// Minimum signed int value that can be stored in a Json::Value.
static constexpr Int minInt = Int(~(UInt(-1) / 2));
/// Maximum signed int value that can be stored in a Json::Value.
static constexpr Int maxInt = Int(UInt(-1) / 2);
/// Maximum unsigned int value that can be stored in a Json::Value.
static constexpr UInt maxUInt = UInt(-1);
#if defined(JSON_HAS_INT64)
/// Minimum signed 64 bits int value that can be stored in a Json::Value.
static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2));
/// Maximum signed 64 bits int value that can be stored in a Json::Value.
static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2);
/// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
static constexpr UInt64 maxUInt64 = UInt64(-1);
#endif // defined(JSON_HAS_INT64)
/// Default precision for real value for string representation.
static constexpr UInt defaultRealPrecision = 17;
// The constant is hard-coded because some compiler have trouble
// converting Value::maxUInt64 to a double correctly (AIX/xlC).
// Assumes that UInt64 is a 64 bits integer.
static constexpr double maxUInt64AsDouble = 18446744073709551615.0;
// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
// when using gcc and clang backend compilers. CZString
// cannot be defined as private. See issue #486
#ifdef __NVCC__
public:
#else
private:
#endif
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
class CZString {
public:
enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
CZString(ArrayIndex index);
CZString(char const* str, unsigned length, DuplicationPolicy allocate);
CZString(CZString const& other);
CZString(CZString&& other) noexcept;
~CZString();
CZString& operator=(const CZString& other);
CZString& operator=(CZString&& other) noexcept;
bool operator<(CZString const& other) const;
bool operator==(CZString const& other) const;
ArrayIndex index() const;
// const char* c_str() const; ///< \deprecated
char const* data() const;
unsigned length() const;
bool isStaticString() const;
private:
void swap(CZString& other);
struct StringStorage {
unsigned policy_ : 2;
unsigned length_ : 30; // 1GB max
};
char const* cstr_; // actually, a prefixed string, unless policy is noDup
union {
ArrayIndex index_;
StringStorage storage_;
};
};
public:
typedef std::map<CZString, Value> ObjectValues;
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
public:
/**
* \brief Create a default Value of the given type.
*
* This is a very useful constructor.
* To create an empty array, pass arrayValue.
* To create an empty object, pass objectValue.
* Another Value can then be set to this one by assignment.
* This is useful since clear() and resize() will not alter types.
*
* Examples:
* \code
* Json::Value null_value; // null
* Json::Value arr_value(Json::arrayValue); // []
* Json::Value obj_value(Json::objectValue); // {}
* \endcode
*/
Value(ValueType type = nullValue);
Value(Int value);
Value(UInt value);
#if defined(JSON_HAS_INT64)
Value(Int64 value);
Value(UInt64 value);
#endif // if defined(JSON_HAS_INT64)
Value(double value);
Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
/**
* \brief Constructs a value from a static string.
*
* Like other value string constructor but do not duplicate the string for
* internal storage. The given string must remain alive after the call to
* this constructor.
*
* \note This works only for null-terminated strings. (We cannot change the
* size of this class, so we have nowhere to store the length, which might be
* computed later for various operations.)
*
* Example of usage:
* \code
* static StaticString foo("some text");
* Json::Value aValue(foo);
* \endcode
*/
Value(const StaticString& value);
Value(const String& value);
Value(bool value);
Value(std::nullptr_t ptr) = delete;
Value(const Value& other);
Value(Value&& other) noexcept;
~Value();
/// \note Overwrite existing comments. To preserve comments, use
/// #swapPayload().
Value& operator=(const Value& other);
Value& operator=(Value&& other) noexcept;
/// Swap everything.
void swap(Value& other);
/// Swap values but leave comments and source offsets in place.
void swapPayload(Value& other);
/// copy everything.
void copy(const Value& other);
/// copy values but leave comments and source offsets in place.
void copyPayload(const Value& other);
ValueType type() const;
/// Compare payload only, not comments etc.
bool operator<(const Value& other) const;
bool operator<=(const Value& other) const;
bool operator>=(const Value& other) const;
bool operator>(const Value& other) const;
bool operator==(const Value& other) const;
bool operator!=(const Value& other) const;
int compare(const Value& other) const;
const char* asCString() const; ///< Embedded zeroes could cause you trouble!
#if JSONCPP_USING_SECURE_MEMORY
unsigned getCStringLength() const; // Allows you to understand the length of
// the CString
#endif
String asString() const; ///< Embedded zeroes are possible.
/** Get raw char* of string-value.
* \return false if !string. (Seg-fault if str or end are NULL.)
*/
bool getString(char const** begin, char const** end) const;
Int asInt() const;
UInt asUInt() const;
#if defined(JSON_HAS_INT64)
Int64 asInt64() const;
UInt64 asUInt64() const;
#endif // if defined(JSON_HAS_INT64)
LargestInt asLargestInt() const;
LargestUInt asLargestUInt() const;
float asFloat() const;
double asDouble() const;
bool asBool() const;
bool isNull() const;
bool isBool() const;
bool isInt() const;
bool isInt64() const;
bool isUInt() const;
bool isUInt64() const;
bool isIntegral() const;
bool isDouble() const;
bool isNumeric() const;
bool isString() const;
bool isArray() const;
bool isObject() const;
/// The `as<T>` and `is<T>` member function templates and specializations.
template <typename T> T as() const JSONCPP_TEMPLATE_DELETE;
template <typename T> bool is() const JSONCPP_TEMPLATE_DELETE;
bool isConvertibleTo(ValueType other) const;
/// Number of values in array or object
ArrayIndex size() const;
/// \brief Return true if empty array, empty object, or null;
/// otherwise, false.
bool empty() const;
/// Return !isNull()
explicit operator bool() const;
/// Remove all object members and array elements.
/// \pre type() is arrayValue, objectValue, or nullValue
/// \post type() is unchanged
void clear();
/// Resize the array to newSize elements.
/// New elements are initialized to null.
/// May only be called on nullValue or arrayValue.
/// \pre type() is arrayValue or nullValue
/// \post type() is arrayValue
void resize(ArrayIndex newSize);
///@{
/// Access an array element (zero based index). If the array contains less
/// than index element, then null value are inserted in the array so that
/// its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value& operator[](ArrayIndex index);
Value& operator[](int index);
///@}
///@{
/// Access an array element (zero based index).
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
const Value& operator[](ArrayIndex index) const;
const Value& operator[](int index) const;
///@}
/// If the array contains at least index+1 elements, returns the element
/// value, otherwise returns defaultValue.
Value get(ArrayIndex index, const Value& defaultValue) const;
/// Return true if index < size().
bool isValidIndex(ArrayIndex index) const;
/// \brief Append value to array at the end.
///
/// Equivalent to jsonvalue[jsonvalue.size()] = value;
Value& append(const Value& value);
Value& append(Value&& value);
/// \brief Insert value in array at specific index
bool insert(ArrayIndex index, const Value& newValue);
bool insert(ArrayIndex index, Value&& newValue);
/// Access an object value by name, create a null member if it does not exist.
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
/// Exceeding that will cause an exception.
Value& operator[](const char* key);
/// Access an object value by name, returns null if there is no member with
/// that name.
const Value& operator[](const char* key) const;
/// Access an object value by name, create a null member if it does not exist.
/// \param key may contain embedded nulls.
Value& operator[](const String& key);
/// Access an object value by name, returns null if there is no member with
/// that name.
/// \param key may contain embedded nulls.
const Value& operator[](const String& key) const;
/** \brief Access an object value by name, create a null member if it does not
* exist.
*
* If the object has no entry for that name, then the member name used to
* store the new entry is not duplicated.
* Example of use:
* \code
* Json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
Value& operator[](const StaticString& key);
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
Value get(const char* key, const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \note key may contain embedded nulls.
Value get(const char* begin, const char* end,
const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \param key may contain embedded nulls.
Value get(const String& key, const Value& defaultValue) const;
/// Most general and efficient version of isMember()const, get()const,
/// and operator[]const
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
Value const* find(char const* begin, char const* end) const;
/// Most general and efficient version of object-mutators.
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
/// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
Value* demand(char const* begin, char const* end);
/// \brief Remove and return the named member.
///
/// Do nothing if it did not exist.
/// \pre type() is objectValue or nullValue
/// \post type() is unchanged
void removeMember(const char* key);
/// Same as removeMember(const char*)
/// \param key may contain embedded nulls.
void removeMember(const String& key);
/// Same as removeMember(const char* begin, const char* end, Value* removed),
/// but 'key' is null-terminated.
bool removeMember(const char* key, Value* removed);
/** \brief Remove the named map member.
*
* Update 'removed' iff removed.
* \param key may contain embedded nulls.
* \return true iff removed (no exceptions)
*/
bool removeMember(String const& key, Value* removed);
/// Same as removeMember(String const& key, Value* removed)
bool removeMember(const char* begin, const char* end, Value* removed);
/** \brief Remove the indexed array element.
*
* O(n) expensive operations.
* Update 'removed' iff removed.
* \return true if removed (no exceptions)
*/
bool removeIndex(ArrayIndex index, Value* removed);
/// Return true if the object has a member named key.
/// \note 'key' must be null-terminated.
bool isMember(const char* key) const;
/// Return true if the object has a member named key.
/// \param key may contain embedded nulls.
bool isMember(const String& key) const;
/// Same as isMember(String const& key)const
bool isMember(const char* begin, const char* end) const;
/// \brief Return a list of the member names.
///
/// If null, return an empty list.
/// \pre type() is objectValue or nullValue
/// \post if type() was nullValue, it remains nullValue
Members getMemberNames() const;
/// \deprecated Always pass len.
JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
void setComment(const char* comment, CommentPlacement placement) {
setComment(String(comment, strlen(comment)), placement);
}
/// Comments must be //... or /* ... */
void setComment(const char* comment, size_t len, CommentPlacement placement) {
setComment(String(comment, len), placement);
}
/// Comments must be //... or /* ... */
void setComment(String comment, CommentPlacement placement);
bool hasComment(CommentPlacement placement) const;
/// Include delimiters and embedded newlines.
String getComment(CommentPlacement placement) const;
String toStyledString() const;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
// Accessors for the [start, limit) range of bytes within the JSON text from
// which this value was parsed, if any.
void setOffsetStart(ptrdiff_t start);
void setOffsetLimit(ptrdiff_t limit);
ptrdiff_t getOffsetStart() const;
ptrdiff_t getOffsetLimit() const;
private:
void setType(ValueType v) {
bits_.value_type_ = static_cast<unsigned char>(v);
}
bool isAllocated() const { return bits_.allocated_; }
void setIsAllocated(bool v) { bits_.allocated_ = v; }
void initBasic(ValueType type, bool allocated = false);
void dupPayload(const Value& other);
void releasePayload();
void dupMeta(const Value& other);
Value& resolveReference(const char* key);
Value& resolveReference(const char* key, const char* end);
// struct MemberNamesTransform
//{
// typedef const char *result_type;
// const char *operator()( const CZString &name ) const
// {
// return name.c_str();
// }
//};
union ValueHolder {
LargestInt int_;
LargestUInt uint_;
double real_;
bool bool_;
char* string_; // if allocated_, ptr to { unsigned, char[] }.
ObjectValues* map_;
} value_;
struct {
// Really a ValueType, but types should agree for bitfield packing.
unsigned int value_type_ : 8;
// Unless allocated_, string_ must be null-terminated.
unsigned int allocated_ : 1;
} bits_;
class Comments {
public:
Comments() = default;
Comments(const Comments& that);
Comments(Comments&& that) noexcept;
Comments& operator=(const Comments& that);
Comments& operator=(Comments&& that) noexcept;
bool has(CommentPlacement slot) const;
String get(CommentPlacement slot) const;
void set(CommentPlacement slot, String comment);
private:
using Array = std::array<String, numberOfCommentPlacement>;
std::unique_ptr<Array> ptr_;
};
Comments comments_;
// [start, limit) byte offsets in the source JSON text from which this Value
// was extracted.
ptrdiff_t start_;
ptrdiff_t limit_;
};
template <> inline bool Value::as<bool>() const { return asBool(); }
template <> inline bool Value::is<bool>() const { return isBool(); }
template <> inline Int Value::as<Int>() const { return asInt(); }
template <> inline bool Value::is<Int>() const { return isInt(); }
template <> inline UInt Value::as<UInt>() const { return asUInt(); }
template <> inline bool Value::is<UInt>() const { return isUInt(); }
#if defined(JSON_HAS_INT64)
template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
template <> inline bool Value::is<Int64>() const { return isInt64(); }
template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
#endif
template <> inline double Value::as<double>() const { return asDouble(); }
template <> inline bool Value::is<double>() const { return isDouble(); }
template <> inline String Value::as<String>() const { return asString(); }
template <> inline bool Value::is<String>() const { return isString(); }
/// These `as` specializations are type conversions, and do not have a
/// corresponding `is`.
template <> inline float Value::as<float>() const { return asFloat(); }
template <> inline const char* Value::as<const char*>() const {
return asCString();
}
/** \brief Experimental and untested: represents an element of the "path" to
* access a node.
*/
class JSON_API PathArgument {
public:
friend class Path;
PathArgument();
PathArgument(ArrayIndex index);
PathArgument(const char* key);
PathArgument(String key);
private:
enum Kind { kindNone = 0, kindIndex, kindKey };
String key_;
ArrayIndex index_{};
Kind kind_{kindNone};
};
/** \brief Experimental and untested: represents a "path" to access a node.
*
* Syntax:
* - "." => root node
* - ".[n]" => elements at index 'n' of root node (an array value)
* - ".name" => member named 'name' of root node (an object value)
* - ".name1.name2.name3"
* - ".[0][1][2].name1[3]"
* - ".%" => member name is provided as parameter
* - ".[%]" => index is provided as parameter
*/
class JSON_API Path {
public:
Path(const String& path, const PathArgument& a1 = PathArgument(),
const PathArgument& a2 = PathArgument(),
const PathArgument& a3 = PathArgument(),
const PathArgument& a4 = PathArgument(),
const PathArgument& a5 = PathArgument());
const Value& resolve(const Value& root) const;
Value resolve(const Value& root, const Value& defaultValue) const;
/// Creates the "path" to access the specified node and returns a reference on
/// the node.
Value& make(Value& root) const;
private:
using InArgs = std::vector<const PathArgument*>;
using Args = std::vector<PathArgument>;
void makePath(const String& path, const InArgs& in);
void addPathInArg(const String& path, const InArgs& in,
InArgs::const_iterator& itInArg, PathArgument::Kind kind);
static void invalidPath(const String& path, int location);
Args args_;
};
/** \brief base class for Value iterators.
*
*/
class JSON_API ValueIteratorBase {
public:
using iterator_category = std::bidirectional_iterator_tag;
using size_t = unsigned int;
using difference_type = int;
using SelfType = ValueIteratorBase;
bool operator==(const SelfType& other) const { return isEqual(other); }
bool operator!=(const SelfType& other) const { return !isEqual(other); }
difference_type operator-(const SelfType& other) const {
return other.computeDistance(*this);
}
/// Return either the index or the member name of the referenced value as a
/// Value.
Value key() const;
/// Return the index of the referenced Value, or -1 if it is not an
/// arrayValue.
UInt index() const;
/// Return the member name of the referenced Value, or "" if it is not an
/// objectValue.
/// \note Avoid `c_str()` on result, as embedded zeroes are possible.
String name() const;
/// Return the member name of the referenced Value. "" if it is not an
/// objectValue.
/// \deprecated This cannot be used for UTF-8 strings, since there can be
/// embedded nulls.
JSONCPP_DEPRECATED("Use `key = name();` instead.")
char const* memberName() const;
/// Return the member name of the referenced Value, or NULL if it is not an
/// objectValue.
/// \note Better version than memberName(). Allows embedded nulls.
char const* memberName(char const** end) const;
protected:
/*! Internal utility functions to assist with implementing
* other iterator functions. The const and non-const versions
* of the "deref" protected methods expose the protected
* current_ member variable in a way that can often be
* optimized away by the compiler.
*/
const Value& deref() const;
Value& deref();
void increment();
void decrement();
difference_type computeDistance(const SelfType& other) const;
bool isEqual(const SelfType& other) const;
void copy(const SelfType& other);
private:
Value::ObjectValues::iterator current_;
// Indicates that iterator is for a null value.
bool isNull_{true};
public:
// For some reason, BORLAND needs these at the end, rather
// than earlier. No idea why.
ValueIteratorBase();
explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
};
/** \brief const iterator for object and array value.
*
*/
class JSON_API ValueConstIterator : public ValueIteratorBase {
friend class Value;
public:
using value_type = const Value;
// typedef unsigned int size_t;
// typedef int difference_type;
using reference = const Value&;
using pointer = const Value*;
using SelfType = ValueConstIterator;
ValueConstIterator();
ValueConstIterator(ValueIterator const& other);
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
public:
SelfType& operator=(const ValueIteratorBase& other);
SelfType operator++(int) {
SelfType temp(*this);
++*this;
return temp;
}
SelfType operator--(int) {
SelfType temp(*this);
--*this;
return temp;
}
SelfType& operator--() {
decrement();
return *this;
}
SelfType& operator++() {
increment();
return *this;
}
reference operator*() const { return deref(); }
pointer operator->() const { return &deref(); }
};
/** \brief Iterator for object and array value.
*/
class JSON_API ValueIterator : public ValueIteratorBase {
friend class Value;
public:
using value_type = Value;
using size_t = unsigned int;
using difference_type = int;
using reference = Value&;
using pointer = Value*;
using SelfType = ValueIterator;
ValueIterator();
explicit ValueIterator(const ValueConstIterator& other);
ValueIterator(const ValueIterator& other);
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueIterator(const Value::ObjectValues::iterator& current);
public:
SelfType& operator=(const SelfType& other);
SelfType operator++(int) {
SelfType temp(*this);
++*this;
return temp;
}
SelfType operator--(int) {
SelfType temp(*this);
--*this;
return temp;
}
SelfType& operator--() {
decrement();
return *this;
}
SelfType& operator++() {
increment();
return *this;
}
/*! The return value of non-const iterators can be
* changed, so the these functions are not const
* because the returned references/pointers can be used
* to change state of the base class.
*/
reference operator*() const { return const_cast<reference>(deref()); }
pointer operator->() const { return const_cast<pointer>(&deref()); }
};
inline void swap(Value& a, Value& b) { a.swap(b); }
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_H_INCLUDED

@ -0,0 +1,28 @@
#ifndef JSON_VERSION_H_INCLUDED
#define JSON_VERSION_H_INCLUDED
// Note: version must be updated in three places when doing a release. This
// annoying process ensures that amalgamate, CMake, and meson all report the
// correct version.
// 1. /meson.build
// 2. /include/json/version.h
// 3. /CMakeLists.txt
// IMPORTANT: also update the SOVERSION!!
#define JSONCPP_VERSION_STRING "1.9.5"
#define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 9
#define JSONCPP_VERSION_PATCH 5
#define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
(JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY
#undef JSONCPP_USING_SECURE_MEMORY
#endif
#define JSONCPP_USING_SECURE_MEMORY 0
// If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory.
#endif // JSON_VERSION_H_INCLUDED

@ -0,0 +1,370 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_WRITER_H_INCLUDED
#define JSON_WRITER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <ostream>
#include <string>
#include <vector>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push)
#pragma pack()
namespace Json {
class Value;
/**
*
* Usage:
* \code
* using namespace Json;
* void writeToStdout(StreamWriter::Factory const& factory, Value const& value)
* { std::unique_ptr<StreamWriter> const writer( factory.newStreamWriter());
* writer->write(value, &std::cout);
* std::cout << std::endl; // add lf and flush
* }
* \endcode
*/
class JSON_API StreamWriter {
protected:
OStream* sout_; // not owned; will not delete
public:
StreamWriter();
virtual ~StreamWriter();
/** Write Value into document as configured in sub-class.
* Do not take ownership of sout, but maintain a reference during function.
* \pre sout != NULL
* \return zero on success (For now, we always return zero, so check the
* stream instead.) \throw std::exception possibly, depending on
* configuration
*/
virtual int write(Value const& root, OStream* sout) = 0;
/** \brief A simple abstract factory.
*/
class JSON_API Factory {
public:
virtual ~Factory();
/** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual StreamWriter* newStreamWriter() const = 0;
}; // Factory
}; // StreamWriter
/** \brief Write into stringstream, then return string, for convenience.
* A StreamWriter will be created from the factory, used, and then deleted.
*/
String JSON_API writeString(StreamWriter::Factory const& factory,
Value const& root);
/** \brief Build a StreamWriter implementation.
* Usage:
* \code
* using namespace Json;
* Value value = ...;
* StreamWriterBuilder builder;
* builder["commentStyle"] = "None";
* builder["indentation"] = " "; // or whatever you like
* std::unique_ptr<Json::StreamWriter> writer(
* builder.newStreamWriter());
* writer->write(value, &std::cout);
* std::cout << std::endl; // add lf and flush
* \endcode
*/
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
public:
// Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump.
/** Configuration of this builder.
* Available settings (case-sensitive):
* - "commentStyle": "None" or "All"
* - "indentation": "<anything>".
* - Setting this to an empty string also omits newline characters.
* - "enableYAMLCompatibility": false or true
* - slightly change the whitespace around colons
* - "dropNullPlaceholders": false or true
* - Drop the "null" string from the writer's output for nullValues.
* Strictly speaking, this is not valid JSON. But when the output is being
* fed to a browser's JavaScript, it makes for smaller output and the
* browser can handle the output just fine.
* - "useSpecialFloats": false or true
* - If true, outputs non-finite floating point values in the following way:
* NaN values as "NaN", positive infinity as "Infinity", and negative
* infinity as "-Infinity".
* - "precision": int
* - Number of precision digits for formatting of real values.
* - "precisionType": "significant"(default) or "decimal"
* - Type of precision for formatting of real values.
* - "emitUTF8": false or true
* - If true, outputs raw UTF8 strings instead of escaping them.
* You can examine 'settings_` yourself
* to see the defaults. You can also write and read them just like any
* JSON Value.
* \sa setDefaults()
*/
Json::Value settings_;
StreamWriterBuilder();
~StreamWriterBuilder() override;
/**
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
StreamWriter* newStreamWriter() const override;
/** \return true if 'settings' are legal and consistent;
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](const String& key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
*/
static void setDefaults(Json::Value* settings);
};
/** \brief Abstract class for writers.
* \deprecated Use StreamWriter. (And really, this is an implementation detail.)
*/
class JSON_API Writer {
public:
virtual ~Writer();
virtual String write(const Value& root) = 0;
};
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
*without formatting (not human friendly).
*
* The JSON document is written in a single line. It is not intended for 'human'
*consumption,
* but may be useful to support feature such as RPC where bandwidth is limited.
* \sa Reader, Value
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSON_API FastWriter
: public Writer {
public:
FastWriter();
~FastWriter() override = default;
void enableYAMLCompatibility();
/** \brief Drop the "null" string from the writer's output for nullValues.
* Strictly speaking, this is not valid JSON. But when the output is being
* fed to a browser's JavaScript, it makes for smaller output and the
* browser can handle the output just fine.
*/
void dropNullPlaceholders();
void omitEndingLineFeed();
public: // overridden from Writer
String write(const Value& root) override;
private:
void writeValue(const Value& value);
String document_;
bool yamlCompatibilityEnabled_{false};
bool dropNullPlaceholders_{false};
bool omitEndingLineFeed_{false};
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
*human friendly way.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per
*line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value
*types,
* and all the values fit on one lines, then print the array on a single
*line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputted according to their
*#CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSON_API
StyledWriter : public Writer {
public:
StyledWriter();
~StyledWriter() override = default;
public: // overridden from Writer
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param root Value to serialize.
* \return String containing the JSON document that represents the root value.
*/
String write(const Value& root) override;
private:
void writeValue(const Value& value);
void writeArrayValue(const Value& value);
bool isMultilineArray(const Value& value);
void pushValue(const String& value);
void writeIndent();
void writeWithIndent(const String& value);
void indent();
void unindent();
void writeCommentBeforeValue(const Value& root);
void writeCommentAfterValueOnSameLine(const Value& root);
static bool hasCommentForValue(const Value& value);
static String normalizeEOL(const String& text);
using ChildValues = std::vector<String>;
ChildValues childValues_;
String document_;
String indentString_;
unsigned int rightMargin_{74};
unsigned int indentSize_{3};
bool addChildValues_{false};
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
human friendly way,
to a stream rather than to a string.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per
line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value
types,
* and all the values fit on one lines, then print the array on a single
line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputted according to their
#CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSON_API
StyledStreamWriter {
public:
/**
* \param indentation Each level will be indented by this amount extra.
*/
StyledStreamWriter(String indentation = "\t");
~StyledStreamWriter() = default;
public:
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param out Stream to write to. (Can be ostringstream, e.g.)
* \param root Value to serialize.
* \note There is no point in deriving from Writer, since write() should not
* return a value.
*/
void write(OStream& out, const Value& root);
private:
void writeValue(const Value& value);
void writeArrayValue(const Value& value);
bool isMultilineArray(const Value& value);
void pushValue(const String& value);
void writeIndent();
void writeWithIndent(const String& value);
void indent();
void unindent();
void writeCommentBeforeValue(const Value& root);
void writeCommentAfterValueOnSameLine(const Value& root);
static bool hasCommentForValue(const Value& value);
static String normalizeEOL(const String& text);
using ChildValues = std::vector<String>;
ChildValues childValues_;
OStream* document_;
String indentString_;
unsigned int rightMargin_{74};
String indentation_;
bool addChildValues_ : 1;
bool indented_ : 1;
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#if defined(JSON_HAS_INT64)
String JSON_API valueToString(Int value);
String JSON_API valueToString(UInt value);
#endif // if defined(JSON_HAS_INT64)
String JSON_API valueToString(LargestInt value);
String JSON_API valueToString(LargestUInt value);
String JSON_API valueToString(
double value, unsigned int precision = Value::defaultRealPrecision,
PrecisionType precisionType = PrecisionType::significantDigits);
String JSON_API valueToString(bool value);
String JSON_API valueToQuotedString(const char* value);
/// \brief Output using the StyledStreamWriter.
/// \see Json::operator>>()
JSON_API OStream& operator<<(OStream&, const Value& root);
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_WRITER_H_INCLUDED

File diff suppressed because it is too large Load Diff

@ -0,0 +1,138 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include <json/config.h>
#endif
// Also support old flag NO_LOCALE_SUPPORT
#ifdef NO_LOCALE_SUPPORT
#define JSONCPP_NO_LOCALE_SUPPORT
#endif
#ifndef JSONCPP_NO_LOCALE_SUPPORT
#include <clocale>
#endif
/* This header provides common string manipulation support, such as UTF-8,
* portable conversion from/to string...
*
* It is an internal header that must not be exposed.
*/
namespace Json {
static inline char getDecimalPoint() {
#ifdef JSONCPP_NO_LOCALE_SUPPORT
return '\0';
#else
struct lconv* lc = localeconv();
return lc ? *(lc->decimal_point) : '\0';
#endif
}
/// Converts a unicode code-point to UTF-8.
static inline String codePointToUTF8(unsigned int cp) {
String result;
// based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f) {
result.resize(1);
result[0] = static_cast<char>(cp);
} else if (cp <= 0x7FF) {
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
} else if (cp <= 0xFFFF) {
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
} else if (cp <= 0x10FFFF) {
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
enum {
/// Constant that specify the size of the buffer that must be passed to
/// uintToString.
uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
};
// Defines a char buffer for use with uintToString().
using UIntToStringBuffer = char[uintToStringBufferSize];
/** Converts an unsigned integer to string.
* @param value Unsigned integer to convert to string
* @param current Input/Output string buffer.
* Must have at least uintToStringBufferSize chars free.
*/
static inline void uintToString(LargestUInt value, char*& current) {
*--current = 0;
do {
*--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
value /= 10;
} while (value != 0);
}
/** Change ',' to '.' everywhere in buffer.
*
* We had a sophisticated way, but it did not work in WinCE.
* @see https://github.com/open-source-parsers/jsoncpp/pull/9
*/
template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
for (; begin != end; ++begin) {
if (*begin == ',') {
*begin = '.';
}
}
return begin;
}
template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
char decimalPoint = getDecimalPoint();
if (decimalPoint == '\0' || decimalPoint == '.') {
return;
}
for (; begin != end; ++begin) {
if (*begin == '.') {
*begin = decimalPoint;
}
}
}
/**
* Return iterator that would be the new end of the range [begin,end), if we
* were to delete zeros in the end of string, but not the last zero before '.'.
*/
template <typename Iter>
Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) {
for (; begin != end; --end) {
if (*(end - 1) != '0') {
return end;
}
// Don't delete the last zero before the decimal point.
if (begin != (end - 1) && begin != (end - 2) && *(end - 2) == '.') {
if (precision) {
return end;
}
return end - 2;
}
}
return end;
}
} // namespace Json
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED

File diff suppressed because it is too large Load Diff

@ -0,0 +1,156 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
// included by json_value.cpp
namespace Json {
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIteratorBase
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIteratorBase::ValueIteratorBase() : current_() {}
ValueIteratorBase::ValueIteratorBase(
const Value::ObjectValues::iterator& current)
: current_(current), isNull_(false) {}
Value& ValueIteratorBase::deref() { return current_->second; }
const Value& ValueIteratorBase::deref() const { return current_->second; }
void ValueIteratorBase::increment() { ++current_; }
void ValueIteratorBase::decrement() { --current_; }
ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance(const SelfType& other) const {
// Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default
// std::map::iterator. As begin() and end() are two instance
// of the default std::map::iterator, they can not be compared.
// To allow this, we handle this comparison specifically.
if (isNull_ && other.isNull_) {
return 0;
}
// Usage of std::distance is not portable (does not compile with Sun Studio 12
// RogueWave STL,
// which is the one used by default).
// Using a portable hand-made version for non random iterator instead:
// return difference_type( std::distance( current_, other.current_ ) );
difference_type myDistance = 0;
for (Value::ObjectValues::iterator it = current_; it != other.current_;
++it) {
++myDistance;
}
return myDistance;
}
bool ValueIteratorBase::isEqual(const SelfType& other) const {
if (isNull_) {
return other.isNull_;
}
return current_ == other.current_;
}
void ValueIteratorBase::copy(const SelfType& other) {
current_ = other.current_;
isNull_ = other.isNull_;
}
Value ValueIteratorBase::key() const {
const Value::CZString czstring = (*current_).first;
if (czstring.data()) {
if (czstring.isStaticString())
return Value(StaticString(czstring.data()));
return Value(czstring.data(), czstring.data() + czstring.length());
}
return Value(czstring.index());
}
UInt ValueIteratorBase::index() const {
const Value::CZString czstring = (*current_).first;
if (!czstring.data())
return czstring.index();
return Value::UInt(-1);
}
String ValueIteratorBase::name() const {
char const* keey;
char const* end;
keey = memberName(&end);
if (!keey)
return String();
return String(keey, end);
}
char const* ValueIteratorBase::memberName() const {
const char* cname = (*current_).first.data();
return cname ? cname : "";
}
char const* ValueIteratorBase::memberName(char const** end) const {
const char* cname = (*current_).first.data();
if (!cname) {
*end = nullptr;
return nullptr;
}
*end = cname + (*current_).first.length();
return cname;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueConstIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueConstIterator::ValueConstIterator() = default;
ValueConstIterator::ValueConstIterator(
const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueConstIterator::ValueConstIterator(ValueIterator const& other)
: ValueIteratorBase(other) {}
ValueConstIterator& ValueConstIterator::
operator=(const ValueIteratorBase& other) {
copy(other);
return *this;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIterator::ValueIterator() = default;
ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueIterator::ValueIterator(const ValueConstIterator& other)
: ValueIteratorBase(other) {
throwRuntimeError("ConstIterator to Iterator should never be allowed.");
}
ValueIterator::ValueIterator(const ValueIterator& other) = default;
ValueIterator& ValueIterator::operator=(const SelfType& other) {
copy(other);
return *this;
}
} // namespace Json

File diff suppressed because it is too large Load Diff

@ -0,0 +1,72 @@
#ifndef CONFIG_H
#define CONFIG_H
/* ============================================================
* Platform options
* ============================================================ */
#ifdef __APPLE__
# define __DARWIN_C_SOURCE
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__SYMBIAN32__) || defined(__QNX__)
# define _XOPEN_SOURCE 700
# define __BSD_VISIBLE 1
# define HAVE_NETINET_IN_H
#else
# define _XOPEN_SOURCE 700
# define _DEFAULT_SOURCE 1
# define _POSIX_C_SOURCE 200809L
#endif
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#define OPENSSL_LOAD_CONF
/* ============================================================
* Compatibility defines
* ============================================================ */
#if defined(_MSC_VER) && _MSC_VER < 1900
# define snprintf sprintf_s
# define EPROTO ECONNABORTED
#endif
#ifdef WIN32
# ifndef strcasecmp
# define strcasecmp strcmpi
# endif
# define strtok_r strtok_s
# define strerror_r(e, b, l) strerror_s(b, l, e)
#endif
#define uthash_malloc(sz) mosquitto__malloc(sz)
#define uthash_free(ptr,sz) mosquitto__free(ptr)
#ifdef WITH_TLS
# include <openssl/conf.h>
# if defined(WITH_TLS_PSK) && !defined(OPENSSL_NO_PSK)
# define FINAL_WITH_TLS_PSK
# endif
#endif
#ifdef __COVERITY__
# include <stdint.h>
/* These are "wrong", but we don't use them so it doesn't matter */
# define _Float32 uint32_t
# define _Float32x uint32_t
# define _Float64 uint64_t
# define _Float64x uint64_t
# define _Float128 uint64_t
#endif
#define UNUSED(A) (void)(A)
/* Android Bionic libpthread implementation doesn't have pthread_cancel */
#ifndef ANDROID
# define HAVE_PTHREAD_CANCEL
#endif
#endif

@ -0,0 +1,274 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <string.h>
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "send_mosq.h"
#include "util_mosq.h"
int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain)
{
return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL);
}
int mosquitto_publish_v5(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties)
{
struct mosquitto_message_all *message;
uint16_t local_mid;
const mosquitto_property *p;
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property *properties_copy = NULL;
mosquitto_property local_property;
bool have_topic_alias;
int rc;
int tlen = 0;
uint32_t remaining_length;
if(!mosq || qos<0 || qos>2) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(qos > mosq->maximum_qos) return MOSQ_ERR_QOS_NOT_SUPPORTED;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_PUBLISH, outgoing_properties);
if(rc) return rc;
}
if(!topic || STREMPTY(topic)){
if(topic) topic = NULL;
if(mosq->protocol == mosq_p_mqtt5){
p = outgoing_properties;
have_topic_alias = false;
while(p){
if(p->identifier == MQTT_PROP_TOPIC_ALIAS){
have_topic_alias = true;
break;
}
p = p->next;
}
if(have_topic_alias == false){
return MOSQ_ERR_INVAL;
}
}else{
return MOSQ_ERR_INVAL;
}
}else{
tlen = strlen(topic);
if(mosquitto_validate_utf8(topic, tlen)) return MOSQ_ERR_MALFORMED_UTF8;
if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE;
if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){
return MOSQ_ERR_INVAL;
}
}
if(mosq->maximum_packet_size > 0){
remaining_length = 1 + 2+tlen + payloadlen + property__get_length_all(outgoing_properties);
if(qos > 0){
remaining_length++;
}
if(packet__check_oversize(mosq, remaining_length)){
return MOSQ_ERR_OVERSIZE_PACKET;
}
}
local_mid = mosquitto__mid_generate(mosq);
if(mid){
*mid = local_mid;
}
if(qos == 0){
return send__publish(mosq, local_mid, topic, payloadlen, payload, qos, retain, false, outgoing_properties, NULL, 0);
}else{
if(outgoing_properties){
rc = mosquitto_property_copy_all(&properties_copy, outgoing_properties);
if(rc) return rc;
}
message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all));
if(!message){
mosquitto_property_free_all(&properties_copy);
return MOSQ_ERR_NOMEM;
}
message->next = NULL;
message->timestamp = mosquitto_time();
message->msg.mid = local_mid;
if(topic){
message->msg.topic = mosquitto__strdup(topic);
if(!message->msg.topic){
message__cleanup(&message);
mosquitto_property_free_all(&properties_copy);
return MOSQ_ERR_NOMEM;
}
}
if(payloadlen){
message->msg.payloadlen = payloadlen;
message->msg.payload = mosquitto__malloc(payloadlen*sizeof(uint8_t));
if(!message->msg.payload){
message__cleanup(&message);
mosquitto_property_free_all(&properties_copy);
return MOSQ_ERR_NOMEM;
}
memcpy(message->msg.payload, payload, payloadlen*sizeof(uint8_t));
}else{
message->msg.payloadlen = 0;
message->msg.payload = NULL;
}
message->msg.qos = qos;
message->msg.retain = retain;
message->dup = false;
message->properties = properties_copy;
pthread_mutex_lock(&mosq->msgs_out.mutex);
message->state = mosq_ms_invalid;
message__queue(mosq, message, mosq_md_out);
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_SUCCESS;
}
}
int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos)
{
return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, 0, NULL);
}
int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties)
{
return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, options, properties);
}
int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties)
{
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property local_property;
int i;
int rc;
uint32_t remaining_length = 0;
int slen;
if(!mosq || !sub_count || !sub) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(qos < 0 || qos > 2) return MOSQ_ERR_INVAL;
if((options & 0x30) == 0x30 || (options & 0xC0) != 0) return MOSQ_ERR_INVAL;
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_SUBSCRIBE, outgoing_properties);
if(rc) return rc;
}
for(i=0; i<sub_count; i++){
if(mosquitto_sub_topic_check(sub[i])) return MOSQ_ERR_INVAL;
slen = strlen(sub[i]);
if(mosquitto_validate_utf8(sub[i], slen)) return MOSQ_ERR_MALFORMED_UTF8;
remaining_length += 2+slen + 1;
}
if(mosq->maximum_packet_size > 0){
remaining_length += 2 + property__get_length_all(outgoing_properties);
if(packet__check_oversize(mosq, remaining_length)){
return MOSQ_ERR_OVERSIZE_PACKET;
}
}
if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){
options = 0;
}
return send__subscribe(mosq, mid, sub_count, sub, qos|options, outgoing_properties);
}
int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub)
{
return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, NULL);
}
int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties)
{
return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, properties);
}
int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties)
{
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property local_property;
int rc;
int i;
uint32_t remaining_length = 0;
int slen;
if(!mosq) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, outgoing_properties);
if(rc) return rc;
}
for(i=0; i<sub_count; i++){
if(mosquitto_sub_topic_check(sub[i])) return MOSQ_ERR_INVAL;
slen = strlen(sub[i]);
if(mosquitto_validate_utf8(sub[i], slen)) return MOSQ_ERR_MALFORMED_UTF8;
remaining_length += 2+slen;
}
if(mosq->maximum_packet_size > 0){
remaining_length += 2 + property__get_length_all(outgoing_properties);
if(packet__check_oversize(mosq, remaining_length)){
return MOSQ_ERR_OVERSIZE_PACKET;
}
}
return send__unsubscribe(mosq, mid, sub_count, sub, outgoing_properties);
}

@ -0,0 +1,85 @@
/*
Copyright (c) 2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include "mosquitto.h"
#include "alias_mosq.h"
#include "memory_mosq.h"
int alias__add(struct mosquitto *mosq, const char *topic, int alias)
{
int i;
struct mosquitto__alias *aliases;
for(i=0; i<mosq->alias_count; i++){
if(mosq->aliases[i].alias == alias){
mosquitto__free(mosq->aliases[i].topic);
mosq->aliases[i].topic = mosquitto__strdup(topic);
if(mosq->aliases[i].topic){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOMEM;
}
}
}
/* New alias */
aliases = mosquitto__realloc(mosq->aliases, sizeof(struct mosquitto__alias)*(mosq->alias_count+1));
if(!aliases) return MOSQ_ERR_NOMEM;
mosq->aliases = aliases;
mosq->aliases[mosq->alias_count].alias = alias;
mosq->aliases[mosq->alias_count].topic = mosquitto__strdup(topic);
if(!mosq->aliases[mosq->alias_count].topic){
return MOSQ_ERR_NOMEM;
}
mosq->alias_count++;
return MOSQ_ERR_SUCCESS;
}
int alias__find(struct mosquitto *mosq, char **topic, int alias)
{
int i;
for(i=0; i<mosq->alias_count; i++){
if(mosq->aliases[i].alias == alias){
*topic = mosquitto__strdup(mosq->aliases[i].topic);
if(*topic){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOMEM;
}
}
}
return MOSQ_ERR_INVAL;
}
void alias__free_all(struct mosquitto *mosq)
{
int i;
for(i=0; i<mosq->alias_count; i++){
mosquitto__free(mosq->aliases[i].topic);
}
mosquitto__free(mosq->aliases);
mosq->aliases = NULL;
mosq->alias_count = 0;
}

@ -0,0 +1,26 @@
/*
Copyright (c) 2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef ALIAS_MOSQ_H
#define ALIAS_MOSQ_H
#include "mosquitto_internal.h"
int alias__add(struct mosquitto *mosq, const char *topic, int alias);
int alias__find(struct mosquitto *mosq, char **topic, int alias);
void alias__free_all(struct mosquitto *mosq);
#endif

@ -0,0 +1,120 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include "mosquitto.h"
#include "mosquitto_internal.h"
void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_connect = on_connect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_connect_with_flags = on_connect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_connect_v5 = on_connect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_disconnect = on_disconnect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_disconnect_v5 = on_disconnect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_publish = on_publish;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_publish_v5 = on_publish;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_message = on_message;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_message_v5 = on_message;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_subscribe = on_subscribe;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_subscribe_v5 = on_subscribe;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_unsubscribe = on_unsubscribe;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_unsubscribe_v5 = on_unsubscribe;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *))
{
pthread_mutex_lock(&mosq->log_callback_mutex);
mosq->on_log = on_log;
pthread_mutex_unlock(&mosq->log_callback_mutex);
}

@ -0,0 +1,296 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <string.h>
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "messages_mosq.h"
#include "memory_mosq.h"
#include "packet_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "send_mosq.h"
#include "socks_mosq.h"
#include "util_mosq.h"
static char alphanum[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const mosquitto_property *properties);
static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address);
static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address)
{
int i;
int rc;
if(!mosq) return MOSQ_ERR_INVAL;
if(!host || port <= 0) return MOSQ_ERR_INVAL;
if(mosq->id == NULL && (mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311)){
mosq->id = (char *)mosquitto__calloc(24, sizeof(char));
if(!mosq->id){
return MOSQ_ERR_NOMEM;
}
mosq->id[0] = 'm';
mosq->id[1] = 'o';
mosq->id[2] = 's';
mosq->id[3] = 'q';
mosq->id[4] = '-';
rc = util__random_bytes(&mosq->id[5], 18);
if(rc) return rc;
for(i=5; i<23; i++){
mosq->id[i] = alphanum[(mosq->id[i]&0x7F)%(sizeof(alphanum)-1)];
}
}
mosquitto__free(mosq->host);
mosq->host = mosquitto__strdup(host);
if(!mosq->host) return MOSQ_ERR_NOMEM;
mosq->port = port;
mosquitto__free(mosq->bind_address);
if(bind_address){
mosq->bind_address = mosquitto__strdup(bind_address);
if(!mosq->bind_address) return MOSQ_ERR_NOMEM;
}
mosq->keepalive = keepalive;
mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum;
mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;
if(mosq->sockpairR != INVALID_SOCKET){
COMPAT_CLOSE(mosq->sockpairR);
mosq->sockpairR = INVALID_SOCKET;
}
if(mosq->sockpairW != INVALID_SOCKET){
COMPAT_CLOSE(mosq->sockpairW);
mosq->sockpairW = INVALID_SOCKET;
}
if(net__socketpair(&mosq->sockpairR, &mosq->sockpairW)){
log__printf(mosq, MOSQ_LOG_WARNING,
"Warning: Unable to open socket pair, outgoing publish commands may be delayed.");
}
return MOSQ_ERR_SUCCESS;
}
int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive)
{
return mosquitto_connect_bind(mosq, host, port, keepalive, NULL);
}
int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address)
{
return mosquitto_connect_bind_v5(mosq, host, port, keepalive, bind_address, NULL);
}
int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties)
{
int rc;
if(properties){
rc = mosquitto_property_check_all(CMD_CONNECT, properties);
if(rc) return rc;
}
rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address);
if(rc) return rc;
mosquitto__set_state(mosq, mosq_cs_new);
return mosquitto__reconnect(mosq, true, properties);
}
int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive)
{
return mosquitto_connect_bind_async(mosq, host, port, keepalive, NULL);
}
int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address)
{
int rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address);
if(rc) return rc;
return mosquitto__reconnect(mosq, false, NULL);
}
int mosquitto_reconnect_async(struct mosquitto *mosq)
{
return mosquitto__reconnect(mosq, false, NULL);
}
int mosquitto_reconnect(struct mosquitto *mosq)
{
return mosquitto__reconnect(mosq, true, NULL);
}
static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const mosquitto_property *properties)
{
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property local_property;
int rc;
if(!mosq) return MOSQ_ERR_INVAL;
if(!mosq->host || mosq->port <= 0) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_CONNECT, outgoing_properties);
if(rc) return rc;
}
pthread_mutex_lock(&mosq->msgtime_mutex);
mosq->last_msg_in = mosquitto_time();
mosq->next_msg_out = mosq->last_msg_in + mosq->keepalive;
pthread_mutex_unlock(&mosq->msgtime_mutex);
mosq->ping_t = 0;
packet__cleanup(&mosq->in_packet);
packet__cleanup_all(mosq);
message__reconnect_reset(mosq);
if(mosq->sock != INVALID_SOCKET){
net__socket_close(mosq); //close socket
}
#ifdef WITH_SOCKS
if(mosq->socks5_host){
rc = net__socket_connect(mosq, mosq->socks5_host, mosq->socks5_port, mosq->bind_address, blocking);
}else
#endif
{
rc = net__socket_connect(mosq, mosq->host, mosq->port, mosq->bind_address, blocking);
}
if(rc>0){
mosquitto__set_state(mosq, mosq_cs_connect_pending);
return rc;
}
#ifdef WITH_SOCKS
if(mosq->socks5_host){
mosquitto__set_state(mosq, mosq_cs_socks5_new);
return socks5__send(mosq);
}else
#endif
{
mosquitto__set_state(mosq, mosq_cs_connected);
rc = send__connect(mosq, mosq->keepalive, mosq->clean_start, outgoing_properties);
if(rc){
packet__cleanup_all(mosq);
net__socket_close(mosq);
mosquitto__set_state(mosq, mosq_cs_new);
}
return rc;
}
}
int mosquitto_disconnect(struct mosquitto *mosq)
{
return mosquitto_disconnect_v5(mosq, 0, NULL);
}
int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)
{
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property local_property;
int rc;
if(!mosq) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_DISCONNECT, outgoing_properties);
if(rc) return rc;
}
mosquitto__set_state(mosq, mosq_cs_disconnected);
if(mosq->sock == INVALID_SOCKET){
return MOSQ_ERR_NO_CONN;
}else{
return send__disconnect(mosq, reason_code, outgoing_properties);
}
}
void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)
{
mosquitto__set_state(mosq, mosq_cs_disconnected);
net__socket_close(mosq);
/* Free data and reset values */
pthread_mutex_lock(&mosq->out_packet_mutex);
mosq->current_out_packet = mosq->out_packet;
if(mosq->out_packet){
mosq->out_packet = mosq->out_packet->next;
if(!mosq->out_packet){
mosq->out_packet_last = NULL;
}
}
pthread_mutex_unlock(&mosq->out_packet_mutex);
pthread_mutex_lock(&mosq->msgtime_mutex);
mosq->next_msg_out = mosquitto_time() + mosq->keepalive;
pthread_mutex_unlock(&mosq->msgtime_mutex);
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_disconnect){
mosq->in_callback = true;
mosq->on_disconnect(mosq, mosq->userdata, reason_code);
mosq->in_callback = false;
}
if(mosq->on_disconnect_v5){
mosq->in_callback = true;
mosq->on_disconnect_v5(mosq, mosq->userdata, reason_code, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
}

@ -0,0 +1,13 @@
#ifndef DUMMYPTHREAD_H
#define DUMMYPTHREAD_H
#define pthread_create(A, B, C, D)
#define pthread_join(A, B)
#define pthread_cancel(A)
#define pthread_mutex_init(A, B)
#define pthread_mutex_destroy(A)
#define pthread_mutex_lock(A)
#define pthread_mutex_unlock(A)
#endif

@ -0,0 +1,49 @@
/*
Copyright (c) 2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include "logging_mosq.h"
#include "mosquitto_internal.h"
#include "mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
int handle__auth(struct mosquitto *mosq)
{
int rc = 0;
uint8_t reason_code;
mosquitto_property *properties = NULL;
if(!mosq) return MOSQ_ERR_INVAL;
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received AUTH", mosq->id);
if(mosq->protocol != mosq_p_mqtt5){
return MOSQ_ERR_PROTOCOL;
}
if(packet__read_byte(&mosq->in_packet, &reason_code)) return 1;
rc = property__read_all(CMD_AUTH, &mosq->in_packet, &properties);
if(rc) return rc;
mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,129 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "read_handle.h"
static void connack_callback(struct mosquitto *mosq, uint8_t reason_code, uint8_t connect_flags, const mosquitto_property *properties)
{
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", mosq->id, reason_code);
if(reason_code == MQTT_RC_SUCCESS){
mosq->reconnects = 0;
}
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_connect){
mosq->in_callback = true;
mosq->on_connect(mosq, mosq->userdata, reason_code);
mosq->in_callback = false;
}
if(mosq->on_connect_with_flags){
mosq->in_callback = true;
mosq->on_connect_with_flags(mosq, mosq->userdata, reason_code, connect_flags);
mosq->in_callback = false;
}
if(mosq->on_connect_v5){
mosq->in_callback = true;
mosq->on_connect_v5(mosq, mosq->userdata, reason_code, connect_flags, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
}
int handle__connack(struct mosquitto *mosq)
{
uint8_t connect_flags;
uint8_t reason_code;
int rc;
mosquitto_property *properties = NULL;
char *clientid = NULL;
assert(mosq);
rc = packet__read_byte(&mosq->in_packet, &connect_flags);
if(rc) return rc;
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->protocol == mosq_p_mqtt5){
rc = property__read_all(CMD_CONNACK, &mosq->in_packet, &properties);
if(rc == MOSQ_ERR_PROTOCOL && reason_code == CONNACK_REFUSED_PROTOCOL_VERSION){
/* This could occur because we are connecting to a v3.x broker and
* it has replied with "unacceptable protocol version", but with a
* v3 CONNACK. */
connack_callback(mosq, MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION, connect_flags, NULL);
return rc;
}else if(rc){
return rc;
}
}
mosquitto_property_read_string(properties, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, &clientid, false);
if(clientid){
if(mosq->id){
/* We've been sent a client identifier but already have one. This
* shouldn't happen. */
free(clientid);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_PROTOCOL;
}else{
mosq->id = clientid;
clientid = NULL;
}
}
mosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS, &mosq->maximum_qos, false);
mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &mosq->msgs_out.inflight_maximum, false);
mosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE, &mosq->keepalive, false);
mosquitto_property_read_int32(properties, MQTT_PROP_MAXIMUM_PACKET_SIZE, &mosq->maximum_packet_size, false);
mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;
connack_callback(mosq, reason_code, connect_flags, properties);
mosquitto_property_free_all(&properties);
switch(reason_code){
case 0:
pthread_mutex_lock(&mosq->state_mutex);
if(mosq->state != mosq_cs_disconnecting){
mosq->state = mosq_cs_active;
}
pthread_mutex_unlock(&mosq->state_mutex);
message__retry_check(mosq);
return MOSQ_ERR_SUCCESS;
case 1:
case 2:
case 3:
case 4:
case 5:
return MOSQ_ERR_CONN_REFUSED;
default:
return MOSQ_ERR_PROTOCOL;
}
}

@ -0,0 +1,62 @@
/*
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include "logging_mosq.h"
#include "mqtt_protocol.h"
#include "memory_mosq.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__disconnect(struct mosquitto *mosq)
{
int rc;
uint8_t reason_code;
mosquitto_property *properties = NULL;
if(!mosq){
return MOSQ_ERR_INVAL;
}
if(mosq->protocol != mosq_p_mqtt5){
return MOSQ_ERR_PROTOCOL;
}
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->in_packet.remaining_length > 2){
rc = property__read_all(CMD_DISCONNECT, &mosq->in_packet, &properties);
if(rc) return rc;
mosquitto_property_free_all(&properties);
}
log__printf(mosq, MOSQ_LOG_DEBUG, "Received DISCONNECT (%d)", reason_code);
do_client_disconnect(mosq, reason_code, properties);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,76 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__pingreq(struct mosquitto *mosq)
{
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGREQ from %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGREQ", mosq->id);
#endif
return send__pingresp(mosq);
}
int handle__pingresp(struct mosquitto *mosq)
{
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
mosq->ping_t = 0; /* No longer waiting for a PINGRESP. */
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGRESP from %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGRESP", mosq->id);
#endif
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,120 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
#ifdef WITH_BROKER
int handle__pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const char *type)
#else
int handle__pubackcomp(struct mosquitto *mosq, const char *type)
#endif
{
uint8_t reason_code = 0;
uint16_t mid;
int rc;
mosquitto_property *properties = NULL;
int qos;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
pthread_mutex_lock(&mosq->msgs_out.mutex);
util__increment_send_quota(mosq);
pthread_mutex_unlock(&mosq->msgs_out.mutex);
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
qos = type[3] == 'A'?1:2; /* pubAck or pubComp */
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->in_packet.remaining_length > 3){
rc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties);
if(rc) return rc;
}
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received %s from %s (Mid: %d, RC:%d)", type, mosq->id, mid, reason_code);
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
rc = db__message_delete_outgoing(db, mosq, mid, mosq_ms_wait_for_pubcomp, qos);
if(rc == MOSQ_ERR_NOT_FOUND){
log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received %s from %s for an unknown packet identifier %d.", type, mosq->id, mid);
return MOSQ_ERR_SUCCESS;
}else{
return rc;
}
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d, RC:%d)", mosq->id, type, mid, reason_code);
rc = message__delete(mosq, mid, mosq_md_out, qos);
if(rc){
return rc;
}else{
/* Only inform the client the message has been sent once. */
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_publish){
mosq->in_callback = true;
mosq->on_publish(mosq, mosq->userdata, mid);
mosq->in_callback = false;
}
if(mosq->on_publish_v5){
mosq->in_callback = true;
mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
mosquitto_property_free_all(&properties);
}
pthread_mutex_lock(&mosq->msgs_out.mutex);
message__release_to_inflight(mosq, mosq_md_out);
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_SUCCESS;
#endif
}

@ -0,0 +1,169 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <string.h>
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "messages_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
#include "time_mosq.h"
#include "util_mosq.h"
int handle__publish(struct mosquitto *mosq)
{
uint8_t header;
struct mosquitto_message_all *message;
int rc = 0;
uint16_t mid;
int slen;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all));
if(!message) return MOSQ_ERR_NOMEM;
header = mosq->in_packet.command;
message->dup = (header & 0x08)>>3;
message->msg.qos = (header & 0x06)>>1;
message->msg.retain = (header & 0x01);
rc = packet__read_string(&mosq->in_packet, &message->msg.topic, &slen);
if(rc){
message__cleanup(&message);
return rc;
}
if(!slen){
message__cleanup(&message);
return MOSQ_ERR_PROTOCOL;
}
if(message->msg.qos > 0){
if(mosq->protocol == mosq_p_mqtt5){
if(mosq->msgs_in.inflight_quota == 0){
message__cleanup(&message);
/* FIXME - should send a DISCONNECT here */
return MOSQ_ERR_PROTOCOL;
}
}
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc){
message__cleanup(&message);
return rc;
}
if(mid == 0){
message__cleanup(&message);
return MOSQ_ERR_PROTOCOL;
}
message->msg.mid = (int)mid;
}
if(mosq->protocol == mosq_p_mqtt5){
rc = property__read_all(CMD_PUBLISH, &mosq->in_packet, &properties);
if(rc) return rc;
}
message->msg.payloadlen = mosq->in_packet.remaining_length - mosq->in_packet.pos;
if(message->msg.payloadlen){
message->msg.payload = mosquitto__calloc(message->msg.payloadlen+1, sizeof(uint8_t));
if(!message->msg.payload){
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_NOMEM;
}
rc = packet__read_bytes(&mosq->in_packet, message->msg.payload, message->msg.payloadlen);
if(rc){
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return rc;
}
}
log__printf(mosq, MOSQ_LOG_DEBUG,
"Client %s received PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))",
mosq->id, message->dup, message->msg.qos, message->msg.retain,
message->msg.mid, message->msg.topic,
(long)message->msg.payloadlen);
message->timestamp = mosquitto_time();
switch(message->msg.qos){
case 0:
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_message){
mosq->in_callback = true;
mosq->on_message(mosq, mosq->userdata, &message->msg);
mosq->in_callback = false;
}
if(mosq->on_message_v5){
mosq->in_callback = true;
mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_SUCCESS;
case 1:
util__decrement_receive_quota(mosq);
rc = send__puback(mosq, message->msg.mid, 0);
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_message){
mosq->in_callback = true;
mosq->on_message(mosq, mosq->userdata, &message->msg);
mosq->in_callback = false;
}
if(mosq->on_message_v5){
mosq->in_callback = true;
mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return rc;
case 2:
message->properties = properties;
util__decrement_receive_quota(mosq);
rc = send__pubrec(mosq, message->msg.mid, 0);
pthread_mutex_lock(&mosq->msgs_in.mutex);
message->state = mosq_ms_wait_for_pubrel;
message__queue(mosq, message, mosq_md_in);
pthread_mutex_unlock(&mosq->msgs_in.mutex);
return rc;
default:
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_PROTOCOL;
}
}

@ -0,0 +1,112 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq)
{
uint8_t reason_code = 0;
uint16_t mid;
int rc;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->in_packet.remaining_length > 3){
rc = property__read_all(CMD_PUBREC, &mosq->in_packet, &properties);
if(rc) return rc;
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
}
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREC from %s (Mid: %d)", mosq->id, mid);
if(reason_code < 0x80){
rc = db__message_update_outgoing(mosq, mid, mosq_ms_wait_for_pubcomp, 2);
}else{
return db__message_delete_outgoing(db, mosq, mid, mosq_ms_wait_for_pubrec, 2);
}
#else
UNUSED(db);
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREC (Mid: %d)", mosq->id, mid);
if(reason_code < 0x80 || mosq->protocol != mosq_p_mqtt5){
rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp, 2);
}else{
if(!message__delete(mosq, mid, mosq_md_out, 2)){
/* Only inform the client the message has been sent once. */
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_publish_v5){
mosq->in_callback = true;
mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
}
util__increment_send_quota(mosq);
pthread_mutex_lock(&mosq->msgs_out.mutex);
message__release_to_inflight(mosq, mosq_md_out);
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_SUCCESS;
}
#endif
if(rc == MOSQ_ERR_NOT_FOUND){
log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREC from %s for an unknown packet identifier %d.", mosq->id, mid);
}else if(rc != MOSQ_ERR_SUCCESS){
return rc;
}
rc = send__pubrel(mosq, mid);
if(rc) return rc;
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,128 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq)
{
uint8_t reason_code;
uint16_t mid;
#ifndef WITH_BROKER
struct mosquitto_message_all *message = NULL;
#endif
int rc;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
if(mosq->protocol != mosq_p_mqtt31){
if((mosq->in_packet.command&0x0F) != 0x02){
return MOSQ_ERR_PROTOCOL;
}
}
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->in_packet.remaining_length > 3){
rc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties);
if(rc) return rc;
}
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREL from %s (Mid: %d)", mosq->id, mid);
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
rc = db__message_release_incoming(db, mosq, mid);
if(rc == MOSQ_ERR_NOT_FOUND){
/* Message not found. Still send a PUBCOMP anyway because this could be
* due to a repeated PUBREL after a client has reconnected. */
}else if(rc != MOSQ_ERR_SUCCESS){
return rc;
}
rc = send__pubcomp(mosq, mid);
if(rc) return rc;
#else
UNUSED(db);
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", mosq->id, mid);
rc = send__pubcomp(mosq, mid);
if(rc){
message__remove(mosq, mid, mosq_md_in, &message, 2);
return rc;
}
rc = message__remove(mosq, mid, mosq_md_in, &message, 2);
if(rc){
return rc;
}else{
/* Only pass the message on if we have removed it from the queue - this
* prevents multiple callbacks for the same message. */
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_message){
mosq->in_callback = true;
mosq->on_message(mosq, mosq->userdata, &message->msg);
mosq->in_callback = false;
}
if(mosq->on_message_v5){
mosq->in_callback = true;
mosq->on_message_v5(mosq, mosq->userdata, &message->msg, message->properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
mosquitto_property_free_all(&properties);
message__cleanup(&message);
}
#endif
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,101 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "util_mosq.h"
int handle__suback(struct mosquitto *mosq)
{
uint16_t mid;
uint8_t qos;
int *granted_qos;
int qos_count;
int i = 0;
int rc;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received SUBACK from %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received SUBACK", mosq->id);
#endif
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5){
rc = property__read_all(CMD_SUBACK, &mosq->in_packet, &properties);
if(rc) return rc;
}
qos_count = mosq->in_packet.remaining_length - mosq->in_packet.pos;
granted_qos = mosquitto__malloc(qos_count*sizeof(int));
if(!granted_qos) return MOSQ_ERR_NOMEM;
while(mosq->in_packet.pos < mosq->in_packet.remaining_length){
rc = packet__read_byte(&mosq->in_packet, &qos);
if(rc){
mosquitto__free(granted_qos);
return rc;
}
granted_qos[i] = (int)qos;
i++;
}
#ifdef WITH_BROKER
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
#else
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_subscribe){
mosq->in_callback = true;
mosq->on_subscribe(mosq, mosq->userdata, mid, qos_count, granted_qos);
mosq->in_callback = false;
}
if(mosq->on_subscribe_v5){
mosq->in_callback = true;
mosq->on_subscribe_v5(mosq, mosq->userdata, mid, qos_count, granted_qos, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
mosquitto_property_free_all(&properties);
#endif
mosquitto__free(granted_qos);
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,89 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__unsuback(struct mosquitto *mosq)
{
uint16_t mid;
int rc;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBACK from %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received UNSUBACK", mosq->id);
#endif
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5){
rc = property__read_all(CMD_UNSUBACK, &mosq->in_packet, &properties);
if(rc) return rc;
}
#ifdef WITH_BROKER
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
#else
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_unsubscribe){
mosq->in_callback = true;
mosq->on_unsubscribe(mosq, mosq->userdata, mid);
mosq->in_callback = false;
}
if(mosq->on_unsubscribe_v5){
mosq->in_callback = true;
mosq->on_unsubscribe_v5(mosq, mosq->userdata, mid, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
mosquitto_property_free_all(&properties);
#endif
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,227 @@
/*
Copyright (c) 2016-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include "mosquitto.h"
#include "mosquitto_internal.h"
struct userdata__callback {
const char *topic;
int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *);
void *userdata;
int qos;
int rc;
};
struct userdata__simple {
struct mosquitto_message *messages;
int max_msg_count;
int message_count;
bool want_retained;
};
static void on_connect(struct mosquitto *mosq, void *obj, int rc)
{
struct userdata__callback *userdata = obj;
UNUSED(rc);
mosquitto_subscribe(mosq, NULL, userdata->topic, userdata->qos);
}
static void on_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
{
int rc;
struct userdata__callback *userdata = obj;
rc = userdata->callback(mosq, userdata->userdata, message);
if(rc){
mosquitto_disconnect(mosq);
}
}
static int on_message_simple(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
{
struct userdata__simple *userdata = obj;
int rc;
if(userdata->max_msg_count == 0){
return 0;
}
/* Don't process stale retained messages if 'want_retained' was false */
if(!userdata->want_retained && message->retain){
return 0;
}
userdata->max_msg_count--;
rc = mosquitto_message_copy(&userdata->messages[userdata->message_count], message);
if(rc){
return rc;
}
userdata->message_count++;
if(userdata->max_msg_count == 0){
mosquitto_disconnect(mosq);
}
return 0;
}
libmosq_EXPORT int mosquitto_subscribe_simple(
struct mosquitto_message **messages,
int msg_count,
bool want_retained,
const char *topic,
int qos,
const char *host,
int port,
const char *client_id,
int keepalive,
bool clean_session,
const char *username,
const char *password,
const struct libmosquitto_will *will,
const struct libmosquitto_tls *tls)
{
struct userdata__simple userdata;
int rc;
int i;
if(!topic || msg_count < 1 || !messages){
return MOSQ_ERR_INVAL;
}
*messages = NULL;
userdata.messages = calloc(sizeof(struct mosquitto_message), msg_count);
if(!userdata.messages){
return MOSQ_ERR_NOMEM;
}
userdata.message_count = 0;
userdata.max_msg_count = msg_count;
userdata.want_retained = want_retained;
rc = mosquitto_subscribe_callback(
on_message_simple, &userdata,
topic, qos,
host, port,
client_id, keepalive, clean_session,
username, password,
will, tls);
if(!rc && userdata.max_msg_count == 0){
*messages = userdata.messages;
return MOSQ_ERR_SUCCESS;
}else{
for(i=0; i<msg_count; i++){
mosquitto_message_free_contents(&userdata.messages[i]);
}
free(userdata.messages);
userdata.messages = NULL;
return rc;
}
}
libmosq_EXPORT int mosquitto_subscribe_callback(
int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *),
void *userdata,
const char *topic,
int qos,
const char *host,
int port,
const char *client_id,
int keepalive,
bool clean_session,
const char *username,
const char *password,
const struct libmosquitto_will *will,
const struct libmosquitto_tls *tls)
{
struct mosquitto *mosq;
struct userdata__callback cb_userdata;
int rc;
if(!callback || !topic){
return MOSQ_ERR_INVAL;
}
cb_userdata.topic = topic;
cb_userdata.qos = qos;
cb_userdata.rc = 0;
cb_userdata.userdata = userdata;
cb_userdata.callback = callback;
mosq = mosquitto_new(client_id, clean_session, &cb_userdata);
if(!mosq){
return MOSQ_ERR_NOMEM;
}
if(will){
rc = mosquitto_will_set(mosq, will->topic, will->payloadlen, will->payload, will->qos, will->retain);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
}
if(username){
rc = mosquitto_username_pw_set(mosq, username, password);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
}
if(tls){
rc = mosquitto_tls_set(mosq, tls->cafile, tls->capath, tls->certfile, tls->keyfile, tls->pw_callback);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
rc = mosquitto_tls_opts_set(mosq, tls->cert_reqs, tls->tls_version, tls->ciphers);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
}
mosquitto_connect_callback_set(mosq, on_connect);
mosquitto_message_callback_set(mosq, on_message_callback);
rc = mosquitto_connect(mosq, host, port, keepalive);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
rc = mosquitto_loop_forever(mosq, -1, 1);
mosquitto_destroy(mosq);
if(cb_userdata.rc){
rc = cb_userdata.rc;
}
//if(!rc && cb_userdata.max_msg_count == 0){
//return MOSQ_ERR_SUCCESS;
//}else{
//return rc;
//}
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,59 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "mosquitto_internal.h"
#include "mosquitto.h"
#include "memory_mosq.h"
int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...)
{
va_list va;
char *s;
int len;
assert(mosq);
assert(fmt);
pthread_mutex_lock(&mosq->log_callback_mutex);
if(mosq->on_log){
len = strlen(fmt) + 500;
s = mosquitto__malloc(len*sizeof(char));
if(!s){
pthread_mutex_unlock(&mosq->log_callback_mutex);
return MOSQ_ERR_NOMEM;
}
va_start(va, fmt);
vsnprintf(s, len, fmt, va);
va_end(va);
s[len-1] = '\0'; /* Ensure string is null terminated. */
mosq->on_log(mosq, mosq->userdata, priority, s);
mosquitto__free(s);
}
pthread_mutex_unlock(&mosq->log_callback_mutex);
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,23 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef LOGGING_MOSQ_H
#define LOGGING_MOSQ_H
#include "mosquitto.h"
int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...);
#endif

@ -0,0 +1,381 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <errno.h>
#ifndef WIN32
#include <sys/select.h>
#include <time.h>
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "socks_mosq.h"
#include "tls_mosq.h"
#include "util_mosq.h"
#if !defined(WIN32) && !defined(__SYMBIAN32__)
#define HAVE_PSELECT
#endif
int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)
{
#ifdef HAVE_PSELECT
struct timespec local_timeout;
#else
struct timeval local_timeout;
#endif
fd_set readfds, writefds;
int fdcount;
int rc;
char pairbuf;
int maxfd = 0;
time_t now;
#ifdef WITH_SRV
int state;
#endif
if(!mosq || max_packets < 1) return MOSQ_ERR_INVAL;
#ifndef WIN32
if(mosq->sock >= FD_SETSIZE || mosq->sockpairR >= FD_SETSIZE){
return MOSQ_ERR_INVAL;
}
#endif
FD_ZERO(&readfds);
FD_ZERO(&writefds);
if(mosq->sock != INVALID_SOCKET){
maxfd = mosq->sock;
FD_SET(mosq->sock, &readfds);
pthread_mutex_lock(&mosq->current_out_packet_mutex);
pthread_mutex_lock(&mosq->out_packet_mutex);
if(mosq->out_packet || mosq->current_out_packet){
FD_SET(mosq->sock, &writefds);
}
#ifdef WITH_TLS
if(mosq->ssl){
if(mosq->want_write){
FD_SET(mosq->sock, &writefds);
}else if(mosq->want_connect){
/* Remove possible FD_SET from above, we don't want to check
* for writing if we are still connecting, unless want_write is
* definitely set. The presence of outgoing packets does not
* matter yet. */
FD_CLR(mosq->sock, &writefds);
}
}
#endif
pthread_mutex_unlock(&mosq->out_packet_mutex);
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
}else{
#ifdef WITH_SRV
if(mosq->achan){
state = mosquitto__get_state(mosq);
if(state == mosq_cs_connect_srv){
rc = ares_fds(mosq->achan, &readfds, &writefds);
if(rc > maxfd){
maxfd = rc;
}
}else{
return MOSQ_ERR_NO_CONN;
}
}
#else
return MOSQ_ERR_NO_CONN;
#endif
}
if(mosq->sockpairR != INVALID_SOCKET){
/* sockpairR is used to break out of select() before the timeout, on a
* call to publish() etc. */
FD_SET(mosq->sockpairR, &readfds);
if(mosq->sockpairR > maxfd){
maxfd = mosq->sockpairR;
}
}
if(timeout < 0){
timeout = 1000;
}
now = mosquitto_time();
if(mosq->next_msg_out && now + timeout/1000 > mosq->next_msg_out){
timeout = (mosq->next_msg_out - now)*1000;
}
if(timeout < 0){
/* There has been a delay somewhere which means we should have already
* sent a message. */
timeout = 0;
}
local_timeout.tv_sec = timeout/1000;
#ifdef HAVE_PSELECT
local_timeout.tv_nsec = (timeout-local_timeout.tv_sec*1000)*1e6;
#else
local_timeout.tv_usec = (timeout-local_timeout.tv_sec*1000)*1000;
#endif
#ifdef HAVE_PSELECT
fdcount = pselect(maxfd+1, &readfds, &writefds, NULL, &local_timeout, NULL);
#else
fdcount = select(maxfd+1, &readfds, &writefds, NULL, &local_timeout);
#endif
if(fdcount == -1){
#ifdef WIN32
errno = WSAGetLastError();
#endif
if(errno == EINTR){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_ERRNO;
}
}else{
if(mosq->sock != INVALID_SOCKET){
if(FD_ISSET(mosq->sock, &readfds)){
rc = mosquitto_loop_read(mosq, max_packets);
if(rc || mosq->sock == INVALID_SOCKET){
return rc;
}
}
if(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){
#ifndef WIN32
if(read(mosq->sockpairR, &pairbuf, 1) == 0){
}
#else
recv(mosq->sockpairR, &pairbuf, 1, 0);
#endif
/* Fake write possible, to stimulate output write even though
* we didn't ask for it, because at that point the publish or
* other command wasn't present. */
if(mosq->sock != INVALID_SOCKET)
FD_SET(mosq->sock, &writefds);
}
if(mosq->sock != INVALID_SOCKET && FD_ISSET(mosq->sock, &writefds)){
#ifdef WITH_TLS
if(mosq->want_connect){
rc = net__socket_connect_tls(mosq);
if(rc) return rc;
}else
#endif
{
rc = mosquitto_loop_write(mosq, max_packets);
if(rc || mosq->sock == INVALID_SOCKET){
return rc;
}
}
}
}
#ifdef WITH_SRV
if(mosq->achan){
ares_process(mosq->achan, &readfds, &writefds);
}
#endif
}
return mosquitto_loop_misc(mosq);
}
int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
{
int run = 1;
int rc;
unsigned long reconnect_delay;
#ifndef WIN32
struct timespec req, rem;
#endif
int state;
if(!mosq) return MOSQ_ERR_INVAL;
mosq->reconnects = 0;
while(run){
do{
rc = mosquitto_loop(mosq, timeout, max_packets);
}while(run && rc == MOSQ_ERR_SUCCESS);
/* Quit after fatal errors. */
switch(rc){
case MOSQ_ERR_NOMEM:
case MOSQ_ERR_PROTOCOL:
case MOSQ_ERR_INVAL:
case MOSQ_ERR_NOT_FOUND:
case MOSQ_ERR_TLS:
case MOSQ_ERR_PAYLOAD_SIZE:
case MOSQ_ERR_NOT_SUPPORTED:
case MOSQ_ERR_AUTH:
case MOSQ_ERR_ACL_DENIED:
case MOSQ_ERR_UNKNOWN:
case MOSQ_ERR_EAI:
case MOSQ_ERR_PROXY:
return rc;
case MOSQ_ERR_ERRNO:
break;
}
if(errno == EPROTO){
return rc;
}
do{
rc = MOSQ_ERR_SUCCESS;
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
run = 0;
}else{
if(mosq->reconnect_delay_max > mosq->reconnect_delay){
if(mosq->reconnect_exponential_backoff){
reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1)*(mosq->reconnects+1);
}else{
reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1);
}
}else{
reconnect_delay = mosq->reconnect_delay;
}
if(reconnect_delay > mosq->reconnect_delay_max){
reconnect_delay = mosq->reconnect_delay_max;
}else{
mosq->reconnects++;
}
#ifdef WIN32
Sleep(reconnect_delay*1000);
#else
req.tv_sec = reconnect_delay;
req.tv_nsec = 0;
while(nanosleep(&req, &rem) == -1 && errno == EINTR){
req = rem;
}
#endif
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
run = 0;
}else{
rc = mosquitto_reconnect(mosq);
}
}
}while(run && rc != MOSQ_ERR_SUCCESS);
}
return rc;
}
int mosquitto_loop_misc(struct mosquitto *mosq)
{
if(!mosq) return MOSQ_ERR_INVAL;
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
return mosquitto__check_keepalive(mosq);
}
static int mosquitto__loop_rc_handle(struct mosquitto *mosq, int rc)
{
int state;
if(rc){
net__socket_close(mosq);
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
rc = MOSQ_ERR_SUCCESS;
}
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_disconnect){
mosq->in_callback = true;
mosq->on_disconnect(mosq, mosq->userdata, rc);
mosq->in_callback = false;
}
if(mosq->on_disconnect_v5){
mosq->in_callback = true;
mosq->on_disconnect_v5(mosq, mosq->userdata, rc, NULL);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
}
return rc;
}
int mosquitto_loop_read(struct mosquitto *mosq, int max_packets)
{
int rc;
int i;
if(max_packets < 1) return MOSQ_ERR_INVAL;
#ifdef WITH_TLS
if(mosq->want_connect){
return net__socket_connect_tls(mosq);
}
#endif
pthread_mutex_lock(&mosq->msgs_out.mutex);
max_packets = mosq->msgs_out.queue_len;
pthread_mutex_unlock(&mosq->msgs_out.mutex);
pthread_mutex_lock(&mosq->msgs_in.mutex);
max_packets += mosq->msgs_in.queue_len;
pthread_mutex_unlock(&mosq->msgs_in.mutex);
if(max_packets < 1) max_packets = 1;
/* Queue len here tells us how many messages are awaiting processing and
* have QoS > 0. We should try to deal with that many in this loop in order
* to keep up. */
for(i=0; i<max_packets || SSL_DATA_PENDING(mosq); i++){
#ifdef WITH_SOCKS
if(mosq->socks5_host){
rc = socks5__read(mosq);
}else
#endif
{
rc = packet__read(mosq);
}
if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
return mosquitto__loop_rc_handle(mosq, rc);
}
}
return rc;
}
int mosquitto_loop_write(struct mosquitto *mosq, int max_packets)
{
int rc;
int i;
if(max_packets < 1) return MOSQ_ERR_INVAL;
pthread_mutex_lock(&mosq->msgs_out.mutex);
max_packets = mosq->msgs_out.queue_len;
pthread_mutex_unlock(&mosq->msgs_out.mutex);
pthread_mutex_lock(&mosq->msgs_in.mutex);
max_packets += mosq->msgs_in.queue_len;
pthread_mutex_unlock(&mosq->msgs_in.mutex);
if(max_packets < 1) max_packets = 1;
/* Queue len here tells us how many messages are awaiting processing and
* have QoS > 0. We should try to deal with that many in this loop in order
* to keep up. */
for(i=0; i<max_packets; i++){
rc = packet__write(mosq);
if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
return mosquitto__loop_rc_handle(mosq, rc);
}
}
return rc;
}

@ -0,0 +1,160 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "memory_mosq.h"
#ifdef REAL_WITH_MEMORY_TRACKING
# if defined(__APPLE__)
# include <malloc/malloc.h>
# define malloc_usable_size malloc_size
# elif defined(__FreeBSD__)
# include <malloc_np.h>
# else
# include <malloc.h>
# endif
#endif
#ifdef REAL_WITH_MEMORY_TRACKING
static unsigned long memcount = 0;
static unsigned long max_memcount = 0;
#endif
#ifdef WITH_BROKER
static size_t mem_limit = 0;
void memory__set_limit(size_t lim)
{
mem_limit = lim;
}
#endif
void *mosquitto__calloc(size_t nmemb, size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem = calloc(nmemb, size);
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem){
memcount += malloc_usable_size(mem);
if(memcount > max_memcount){
max_memcount = memcount;
}
}
#endif
return mem;
}
void mosquitto__free(void *mem)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(!mem){
return;
}
memcount -= malloc_usable_size(mem);
#endif
free(mem);
}
void *mosquitto__malloc(size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem = malloc(size);
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem){
memcount += malloc_usable_size(mem);
if(memcount > max_memcount){
max_memcount = memcount;
}
}
#endif
return mem;
}
#ifdef REAL_WITH_MEMORY_TRACKING
unsigned long mosquitto__memory_used(void)
{
return memcount;
}
unsigned long mosquitto__max_memory_used(void)
{
return max_memcount;
}
#endif
void *mosquitto__realloc(void *ptr, size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem;
#ifdef REAL_WITH_MEMORY_TRACKING
if(ptr){
memcount -= malloc_usable_size(ptr);
}
#endif
mem = realloc(ptr, size);
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem){
memcount += malloc_usable_size(mem);
if(memcount > max_memcount){
max_memcount = memcount;
}
}
#endif
return mem;
}
char *mosquitto__strdup(const char *s)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + strlen(s) > mem_limit){
return NULL;
}
#endif
char *str = strdup(s);
#ifdef REAL_WITH_MEMORY_TRACKING
if(str){
memcount += malloc_usable_size(str);
if(memcount > max_memcount){
max_memcount = memcount;
}
}
#endif
return str;
}

@ -0,0 +1,41 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef MEMORY_MOSQ_H
#define MEMORY_MOSQ_H
#include <stdio.h>
#include <sys/types.h>
#if defined(WITH_MEMORY_TRACKING) && defined(WITH_BROKER) && defined(__GLIBC__)
#define REAL_WITH_MEMORY_TRACKING
#endif
void *mosquitto__calloc(size_t nmemb, size_t size);
void mosquitto__free(void *mem);
void *mosquitto__malloc(size_t size);
#ifdef REAL_WITH_MEMORY_TRACKING
unsigned long mosquitto__memory_used(void);
unsigned long mosquitto__max_memory_used(void);
#endif
void *mosquitto__realloc(void *ptr, size_t size);
char *mosquitto__strdup(const char *s);
#ifdef WITH_BROKER
void memory__set_limit(size_t lim);
#endif
#endif

@ -0,0 +1,345 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <utlist.h>
#include "mosquitto_internal.h"
#include "mosquitto.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "send_mosq.h"
#include "time_mosq.h"
#include "util_mosq.h"
void message__cleanup(struct mosquitto_message_all **message)
{
struct mosquitto_message_all *msg;
if(!message || !*message) return;
msg = *message;
mosquitto__free(msg->msg.topic);
mosquitto__free(msg->msg.payload);
mosquitto_property_free_all(&msg->properties);
mosquitto__free(msg);
}
void message__cleanup_all(struct mosquitto *mosq)
{
struct mosquitto_message_all *tail, *tmp;
assert(mosq);
DL_FOREACH_SAFE(mosq->msgs_in.inflight, tail, tmp){
DL_DELETE(mosq->msgs_in.inflight, tail);
message__cleanup(&tail);
}
DL_FOREACH_SAFE(mosq->msgs_out.inflight, tail, tmp){
DL_DELETE(mosq->msgs_out.inflight, tail);
message__cleanup(&tail);
}
}
int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src)
{
if(!dst || !src) return MOSQ_ERR_INVAL;
dst->mid = src->mid;
dst->topic = mosquitto__strdup(src->topic);
if(!dst->topic) return MOSQ_ERR_NOMEM;
dst->qos = src->qos;
dst->retain = src->retain;
if(src->payloadlen){
dst->payload = mosquitto__calloc(src->payloadlen+1, sizeof(uint8_t));
if(!dst->payload){
mosquitto__free(dst->topic);
return MOSQ_ERR_NOMEM;
}
memcpy(dst->payload, src->payload, src->payloadlen);
dst->payloadlen = src->payloadlen;
}else{
dst->payloadlen = 0;
dst->payload = NULL;
}
return MOSQ_ERR_SUCCESS;
}
int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos)
{
struct mosquitto_message_all *message;
int rc;
assert(mosq);
rc = message__remove(mosq, mid, dir, &message, qos);
if(rc == MOSQ_ERR_SUCCESS){
message__cleanup(&message);
}
return rc;
}
void mosquitto_message_free(struct mosquitto_message **message)
{
struct mosquitto_message *msg;
if(!message || !*message) return;
msg = *message;
mosquitto__free(msg->topic);
mosquitto__free(msg->payload);
mosquitto__free(msg);
}
void mosquitto_message_free_contents(struct mosquitto_message *message)
{
if(!message) return;
mosquitto__free(message->topic);
mosquitto__free(message->payload);
}
int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir)
{
/* mosq->*_message_mutex should be locked before entering this function */
assert(mosq);
assert(message);
assert(message->msg.qos != 0);
if(dir == mosq_md_out){
DL_APPEND(mosq->msgs_out.inflight, message);
mosq->msgs_out.queue_len++;
}else{
DL_APPEND(mosq->msgs_in.inflight, message);
mosq->msgs_in.queue_len++;
}
return message__release_to_inflight(mosq, dir);
}
void message__reconnect_reset(struct mosquitto *mosq)
{
struct mosquitto_message_all *message, *tmp;
assert(mosq);
pthread_mutex_lock(&mosq->msgs_in.mutex);
mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum;
mosq->msgs_in.queue_len = 0;
DL_FOREACH_SAFE(mosq->msgs_in.inflight, message, tmp){
mosq->msgs_in.queue_len++;
message->timestamp = 0;
if(message->msg.qos != 2){
DL_DELETE(mosq->msgs_in.inflight, message);
message__cleanup(&message);
}else{
/* Message state can be preserved here because it should match
* whatever the client has got. */
util__decrement_receive_quota(mosq);
}
}
pthread_mutex_unlock(&mosq->msgs_in.mutex);
pthread_mutex_lock(&mosq->msgs_out.mutex);
mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;
mosq->msgs_out.queue_len = 0;
DL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){
mosq->msgs_out.queue_len++;
message->timestamp = 0;
if(mosq->msgs_out.inflight_quota != 0){
util__decrement_send_quota(mosq);
if(message->msg.qos == 1){
message->state = mosq_ms_publish_qos1;
}else if(message->msg.qos == 2){
if(message->state == mosq_ms_wait_for_pubrec){
message->state = mosq_ms_publish_qos2;
}else if(message->state == mosq_ms_wait_for_pubcomp){
message->state = mosq_ms_resend_pubrel;
}
/* Should be able to preserve state. */
}
}else{
message->state = mosq_ms_invalid;
}
}
pthread_mutex_unlock(&mosq->msgs_out.mutex);
}
int message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir)
{
/* mosq->*_message_mutex should be locked before entering this function */
struct mosquitto_message_all *cur, *tmp;
int rc = MOSQ_ERR_SUCCESS;
if(dir == mosq_md_out){
DL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){
if(mosq->msgs_out.inflight_quota > 0){
if(cur->msg.qos > 0 && cur->state == mosq_ms_invalid){
if(cur->msg.qos == 1){
cur->state = mosq_ms_wait_for_puback;
}else if(cur->msg.qos == 2){
cur->state = mosq_ms_wait_for_pubrec;
}
rc = send__publish(mosq, cur->msg.mid, cur->msg.topic, cur->msg.payloadlen, cur->msg.payload, cur->msg.qos, cur->msg.retain, cur->dup, cur->properties, NULL, 0);
if(rc){
return rc;
}
util__decrement_send_quota(mosq);
}
}else{
return MOSQ_ERR_SUCCESS;
}
}
}
return rc;
}
int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos)
{
struct mosquitto_message_all *cur, *tmp;
bool found = false;
assert(mosq);
assert(message);
if(dir == mosq_md_out){
pthread_mutex_lock(&mosq->msgs_out.mutex);
DL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){
if(found == false && cur->msg.mid == mid){
if(cur->msg.qos != qos){
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_PROTOCOL;
}
DL_DELETE(mosq->msgs_out.inflight, cur);
*message = cur;
mosq->msgs_out.queue_len--;
found = true;
break;
}
}
pthread_mutex_unlock(&mosq->msgs_out.mutex);
if(found){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOT_FOUND;
}
}else{
pthread_mutex_lock(&mosq->msgs_in.mutex);
DL_FOREACH_SAFE(mosq->msgs_in.inflight, cur, tmp){
if(cur->msg.mid == mid){
if(cur->msg.qos != qos){
pthread_mutex_unlock(&mosq->msgs_in.mutex);
return MOSQ_ERR_PROTOCOL;
}
DL_DELETE(mosq->msgs_in.inflight, cur);
*message = cur;
mosq->msgs_in.queue_len--;
found = true;
break;
}
}
pthread_mutex_unlock(&mosq->msgs_in.mutex);
if(found){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOT_FOUND;
}
}
}
void message__retry_check(struct mosquitto *mosq)
{
struct mosquitto_message_all *msg;
time_t now = mosquitto_time();
assert(mosq);
#ifdef WITH_THREADING
pthread_mutex_lock(&mosq->msgs_out.mutex);
#endif
DL_FOREACH(mosq->msgs_out.inflight, msg){
switch(msg->state){
case mosq_ms_publish_qos1:
case mosq_ms_publish_qos2:
msg->timestamp = now;
msg->dup = true;
send__publish(mosq, msg->msg.mid, msg->msg.topic, msg->msg.payloadlen, msg->msg.payload, msg->msg.qos, msg->msg.retain, msg->dup, msg->properties, NULL, 0);
break;
case mosq_ms_wait_for_pubrel:
msg->timestamp = now;
msg->dup = true;
send__pubrec(mosq, msg->msg.mid, 0);
break;
case mosq_ms_resend_pubrel:
case mosq_ms_wait_for_pubcomp:
msg->timestamp = now;
msg->dup = true;
send__pubrel(mosq, msg->msg.mid);
break;
default:
break;
}
}
#ifdef WITH_THREADING
pthread_mutex_unlock(&mosq->msgs_out.mutex);
#endif
}
void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry)
{
UNUSED(mosq);
UNUSED(message_retry);
}
int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos)
{
struct mosquitto_message_all *message, *tmp;
assert(mosq);
pthread_mutex_lock(&mosq->msgs_out.mutex);
DL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){
if(message->msg.mid == mid){
if(message->msg.qos != qos){
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_PROTOCOL;
}
message->state = state;
message->timestamp = mosquitto_time();
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_SUCCESS;
}
}
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_NOT_FOUND;
}
int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages)
{
return mosquitto_int_option(mosq, MOSQ_OPT_SEND_MAXIMUM, max_inflight_messages);
}

@ -0,0 +1,32 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef MESSAGES_MOSQ_H
#define MESSAGES_MOSQ_H
#include "mosquitto_internal.h"
#include "mosquitto.h"
void message__cleanup_all(struct mosquitto *mosq);
void message__cleanup(struct mosquitto_message_all **message);
int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos);
int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir);
void message__reconnect_reset(struct mosquitto *mosq);
int message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir);
int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos);
void message__retry_check(struct mosquitto *mosq);
int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos);
#endif

@ -0,0 +1,613 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <errno.h>
#include <signal.h>
#include <string.h>
#ifndef WIN32
#include <sys/time.h>
#include <strings.h>
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "will_mosq.h"
void mosquitto__destroy(struct mosquitto *mosq);
int mosquitto_lib_version(int *major, int *minor, int *revision)
{
if(major) *major = LIBMOSQUITTO_MAJOR;
if(minor) *minor = LIBMOSQUITTO_MINOR;
if(revision) *revision = LIBMOSQUITTO_REVISION;
return LIBMOSQUITTO_VERSION_NUMBER;
}
int mosquitto_lib_init(void)
{
#ifdef WIN32
srand(GetTickCount64());
#elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK)
struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp);
srand(tp.tv_nsec);
#elif defined(__APPLE__)
uint64_t ticks;
ticks = mach_absolute_time();
srand((unsigned int)ticks);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_sec*1000 + tv.tv_usec/1000);
#endif
return net__init();
}
int mosquitto_lib_cleanup(void)
{
net__cleanup();
return MOSQ_ERR_SUCCESS;
}
struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata)
{
struct mosquitto *mosq = NULL;
int rc;
if(clean_start == false && id == NULL){
errno = EINVAL;
return NULL;
}
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
mosq = (struct mosquitto *)mosquitto__calloc(1, sizeof(struct mosquitto));
if(mosq){
mosq->sock = INVALID_SOCKET;
mosq->sockpairR = INVALID_SOCKET;
mosq->sockpairW = INVALID_SOCKET;
#ifdef WITH_THREADING
mosq->thread_id = pthread_self();
#endif
rc = mosquitto_reinitialise(mosq, id, clean_start, userdata);
if(rc){
mosquitto_destroy(mosq);
if(rc == MOSQ_ERR_INVAL){
errno = EINVAL;
}else if(rc == MOSQ_ERR_NOMEM){
errno = ENOMEM;
}
return NULL;
}
}else{
errno = ENOMEM;
}
return mosq;
}
int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_start, void *userdata)
{
if(!mosq) return MOSQ_ERR_INVAL;
if(clean_start == false && id == NULL){
return MOSQ_ERR_INVAL;
}
mosquitto__destroy(mosq);
memset(mosq, 0, sizeof(struct mosquitto));
if(userdata){
mosq->userdata = userdata;
}else{
mosq->userdata = mosq;
}
mosq->protocol = mosq_p_mqtt311;
mosq->sock = INVALID_SOCKET;
mosq->sockpairR = INVALID_SOCKET;
mosq->sockpairW = INVALID_SOCKET;
mosq->keepalive = 60;
mosq->clean_start = clean_start;
if(id){
if(STREMPTY(id)){
return MOSQ_ERR_INVAL;
}
if(mosquitto_validate_utf8(id, strlen(id))){
return MOSQ_ERR_MALFORMED_UTF8;
}
mosq->id = mosquitto__strdup(id);
}
mosq->in_packet.payload = NULL;
packet__cleanup(&mosq->in_packet);
mosq->out_packet = NULL;
mosq->current_out_packet = NULL;
mosq->last_msg_in = mosquitto_time();
mosq->next_msg_out = mosquitto_time() + mosq->keepalive;
mosq->ping_t = 0;
mosq->last_mid = 0;
mosq->state = mosq_cs_new;
mosq->maximum_qos = 2;
mosq->msgs_in.inflight_maximum = 20;
mosq->msgs_out.inflight_maximum = 20;
mosq->msgs_in.inflight_quota = 20;
mosq->msgs_out.inflight_quota = 20;
mosq->will = NULL;
mosq->on_connect = NULL;
mosq->on_publish = NULL;
mosq->on_message = NULL;
mosq->on_subscribe = NULL;
mosq->on_unsubscribe = NULL;
mosq->host = NULL;
mosq->port = 1883;
mosq->in_callback = false;
mosq->reconnect_delay = 1;
mosq->reconnect_delay_max = 1;
mosq->reconnect_exponential_backoff = false;
mosq->threaded = mosq_ts_none;
#ifdef WITH_TLS
mosq->ssl = NULL;
mosq->ssl_ctx = NULL;
mosq->tls_cert_reqs = SSL_VERIFY_PEER;
mosq->tls_insecure = false;
mosq->want_write = false;
mosq->tls_ocsp_required = false;
#endif
#ifdef WITH_THREADING
pthread_mutex_init(&mosq->callback_mutex, NULL);
pthread_mutex_init(&mosq->log_callback_mutex, NULL);
pthread_mutex_init(&mosq->state_mutex, NULL);
pthread_mutex_init(&mosq->out_packet_mutex, NULL);
pthread_mutex_init(&mosq->current_out_packet_mutex, NULL);
pthread_mutex_init(&mosq->msgtime_mutex, NULL);
pthread_mutex_init(&mosq->msgs_in.mutex, NULL);
pthread_mutex_init(&mosq->msgs_out.mutex, NULL);
pthread_mutex_init(&mosq->mid_mutex, NULL);
mosq->thread_id = pthread_self();
#endif
return MOSQ_ERR_SUCCESS;
}
void mosquitto__destroy(struct mosquitto *mosq)
{
struct mosquitto__packet *packet;
if(!mosq) return;
#ifdef WITH_THREADING
# ifdef HAVE_PTHREAD_CANCEL
if(mosq->threaded == mosq_ts_self && !pthread_equal(mosq->thread_id, pthread_self())){
pthread_cancel(mosq->thread_id);
pthread_join(mosq->thread_id, NULL);
mosq->threaded = mosq_ts_none;
}
# endif
if(mosq->id){
/* If mosq->id is not NULL then the client has already been initialised
* and so the mutexes need destroying. If mosq->id is NULL, the mutexes
* haven't been initialised. */
pthread_mutex_destroy(&mosq->callback_mutex);
pthread_mutex_destroy(&mosq->log_callback_mutex);
pthread_mutex_destroy(&mosq->state_mutex);
pthread_mutex_destroy(&mosq->out_packet_mutex);
pthread_mutex_destroy(&mosq->current_out_packet_mutex);
pthread_mutex_destroy(&mosq->msgtime_mutex);
pthread_mutex_destroy(&mosq->msgs_in.mutex);
pthread_mutex_destroy(&mosq->msgs_out.mutex);
pthread_mutex_destroy(&mosq->mid_mutex);
}
#endif
if(mosq->sock != INVALID_SOCKET){
net__socket_close(mosq);
}
message__cleanup_all(mosq);
will__clear(mosq);
#ifdef WITH_TLS
if(mosq->ssl){
SSL_free(mosq->ssl);
}
if(mosq->ssl_ctx){
SSL_CTX_free(mosq->ssl_ctx);
}
mosquitto__free(mosq->tls_cafile);
mosquitto__free(mosq->tls_capath);
mosquitto__free(mosq->tls_certfile);
mosquitto__free(mosq->tls_keyfile);
if(mosq->tls_pw_callback) mosq->tls_pw_callback = NULL;
mosquitto__free(mosq->tls_version);
mosquitto__free(mosq->tls_ciphers);
mosquitto__free(mosq->tls_psk);
mosquitto__free(mosq->tls_psk_identity);
mosquitto__free(mosq->tls_alpn);
#endif
mosquitto__free(mosq->address);
mosq->address = NULL;
mosquitto__free(mosq->id);
mosq->id = NULL;
mosquitto__free(mosq->username);
mosq->username = NULL;
mosquitto__free(mosq->password);
mosq->password = NULL;
mosquitto__free(mosq->host);
mosq->host = NULL;
mosquitto__free(mosq->bind_address);
mosq->bind_address = NULL;
/* Out packet cleanup */
if(mosq->out_packet && !mosq->current_out_packet){
mosq->current_out_packet = mosq->out_packet;
mosq->out_packet = mosq->out_packet->next;
}
while(mosq->current_out_packet){
packet = mosq->current_out_packet;
/* Free data and reset values */
mosq->current_out_packet = mosq->out_packet;
if(mosq->out_packet){
mosq->out_packet = mosq->out_packet->next;
}
packet__cleanup(packet);
mosquitto__free(packet);
}
packet__cleanup(&mosq->in_packet);
if(mosq->sockpairR != INVALID_SOCKET){
COMPAT_CLOSE(mosq->sockpairR);
mosq->sockpairR = INVALID_SOCKET;
}
if(mosq->sockpairW != INVALID_SOCKET){
COMPAT_CLOSE(mosq->sockpairW);
mosq->sockpairW = INVALID_SOCKET;
}
}
void mosquitto_destroy(struct mosquitto *mosq)
{
if(!mosq) return;
mosquitto__destroy(mosq);
mosquitto__free(mosq);
}
int mosquitto_socket(struct mosquitto *mosq)
{
if(!mosq) return INVALID_SOCKET;
return mosq->sock;
}
bool mosquitto_want_write(struct mosquitto *mosq)
{
bool result = false;
if(mosq->out_packet || mosq->current_out_packet){
result = true;
}
#ifdef WITH_TLS
if(mosq->ssl){
if (mosq->want_write) {
result = true;
}else if(mosq->want_connect){
result = false;
}
}
#endif
return result;
}
const char *mosquitto_strerror(int mosq_errno)
{
switch(mosq_errno){
case MOSQ_ERR_AUTH_CONTINUE:
return "Continue with authentication.";
case MOSQ_ERR_NO_SUBSCRIBERS:
return "No subscribers.";
case MOSQ_ERR_SUB_EXISTS:
return "Subscription already exists.";
case MOSQ_ERR_CONN_PENDING:
return "Connection pending.";
case MOSQ_ERR_SUCCESS:
return "No error.";
case MOSQ_ERR_NOMEM:
return "Out of memory.";
case MOSQ_ERR_PROTOCOL:
return "A network protocol error occurred when communicating with the broker.";
case MOSQ_ERR_INVAL:
return "Invalid function arguments provided.";
case MOSQ_ERR_NO_CONN:
return "The client is not currently connected.";
case MOSQ_ERR_CONN_REFUSED:
return "The connection was refused.";
case MOSQ_ERR_NOT_FOUND:
return "Message not found (internal error).";
case MOSQ_ERR_CONN_LOST:
return "The connection was lost.";
case MOSQ_ERR_TLS:
return "A TLS error occurred.";
case MOSQ_ERR_PAYLOAD_SIZE:
return "Payload too large.";
case MOSQ_ERR_NOT_SUPPORTED:
return "This feature is not supported.";
case MOSQ_ERR_AUTH:
return "Authorisation failed.";
case MOSQ_ERR_ACL_DENIED:
return "Access denied by ACL.";
case MOSQ_ERR_UNKNOWN:
return "Unknown error.";
case MOSQ_ERR_ERRNO:
return strerror(errno);
case MOSQ_ERR_EAI:
return "Lookup error.";
case MOSQ_ERR_PROXY:
return "Proxy error.";
case MOSQ_ERR_MALFORMED_UTF8:
return "Malformed UTF-8";
case MOSQ_ERR_DUPLICATE_PROPERTY:
return "Duplicate property in property list";
case MOSQ_ERR_TLS_HANDSHAKE:
return "TLS handshake failed.";
case MOSQ_ERR_QOS_NOT_SUPPORTED:
return "Requested QoS not supported on server.";
case MOSQ_ERR_OVERSIZE_PACKET:
return "Packet larger than supported by the server.";
case MOSQ_ERR_OCSP:
return "OCSP error.";
default:
return "Unknown error.";
}
}
const char *mosquitto_connack_string(int connack_code)
{
switch(connack_code){
case 0:
return "Connection Accepted.";
case 1:
return "Connection Refused: unacceptable protocol version.";
case 2:
return "Connection Refused: identifier rejected.";
case 3:
return "Connection Refused: broker unavailable.";
case 4:
return "Connection Refused: bad user name or password.";
case 5:
return "Connection Refused: not authorised.";
default:
return "Connection Refused: unknown reason.";
}
}
const char *mosquitto_reason_string(int reason_code)
{
switch(reason_code){
case MQTT_RC_SUCCESS:
return "Success";
case MQTT_RC_GRANTED_QOS1:
return "Granted QoS 1";
case MQTT_RC_GRANTED_QOS2:
return "Granted QoS 2";
case MQTT_RC_DISCONNECT_WITH_WILL_MSG:
return "Disconnect with Will Message";
case MQTT_RC_NO_MATCHING_SUBSCRIBERS:
return "No matching subscribers";
case MQTT_RC_NO_SUBSCRIPTION_EXISTED:
return "No subscription existed";
case MQTT_RC_CONTINUE_AUTHENTICATION:
return "Continue authentication";
case MQTT_RC_REAUTHENTICATE:
return "Re-authenticate";
case MQTT_RC_UNSPECIFIED:
return "Unspecified error";
case MQTT_RC_MALFORMED_PACKET:
return "Malformed Packet";
case MQTT_RC_PROTOCOL_ERROR:
return "Protocol Error";
case MQTT_RC_IMPLEMENTATION_SPECIFIC:
return "Implementation specific error";
case MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION:
return "Unsupported Protocol Version";
case MQTT_RC_CLIENTID_NOT_VALID:
return "Client Identifier not valid";
case MQTT_RC_BAD_USERNAME_OR_PASSWORD:
return "Bad User Name or Password";
case MQTT_RC_NOT_AUTHORIZED:
return "Not authorized";
case MQTT_RC_SERVER_UNAVAILABLE:
return "Server unavailable";
case MQTT_RC_SERVER_BUSY:
return "Server busy";
case MQTT_RC_BANNED:
return "Banned";
case MQTT_RC_SERVER_SHUTTING_DOWN:
return "Server shutting down";
case MQTT_RC_BAD_AUTHENTICATION_METHOD:
return "Bad authentication method";
case MQTT_RC_KEEP_ALIVE_TIMEOUT:
return "Keep Alive timeout";
case MQTT_RC_SESSION_TAKEN_OVER:
return "Session taken over";
case MQTT_RC_TOPIC_FILTER_INVALID:
return "Topic Filter invalid";
case MQTT_RC_TOPIC_NAME_INVALID:
return "Topic Name invalid";
case MQTT_RC_PACKET_ID_IN_USE:
return "Packet Identifier in use";
case MQTT_RC_PACKET_ID_NOT_FOUND:
return "Packet Identifier not found";
case MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED:
return "Receive Maximum exceeded";
case MQTT_RC_TOPIC_ALIAS_INVALID:
return "Topic Alias invalid";
case MQTT_RC_PACKET_TOO_LARGE:
return "Packet too large";
case MQTT_RC_MESSAGE_RATE_TOO_HIGH:
return "Message rate too high";
case MQTT_RC_QUOTA_EXCEEDED:
return "Quota exceeded";
case MQTT_RC_ADMINISTRATIVE_ACTION:
return "Administrative action";
case MQTT_RC_PAYLOAD_FORMAT_INVALID:
return "Payload format invalid";
case MQTT_RC_RETAIN_NOT_SUPPORTED:
return "Retain not supported";
case MQTT_RC_QOS_NOT_SUPPORTED:
return "QoS not supported";
case MQTT_RC_USE_ANOTHER_SERVER:
return "Use another server";
case MQTT_RC_SERVER_MOVED:
return "Server moved";
case MQTT_RC_SHARED_SUBS_NOT_SUPPORTED:
return "Shared Subscriptions not supported";
case MQTT_RC_CONNECTION_RATE_EXCEEDED:
return "Connection rate exceeded";
case MQTT_RC_MAXIMUM_CONNECT_TIME:
return "Maximum connect time";
case MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED:
return "Subscription identifiers not supported";
case MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED:
return "Wildcard Subscriptions not supported";
default:
return "Unknown reason";
}
}
int mosquitto_string_to_command(const char *str, int *cmd)
{
if(!strcasecmp(str, "connect")){
*cmd = CMD_CONNECT;
}else if(!strcasecmp(str, "connack")){
*cmd = CMD_CONNACK;
}else if(!strcasecmp(str, "publish")){
*cmd = CMD_PUBLISH;
}else if(!strcasecmp(str, "puback")){
*cmd = CMD_PUBACK;
}else if(!strcasecmp(str, "pubrec")){
*cmd = CMD_PUBREC;
}else if(!strcasecmp(str, "pubrel")){
*cmd = CMD_PUBREL;
}else if(!strcasecmp(str, "pubcomp")){
*cmd = CMD_PUBCOMP;
}else if(!strcasecmp(str, "subscribe")){
*cmd = CMD_SUBSCRIBE;
}else if(!strcasecmp(str, "unsubscribe")){
*cmd = CMD_UNSUBSCRIBE;
}else if(!strcasecmp(str, "disconnect")){
*cmd = CMD_DISCONNECT;
}else if(!strcasecmp(str, "auth")){
*cmd = CMD_AUTH;
}else if(!strcasecmp(str, "will")){
*cmd = CMD_WILL;
}else{
return MOSQ_ERR_INVAL;
}
return MOSQ_ERR_SUCCESS;
}
int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count)
{
int len;
int hier_count = 1;
int start, stop;
int hier;
int tlen;
int i, j;
if(!subtopic || !topics || !count) return MOSQ_ERR_INVAL;
len = strlen(subtopic);
for(i=0; i<len; i++){
if(subtopic[i] == '/'){
if(i > len-1){
/* Separator at end of line */
}else{
hier_count++;
}
}
}
(*topics) = mosquitto__calloc(hier_count, sizeof(char *));
if(!(*topics)) return MOSQ_ERR_NOMEM;
start = 0;
stop = 0;
hier = 0;
for(i=0; i<len+1; i++){
if(subtopic[i] == '/' || subtopic[i] == '\0'){
stop = i;
if(start != stop){
tlen = stop-start + 1;
(*topics)[hier] = mosquitto__calloc(tlen, sizeof(char));
if(!(*topics)[hier]){
for(j=0; j<hier; j++){
mosquitto__free((*topics)[j]);
}
mosquitto__free((*topics));
return MOSQ_ERR_NOMEM;
}
for(j=start; j<stop; j++){
(*topics)[hier][j-start] = subtopic[j];
}
}
start = i+1;
hier++;
}
}
*count = hier_count;
return MOSQ_ERR_SUCCESS;
}
int mosquitto_sub_topic_tokens_free(char ***topics, int count)
{
int i;
if(!topics || !(*topics) || count<1) return MOSQ_ERR_INVAL;
for(i=0; i<count; i++){
mosquitto__free((*topics)[i]);
}
mosquitto__free(*topics);
return MOSQ_ERR_SUCCESS;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,356 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
Tatsuzo Osawa - Add epoll.
*/
#ifndef MOSQUITTO_INTERNAL_H
#define MOSQUITTO_INTERNAL_H
#include "config.h"
#ifdef WIN32
# include <winsock2.h>
#endif
#ifdef WITH_TLS
# include <openssl/ssl.h>
#else
# include <time.h>
#endif
#include <stdlib.h>
#if defined(WITH_THREADING) && !defined(WITH_BROKER)
# include <pthread.h>
#else
# include <dummypthread.h>
#endif
#ifdef WITH_SRV
# include <ares.h>
#endif
#ifdef WIN32
# if _MSC_VER < 1600
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
# else
# include <stdint.h>
# endif
#else
# include <stdint.h>
#endif
#include "mosquitto.h"
#include "time_mosq.h"
#ifdef WITH_BROKER
# ifdef __linux__
# include <netdb.h>
# endif
# include "uthash.h"
struct mosquitto_client_msg;
#endif
#ifdef WIN32
typedef SOCKET mosq_sock_t;
#else
typedef int mosq_sock_t;
#endif
enum mosquitto_msg_direction {
mosq_md_in = 0,
mosq_md_out = 1
};
enum mosquitto_msg_state {
mosq_ms_invalid = 0,
mosq_ms_publish_qos0 = 1,
mosq_ms_publish_qos1 = 2,
mosq_ms_wait_for_puback = 3,
mosq_ms_publish_qos2 = 4,
mosq_ms_wait_for_pubrec = 5,
mosq_ms_resend_pubrel = 6,
mosq_ms_wait_for_pubrel = 7,
mosq_ms_resend_pubcomp = 8,
mosq_ms_wait_for_pubcomp = 9,
mosq_ms_send_pubrec = 10,
mosq_ms_queued = 11
};
enum mosquitto_client_state {
mosq_cs_new = 0,
mosq_cs_connected = 1,
mosq_cs_disconnecting = 2,
mosq_cs_active = 3,
mosq_cs_connect_pending = 4,
mosq_cs_connect_srv = 5,
mosq_cs_disconnect_ws = 6,
mosq_cs_disconnected = 7,
mosq_cs_socks5_new = 8,
mosq_cs_socks5_start = 9,
mosq_cs_socks5_request = 10,
mosq_cs_socks5_reply = 11,
mosq_cs_socks5_auth_ok = 12,
mosq_cs_socks5_userpass_reply = 13,
mosq_cs_socks5_send_userpass = 14,
mosq_cs_expiring = 15,
mosq_cs_duplicate = 17, /* client that has been taken over by another with the same id */
mosq_cs_disconnect_with_will = 18,
mosq_cs_disused = 19, /* client that has been added to the disused list to be freed */
mosq_cs_authenticating = 20, /* Client has sent CONNECT but is still undergoing extended authentication */
mosq_cs_reauthenticating = 21, /* Client is undergoing reauthentication and shouldn't do anything else until complete */
};
enum mosquitto__protocol {
mosq_p_invalid = 0,
mosq_p_mqtt31 = 1,
mosq_p_mqtt311 = 2,
mosq_p_mqtts = 3,
mosq_p_mqtt5 = 5,
};
enum mosquitto__threaded_state {
mosq_ts_none, /* No threads in use */
mosq_ts_self, /* Threads started by libmosquitto */
mosq_ts_external /* Threads started by external code */
};
enum mosquitto__transport {
mosq_t_invalid = 0,
mosq_t_tcp = 1,
mosq_t_ws = 2,
mosq_t_sctp = 3
};
struct mosquitto__alias{
char *topic;
uint16_t alias;
};
struct session_expiry_list {
struct mosquitto *context;
struct session_expiry_list *prev;
struct session_expiry_list *next;
};
struct mosquitto__packet{
uint8_t *payload;
struct mosquitto__packet *next;
uint32_t remaining_mult;
uint32_t remaining_length;
uint32_t packet_length;
uint32_t to_process;
uint32_t pos;
uint16_t mid;
uint8_t command;
int8_t remaining_count;
};
struct mosquitto_message_all{
struct mosquitto_message_all *next;
struct mosquitto_message_all *prev;
mosquitto_property *properties;
time_t timestamp;
//enum mosquitto_msg_direction direction;
enum mosquitto_msg_state state;
bool dup;
struct mosquitto_message msg;
uint32_t expiry_interval;
};
#ifdef WITH_TLS
enum mosquitto__keyform {
mosq_k_pem = 0,
mosq_k_engine = 1,
};
#endif
struct will_delay_list {
struct mosquitto *context;
struct will_delay_list *prev;
struct will_delay_list *next;
};
struct mosquitto_msg_data{
#ifdef WITH_BROKER
struct mosquitto_client_msg *inflight;
struct mosquitto_client_msg *queued;
unsigned long msg_bytes;
unsigned long msg_bytes12;
int msg_count;
int msg_count12;
#else
struct mosquitto_message_all *inflight;
int queue_len;
# ifdef WITH_THREADING
pthread_mutex_t mutex;
# endif
#endif
int inflight_quota;
uint16_t inflight_maximum;
};
struct mosquitto {
mosq_sock_t sock;
#ifndef WITH_BROKER
mosq_sock_t sockpairR, sockpairW;
#endif
#if defined(__GLIBC__) && defined(WITH_ADNS)
struct gaicb *adns; /* For getaddrinfo_a */
#endif
enum mosquitto__protocol protocol;
char *address;
char *id;
char *username;
char *password;
uint16_t keepalive;
uint16_t last_mid;
enum mosquitto_client_state state;
time_t last_msg_in;
time_t next_msg_out;
time_t ping_t;
struct mosquitto__packet in_packet;
struct mosquitto__packet *current_out_packet;
struct mosquitto__packet *out_packet;
struct mosquitto_message_all *will;
struct mosquitto__alias *aliases;
struct will_delay_list *will_delay_entry;
uint32_t maximum_packet_size;
int alias_count;
uint32_t will_delay_interval;
time_t will_delay_time;
#ifdef WITH_TLS
SSL *ssl;
SSL_CTX *ssl_ctx;
char *tls_cafile;
char *tls_capath;
char *tls_certfile;
char *tls_keyfile;
int (*tls_pw_callback)(char *buf, int size, int rwflag, void *userdata);
char *tls_version;
char *tls_ciphers;
char *tls_psk;
char *tls_psk_identity;
int tls_cert_reqs;
bool tls_insecure;
bool ssl_ctx_defaults;
bool tls_ocsp_required;
char *tls_engine;
char *tls_engine_kpass_sha1;
enum mosquitto__keyform tls_keyform;
char *tls_alpn;
#endif
bool want_write;
bool want_connect;
#if defined(WITH_THREADING) && !defined(WITH_BROKER)
pthread_mutex_t callback_mutex;
pthread_mutex_t log_callback_mutex;
pthread_mutex_t msgtime_mutex;
pthread_mutex_t out_packet_mutex;
pthread_mutex_t current_out_packet_mutex;
pthread_mutex_t state_mutex;
pthread_mutex_t mid_mutex;
pthread_t thread_id;
#endif
bool clean_start;
uint32_t session_expiry_interval;
time_t session_expiry_time;
#ifdef WITH_BROKER
bool removed_from_by_id; /* True if removed from by_id hash */
bool is_dropping;
bool is_bridge;
struct mosquitto__bridge *bridge;
struct mosquitto_msg_data msgs_in;
struct mosquitto_msg_data msgs_out;
struct mosquitto__acl_user *acl_list;
struct mosquitto__listener *listener;
struct mosquitto__packet *out_packet_last;
struct mosquitto__subhier **subs;
struct mosquitto__subshared_ref **shared_subs;
char *auth_method;
int sub_count;
int shared_sub_count;
int pollfd_index;
# ifdef WITH_WEBSOCKETS
# if defined(LWS_LIBRARY_VERSION_NUMBER)
struct lws *wsi;
# else
struct libwebsocket_context *ws_context;
struct libwebsocket *wsi;
# endif
# endif
bool ws_want_write;
bool assigned_id;
#else
# ifdef WITH_SOCKS
char *socks5_host;
int socks5_port;
char *socks5_username;
char *socks5_password;
# endif
void *userdata;
bool in_callback;
struct mosquitto_msg_data msgs_in;
struct mosquitto_msg_data msgs_out;
void (*on_connect)(struct mosquitto *, void *userdata, int rc);
void (*on_connect_with_flags)(struct mosquitto *, void *userdata, int rc, int flags);
void (*on_connect_v5)(struct mosquitto *, void *userdata, int rc, int flags, const mosquitto_property *props);
void (*on_disconnect)(struct mosquitto *, void *userdata, int rc);
void (*on_disconnect_v5)(struct mosquitto *, void *userdata, int rc, const mosquitto_property *props);
void (*on_publish)(struct mosquitto *, void *userdata, int mid);
void (*on_publish_v5)(struct mosquitto *, void *userdata, int mid, int reason_code, const mosquitto_property *props);
void (*on_message)(struct mosquitto *, void *userdata, const struct mosquitto_message *message);
void (*on_message_v5)(struct mosquitto *, void *userdata, const struct mosquitto_message *message, const mosquitto_property *props);
void (*on_subscribe)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos);
void (*on_subscribe_v5)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);
void (*on_unsubscribe)(struct mosquitto *, void *userdata, int mid);
void (*on_unsubscribe_v5)(struct mosquitto *, void *userdata, int mid, const mosquitto_property *props);
void (*on_log)(struct mosquitto *, void *userdata, int level, const char *str);
//void (*on_error)();
char *host;
int port;
char *bind_address;
unsigned int reconnects;
unsigned int reconnect_delay;
unsigned int reconnect_delay_max;
bool reconnect_exponential_backoff;
char threaded;
struct mosquitto__packet *out_packet_last;
# ifdef WITH_SRV
ares_channel achan;
# endif
#endif
uint8_t maximum_qos;
#ifdef WITH_BROKER
UT_hash_handle hh_id;
UT_hash_handle hh_sock;
struct mosquitto *for_free_next;
struct session_expiry_list *expiry_list_item;
#endif
#ifdef WITH_EPOLL
uint32_t events;
#endif
};
#define STREMPTY(str) (str[0] == '\0')
void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties);
#endif

@ -0,0 +1,158 @@
/*
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef MQTT_PROTOCOL_H
#define MQTT_PROTOCOL_H
#define PROTOCOL_NAME_v31 "MQIsdp"
#define PROTOCOL_VERSION_v31 3
#define PROTOCOL_NAME "MQTT"
#define PROTOCOL_VERSION_v311 4
#define PROTOCOL_VERSION_v5 5
/* Message types */
#define CMD_CONNECT 0x10
#define CMD_CONNACK 0x20
#define CMD_PUBLISH 0x30
#define CMD_PUBACK 0x40
#define CMD_PUBREC 0x50
#define CMD_PUBREL 0x60
#define CMD_PUBCOMP 0x70
#define CMD_SUBSCRIBE 0x80
#define CMD_SUBACK 0x90
#define CMD_UNSUBSCRIBE 0xA0
#define CMD_UNSUBACK 0xB0
#define CMD_PINGREQ 0xC0
#define CMD_PINGRESP 0xD0
#define CMD_DISCONNECT 0xE0
#define CMD_AUTH 0xF0
/* Mosquitto only: for distinguishing CONNECT and WILL properties */
#define CMD_WILL 0x100
enum mqtt311_connack_codes {
CONNACK_ACCEPTED = 0,
CONNACK_REFUSED_PROTOCOL_VERSION = 1,
CONNACK_REFUSED_IDENTIFIER_REJECTED = 2,
CONNACK_REFUSED_SERVER_UNAVAILABLE = 3,
CONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4,
CONNACK_REFUSED_NOT_AUTHORIZED = 5,
};
enum mqtt5_return_codes {
MQTT_RC_SUCCESS = 0, /* CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH */
MQTT_RC_NORMAL_DISCONNECTION = 0, /* DISCONNECT */
MQTT_RC_GRANTED_QOS0 = 0, /* SUBACK */
MQTT_RC_GRANTED_QOS1 = 1, /* SUBACK */
MQTT_RC_GRANTED_QOS2 = 2, /* SUBACK */
MQTT_RC_DISCONNECT_WITH_WILL_MSG = 4, /* DISCONNECT */
MQTT_RC_NO_MATCHING_SUBSCRIBERS = 16, /* PUBACK, PUBREC */
MQTT_RC_NO_SUBSCRIPTION_EXISTED = 17, /* UNSUBACK */
MQTT_RC_CONTINUE_AUTHENTICATION = 24, /* AUTH */
MQTT_RC_REAUTHENTICATE = 25, /* AUTH */
MQTT_RC_UNSPECIFIED = 128, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */
MQTT_RC_MALFORMED_PACKET = 129, /* CONNACK, DISCONNECT */
MQTT_RC_PROTOCOL_ERROR = 130, /* DISCONNECT */
MQTT_RC_IMPLEMENTATION_SPECIFIC = 131, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */
MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION = 132, /* CONNACK */
MQTT_RC_CLIENTID_NOT_VALID = 133, /* CONNACK */
MQTT_RC_BAD_USERNAME_OR_PASSWORD = 134, /* CONNACK */
MQTT_RC_NOT_AUTHORIZED = 135, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */
MQTT_RC_SERVER_UNAVAILABLE = 136, /* CONNACK */
MQTT_RC_SERVER_BUSY = 137, /* CONNACK, DISCONNECT */
MQTT_RC_BANNED = 138, /* CONNACK */
MQTT_RC_SERVER_SHUTTING_DOWN = 139, /* DISCONNECT */
MQTT_RC_BAD_AUTHENTICATION_METHOD = 140, /* CONNACK */
MQTT_RC_KEEP_ALIVE_TIMEOUT = 141, /* DISCONNECT */
MQTT_RC_SESSION_TAKEN_OVER = 142, /* DISCONNECT */
MQTT_RC_TOPIC_FILTER_INVALID = 143, /* SUBACK, UNSUBACK, DISCONNECT */
MQTT_RC_TOPIC_NAME_INVALID = 144, /* CONNACK, PUBACK, PUBREC, DISCONNECT */
MQTT_RC_PACKET_ID_IN_USE = 145, /* PUBACK, SUBACK, UNSUBACK */
MQTT_RC_PACKET_ID_NOT_FOUND = 146, /* PUBREL, PUBCOMP */
MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED = 147, /* DISCONNECT */
MQTT_RC_TOPIC_ALIAS_INVALID = 148, /* DISCONNECT */
MQTT_RC_PACKET_TOO_LARGE = 149, /* CONNACK, PUBACK, PUBREC, DISCONNECT */
MQTT_RC_MESSAGE_RATE_TOO_HIGH = 150, /* DISCONNECT */
MQTT_RC_QUOTA_EXCEEDED = 151, /* PUBACK, PUBREC, SUBACK, DISCONNECT */
MQTT_RC_ADMINISTRATIVE_ACTION = 152, /* DISCONNECT */
MQTT_RC_PAYLOAD_FORMAT_INVALID = 153, /* CONNACK, DISCONNECT */
MQTT_RC_RETAIN_NOT_SUPPORTED = 154, /* CONNACK, DISCONNECT */
MQTT_RC_QOS_NOT_SUPPORTED = 155, /* CONNACK, DISCONNECT */
MQTT_RC_USE_ANOTHER_SERVER = 156, /* CONNACK, DISCONNECT */
MQTT_RC_SERVER_MOVED = 157, /* CONNACK, DISCONNECT */
MQTT_RC_SHARED_SUBS_NOT_SUPPORTED = 158, /* SUBACK, DISCONNECT */
MQTT_RC_CONNECTION_RATE_EXCEEDED = 159, /* CONNACK, DISCONNECT */
MQTT_RC_MAXIMUM_CONNECT_TIME = 160, /* DISCONNECT */
MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED = 161, /* SUBACK, DISCONNECT */
MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED = 162, /* SUBACK, DISCONNECT */
};
enum mqtt5_property {
MQTT_PROP_PAYLOAD_FORMAT_INDICATOR = 1, /* Byte : PUBLISH, Will Properties */
MQTT_PROP_MESSAGE_EXPIRY_INTERVAL = 2, /* 4 byte int : PUBLISH, Will Properties */
MQTT_PROP_CONTENT_TYPE = 3, /* UTF-8 string : PUBLISH, Will Properties */
MQTT_PROP_RESPONSE_TOPIC = 8, /* UTF-8 string : PUBLISH, Will Properties */
MQTT_PROP_CORRELATION_DATA = 9, /* Binary Data : PUBLISH, Will Properties */
MQTT_PROP_SUBSCRIPTION_IDENTIFIER = 11, /* Variable byte int : PUBLISH, SUBSCRIBE */
MQTT_PROP_SESSION_EXPIRY_INTERVAL = 17, /* 4 byte int : CONNECT, CONNACK, DISCONNECT */
MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER = 18, /* UTF-8 string : CONNACK */
MQTT_PROP_SERVER_KEEP_ALIVE = 19, /* 2 byte int : CONNACK */
MQTT_PROP_AUTHENTICATION_METHOD = 21, /* UTF-8 string : CONNECT, CONNACK, AUTH */
MQTT_PROP_AUTHENTICATION_DATA = 22, /* Binary Data : CONNECT, CONNACK, AUTH */
MQTT_PROP_REQUEST_PROBLEM_INFORMATION = 23, /* Byte : CONNECT */
MQTT_PROP_WILL_DELAY_INTERVAL = 24, /* 4 byte int : Will properties */
MQTT_PROP_REQUEST_RESPONSE_INFORMATION = 25,/* Byte : CONNECT */
MQTT_PROP_RESPONSE_INFORMATION = 26, /* UTF-8 string : CONNACK */
MQTT_PROP_SERVER_REFERENCE = 28, /* UTF-8 string : CONNACK, DISCONNECT */
MQTT_PROP_REASON_STRING = 31, /* UTF-8 string : All except Will properties */
MQTT_PROP_RECEIVE_MAXIMUM = 33, /* 2 byte int : CONNECT, CONNACK */
MQTT_PROP_TOPIC_ALIAS_MAXIMUM = 34, /* 2 byte int : CONNECT, CONNACK */
MQTT_PROP_TOPIC_ALIAS = 35, /* 2 byte int : PUBLISH */
MQTT_PROP_MAXIMUM_QOS = 36, /* Byte : CONNACK */
MQTT_PROP_RETAIN_AVAILABLE = 37, /* Byte : CONNACK */
MQTT_PROP_USER_PROPERTY = 38, /* UTF-8 string pair : All */
MQTT_PROP_MAXIMUM_PACKET_SIZE = 39, /* 4 byte int : CONNECT, CONNACK */
MQTT_PROP_WILDCARD_SUB_AVAILABLE = 40, /* Byte : CONNACK */
MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE = 41, /* Byte : CONNACK */
MQTT_PROP_SHARED_SUB_AVAILABLE = 42, /* Byte : CONNACK */
};
enum mqtt5_property_type {
MQTT_PROP_TYPE_BYTE = 1,
MQTT_PROP_TYPE_INT16 = 2,
MQTT_PROP_TYPE_INT32 = 3,
MQTT_PROP_TYPE_VARINT = 4,
MQTT_PROP_TYPE_BINARY = 5,
MQTT_PROP_TYPE_STRING = 6,
MQTT_PROP_TYPE_STRING_PAIR = 7
};
enum mqtt5_sub_options {
MQTT_SUB_OPT_NO_LOCAL = 0x04,
MQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08,
MQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00,
MQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10,
MQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20,
};
#define MQTT_MAX_PAYLOAD 268435455
#endif

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save