OPENSSL报错: undefined symbol
@ -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.
|
@ -0,0 +1 @@
|
||||
/build
|
@ -0,0 +1,50 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
ndkVersion '22.1.7171670'
|
||||
buildToolsVersion "29.0.2"
|
||||
defaultConfig {
|
||||
applicationId "moe.key.yao.mqtt.client"
|
||||
minSdkVersion 21
|
||||
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.101", 1883, "mqtt_android_client", false)
|
||||
//MqttClient.getInstance().start("192.168.1.101", 8883, "mqtt_android_client", false, caFilePath, "username", "password")
|
||||
}
|
||||
|
||||
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>
|
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 8.9 KiB |
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,21 @@
|
||||
# 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
|
@ -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
|
@ -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" "$@"
|
@ -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'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
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,95 @@
|
||||
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
|
||||
|
||||
# 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 += 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
|
||||
|
||||
# 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-21
|
||||
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,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/opensslconf.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;
|
||||
}
|
||||
|
@ -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
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
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 NET_MOSQ_H
|
||||
#define NET_MOSQ_H
|
||||
|
||||
#ifndef WIN32
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <winsock2.h>
|
||||
# ifndef _SSIZE_T_DEFINED
|
||||
typedef SSIZE_T ssize_t;
|
||||
# define _SSIZE_T_DEFINED
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "mosquitto_internal.h"
|
||||
#include "mosquitto.h"
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
struct mosquitto_db;
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
# define COMPAT_CLOSE(a) closesocket(a)
|
||||
# define COMPAT_ECONNRESET WSAECONNRESET
|
||||
# define COMPAT_EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#else
|
||||
# define COMPAT_CLOSE(a) close(a)
|
||||
# define COMPAT_ECONNRESET ECONNRESET
|
||||
# define COMPAT_EWOULDBLOCK EWOULDBLOCK
|
||||
#endif
|
||||
|
||||
/* For when not using winsock libraries. */
|
||||
#ifndef INVALID_SOCKET
|
||||
#define INVALID_SOCKET -1
|
||||
#endif
|
||||
|
||||
/* Macros for accessing the MSB and LSB of a uint16_t */
|
||||
#define MOSQ_MSB(A) (uint8_t)((A & 0xFF00) >> 8)
|
||||
#define MOSQ_LSB(A) (uint8_t)(A & 0x00FF)
|
||||
|
||||
int net__init(void);
|
||||
void net__cleanup(void);
|
||||
|
||||
int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking);
|
||||
#ifdef WITH_BROKER
|
||||
int net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq);
|
||||
#else
|
||||
int net__socket_close(struct mosquitto *mosq);
|
||||
#endif
|
||||
int net__try_connect(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking);
|
||||
int net__try_connect_step1(struct mosquitto *mosq, const char *host);
|
||||
int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock);
|
||||
int net__socket_connect_step3(struct mosquitto *mosq, const char *host);
|
||||
int net__socket_nonblock(mosq_sock_t *sock);
|
||||
int net__socketpair(mosq_sock_t *sp1, mosq_sock_t *sp2);
|
||||
|
||||
ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count);
|
||||
ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count);
|
||||
|
||||
#ifdef WITH_TLS
|
||||
int net__socket_apply_tls(struct mosquitto *mosq);
|
||||
int net__socket_connect_tls(struct mosquitto *mosq);
|
||||
int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg);
|
||||
UI_METHOD *net__get_ui_method(void);
|
||||
#define ENGINE_FINISH(e) if(e) ENGINE_finish(e)
|
||||
#define ENGINE_SECRET_MODE "SECRET_MODE"
|
||||
#define ENGINE_SECRET_MODE_SHA 0x1000
|
||||
#define ENGINE_PIN "PIN"
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
|
||||
Copyright (c) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG), Dr. Lars Voelker <lars.voelker@bmw.de>
|
||||
|
||||
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:
|
||||
Dr. Lars Voelker, BMW AG
|
||||
*/
|
||||
|
||||
/*
|
||||
COPYRIGHT AND PERMISSION NOTICE of curl on which the ocsp code is based:
|
||||
|
||||
Copyright (c) 1996 - 2016, Daniel Stenberg, <daniel@haxx.se>, and many
|
||||
contributors, see the THANKS file.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright
|
||||
notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of a copyright holder shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization of the copyright holder.
|
||||
*/
|
||||
|
||||
#ifdef WITH_TLS
|
||||
#include <logging_mosq.h>
|
||||
#include <mosquitto_internal.h>
|
||||
#include <net_mosq.h>
|
||||
|
||||
#include <openssl/safestack.h>
|
||||
#include <openssl/tls1.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/ocsp.h>
|
||||
|
||||
int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg)
|
||||
{
|
||||
struct mosquitto *mosq = (struct mosquitto *)arg;
|
||||
int ocsp_status, result2, i;
|
||||
unsigned char *p;
|
||||
const unsigned char *cp;
|
||||
OCSP_RESPONSE *rsp = NULL;
|
||||
OCSP_BASICRESP *br = NULL;
|
||||
X509_STORE *st = NULL;
|
||||
STACK_OF(X509) *ch = NULL;
|
||||
|
||||
long len = SSL_get_tlsext_status_ocsp_resp(mosq->ssl, &p);
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL_get_tlsext_status_ocsp_resp returned %ld bytes", len);
|
||||
|
||||
// the following functions expect a const pointer
|
||||
cp = (const unsigned char *)p;
|
||||
|
||||
if (!cp || len <= 0) {
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: no response");
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
||||
rsp = d2i_OCSP_RESPONSE(NULL, &cp, len);
|
||||
if (rsp==NULL) {
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ocsp_status = OCSP_response_status(rsp);
|
||||
if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid status: %s (%d)",
|
||||
OCSP_response_status_str(ocsp_status), ocsp_status);
|
||||
goto end;
|
||||
}
|
||||
|
||||
br = OCSP_response_get1_basic(rsp);
|
||||
if (!br) {
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ch = SSL_get_peer_cert_chain(mosq->ssl);
|
||||
if (sk_X509_num(ch) <= 0) {
|
||||
log__printf(mosq, MOSQ_LOG_ERR, "OCSP: we did not receive certificates of the server (num: %d)", sk_X509_num(ch));
|
||||
goto end;
|
||||
}
|
||||
|
||||
st = SSL_CTX_get_cert_store(mosq->ssl_ctx);
|
||||
|
||||
// Note:
|
||||
// Other checkers often fix problems in OpenSSL before 1.0.2a (e.g. libcurl).
|
||||
// For all currently supported versions of the OpenSSL project, this is not needed anymore.
|
||||
|
||||
if ((result2=OCSP_basic_verify(br, ch, st, 0)) <= 0) {
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: response verification failed (error: %d)", result2);
|
||||
goto end;
|
||||
}
|
||||
|
||||
for(i = 0; i < OCSP_resp_count(br); i++) {
|
||||
int cert_status, crl_reason;
|
||||
OCSP_SINGLERESP *single = NULL;
|
||||
|
||||
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
|
||||
|
||||
single = OCSP_resp_get0(br, i);
|
||||
if(!single)
|
||||
continue;
|
||||
|
||||
cert_status = OCSP_single_get0_status(single, &crl_reason, &rev, &thisupd, &nextupd);
|
||||
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate status: %s (%d)",
|
||||
OCSP_cert_status_str(cert_status), cert_status);
|
||||
|
||||
switch(cert_status) {
|
||||
case V_OCSP_CERTSTATUS_GOOD:
|
||||
// Note: A OCSP stapling result will be accepted up to 5 minutes after it expired!
|
||||
if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) {
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: OCSP response has expired");
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
|
||||
case V_OCSP_CERTSTATUS_REVOKED:
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation reason: %s (%d)",
|
||||
OCSP_crl_reason_str(crl_reason), crl_reason);
|
||||
goto end;
|
||||
|
||||
case V_OCSP_CERTSTATUS_UNKNOWN:
|
||||
goto end;
|
||||
|
||||
default:
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation status unknown");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (br!=NULL) OCSP_BASICRESP_free(br);
|
||||
if (rsp!=NULL) OCSP_RESPONSE_free(rsp);
|
||||
return 1; // OK
|
||||
|
||||
end:
|
||||
if (br!=NULL) OCSP_BASICRESP_free(br);
|
||||
if (rsp!=NULL) OCSP_RESPONSE_free(rsp);
|
||||
return 0; // Not OK
|
||||
}
|
||||
#endif
|
@ -0,0 +1,493 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
#ifndef WIN32
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WITH_TLS
|
||||
# ifdef WIN32
|
||||
# include <winsock2.h>
|
||||
# endif
|
||||
# include <openssl/engine.h>
|
||||
#endif
|
||||
|
||||
#include "mosquitto.h"
|
||||
#include "mosquitto_internal.h"
|
||||
#include "memory_mosq.h"
|
||||
#include "mqtt_protocol.h"
|
||||
#include "util_mosq.h"
|
||||
#include "will_mosq.h"
|
||||
|
||||
|
||||
int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain)
|
||||
{
|
||||
return mosquitto_will_set_v5(mosq, topic, payloadlen, payload, qos, retain, NULL);
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
|
||||
if(properties){
|
||||
rc = mosquitto_property_check_all(CMD_WILL, properties);
|
||||
if(rc) return rc;
|
||||
}
|
||||
|
||||
return will__set(mosq, topic, payloadlen, payload, qos, retain, properties);
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_will_clear(struct mosquitto *mosq)
|
||||
{
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
return will__clear(mosq);
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password)
|
||||
{
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){
|
||||
if(password != NULL && username == NULL){
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
mosquitto__free(mosq->username);
|
||||
mosq->username = NULL;
|
||||
|
||||
mosquitto__free(mosq->password);
|
||||
mosq->password = NULL;
|
||||
|
||||
if(username){
|
||||
if(mosquitto_validate_utf8(username, strlen(username))){
|
||||
return MOSQ_ERR_MALFORMED_UTF8;
|
||||
}
|
||||
mosq->username = mosquitto__strdup(username);
|
||||
if(!mosq->username) return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if(password){
|
||||
mosq->password = mosquitto__strdup(password);
|
||||
if(!mosq->password){
|
||||
mosquitto__free(mosq->username);
|
||||
mosq->username = NULL;
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff)
|
||||
{
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
|
||||
if(reconnect_delay == 0) reconnect_delay = 1;
|
||||
|
||||
mosq->reconnect_delay = reconnect_delay;
|
||||
mosq->reconnect_delay_max = reconnect_delay_max;
|
||||
mosq->reconnect_exponential_backoff = reconnect_exponential_backoff;
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *capath, const char *certfile, const char *keyfile, int (*pw_callback)(char *buf, int size, int rwflag, void *userdata))
|
||||
{
|
||||
#ifdef WITH_TLS
|
||||
FILE *fptr;
|
||||
|
||||
if(!mosq || (!cafile && !capath) || (certfile && !keyfile) || (!certfile && keyfile)) return MOSQ_ERR_INVAL;
|
||||
|
||||
mosquitto__free(mosq->tls_cafile);
|
||||
mosq->tls_cafile = NULL;
|
||||
if(cafile){
|
||||
fptr = mosquitto__fopen(cafile, "rt", false);
|
||||
if(fptr){
|
||||
fclose(fptr);
|
||||
}else{
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
mosq->tls_cafile = mosquitto__strdup(cafile);
|
||||
|
||||
if(!mosq->tls_cafile){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
mosquitto__free(mosq->tls_capath);
|
||||
mosq->tls_capath = NULL;
|
||||
if(capath){
|
||||
mosq->tls_capath = mosquitto__strdup(capath);
|
||||
if(!mosq->tls_capath){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
mosquitto__free(mosq->tls_certfile);
|
||||
mosq->tls_certfile = NULL;
|
||||
if(certfile){
|
||||
fptr = mosquitto__fopen(certfile, "rt", false);
|
||||
if(fptr){
|
||||
fclose(fptr);
|
||||
}else{
|
||||
mosquitto__free(mosq->tls_cafile);
|
||||
mosq->tls_cafile = NULL;
|
||||
|
||||
mosquitto__free(mosq->tls_capath);
|
||||
mosq->tls_capath = NULL;
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
mosq->tls_certfile = mosquitto__strdup(certfile);
|
||||
if(!mosq->tls_certfile){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
mosquitto__free(mosq->tls_keyfile);
|
||||
mosq->tls_keyfile = NULL;
|
||||
if(keyfile){
|
||||
fptr = mosquitto__fopen(keyfile, "rt", false);
|
||||
if(fptr){
|
||||
fclose(fptr);
|
||||
}else{
|
||||
mosquitto__free(mosq->tls_cafile);
|
||||
mosq->tls_cafile = NULL;
|
||||
|
||||
mosquitto__free(mosq->tls_capath);
|
||||
mosq->tls_capath = NULL;
|
||||
|
||||
mosquitto__free(mosq->tls_certfile);
|
||||
mosq->tls_certfile = NULL;
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
mosq->tls_keyfile = mosquitto__strdup(keyfile);
|
||||
if(!mosq->tls_keyfile){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
mosq->tls_pw_callback = pw_callback;
|
||||
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers)
|
||||
{
|
||||
#ifdef WITH_TLS
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
|
||||
mosq->tls_cert_reqs = cert_reqs;
|
||||
if(tls_version){
|
||||
if(!strcasecmp(tls_version, "tlsv1.3")
|
||||
|| !strcasecmp(tls_version, "tlsv1.2")
|
||||
|| !strcasecmp(tls_version, "tlsv1.1")){
|
||||
|
||||
mosq->tls_version = mosquitto__strdup(tls_version);
|
||||
if(!mosq->tls_version) return MOSQ_ERR_NOMEM;
|
||||
}else{
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
}else{
|
||||
mosq->tls_version = mosquitto__strdup("tlsv1.2");
|
||||
if(!mosq->tls_version) return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
if(ciphers){
|
||||
mosq->tls_ciphers = mosquitto__strdup(ciphers);
|
||||
if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM;
|
||||
}else{
|
||||
mosq->tls_ciphers = NULL;
|
||||
}
|
||||
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value)
|
||||
{
|
||||
#ifdef WITH_TLS
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
mosq->tls_insecure = value;
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value)
|
||||
{
|
||||
#ifdef WITH_TLS
|
||||
ENGINE *eng;
|
||||
char *str;
|
||||
#endif
|
||||
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
|
||||
switch(option){
|
||||
case MOSQ_OPT_TLS_ENGINE:
|
||||
#ifdef WITH_TLS
|
||||
# if !defined(OPENSSL_NO_ENGINE)
|
||||
eng = ENGINE_by_id(value);
|
||||
if(!eng){
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
ENGINE_free(eng); /* release the structural reference from ENGINE_by_id() */
|
||||
mosq->tls_engine = mosquitto__strdup(value);
|
||||
if(!mosq->tls_engine){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#endif
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MOSQ_OPT_TLS_KEYFORM:
|
||||
#ifdef WITH_TLS
|
||||
if(!value) return MOSQ_ERR_INVAL;
|
||||
if(!strcasecmp(value, "pem")){
|
||||
mosq->tls_keyform = mosq_k_pem;
|
||||
}else if (!strcasecmp(value, "engine")){
|
||||
mosq->tls_keyform = mosq_k_engine;
|
||||
}else{
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
||||
case MOSQ_OPT_TLS_ENGINE_KPASS_SHA1:
|
||||
#ifdef WITH_TLS
|
||||
if(mosquitto__hex2bin_sha1(value, (unsigned char**)&str) != MOSQ_ERR_SUCCESS){
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
mosq->tls_engine_kpass_sha1 = str;
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MOSQ_OPT_TLS_ALPN:
|
||||
#ifdef WITH_TLS
|
||||
mosq->tls_alpn = mosquitto__strdup(value);
|
||||
if(!mosq->tls_alpn){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers)
|
||||
{
|
||||
#ifdef FINAL_WITH_TLS_PSK
|
||||
if(!mosq || !psk || !identity) return MOSQ_ERR_INVAL;
|
||||
|
||||
/* Check for hex only digits */
|
||||
if(strspn(psk, "0123456789abcdefABCDEF") < strlen(psk)){
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
mosq->tls_psk = mosquitto__strdup(psk);
|
||||
if(!mosq->tls_psk) return MOSQ_ERR_NOMEM;
|
||||
|
||||
mosq->tls_psk_identity = mosquitto__strdup(identity);
|
||||
if(!mosq->tls_psk_identity){
|
||||
mosquitto__free(mosq->tls_psk);
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
if(ciphers){
|
||||
mosq->tls_ciphers = mosquitto__strdup(ciphers);
|
||||
if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM;
|
||||
}else{
|
||||
mosq->tls_ciphers = NULL;
|
||||
}
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value)
|
||||
{
|
||||
int ival;
|
||||
|
||||
if(!mosq || !value) return MOSQ_ERR_INVAL;
|
||||
|
||||
switch(option){
|
||||
case MOSQ_OPT_PROTOCOL_VERSION:
|
||||
ival = *((int *)value);
|
||||
return mosquitto_int_option(mosq, option, ival);
|
||||
case MOSQ_OPT_SSL_CTX:
|
||||
#ifdef WITH_TLS
|
||||
mosq->ssl_ctx = (SSL_CTX *)value;
|
||||
if(mosq->ssl_ctx){
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
SSL_CTX_up_ref(mosq->ssl_ctx);
|
||||
#else
|
||||
CRYPTO_add(&(mosq->ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
default:
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value)
|
||||
{
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
|
||||
switch(option){
|
||||
case MOSQ_OPT_PROTOCOL_VERSION:
|
||||
if(value == MQTT_PROTOCOL_V31){
|
||||
mosq->protocol = mosq_p_mqtt31;
|
||||
}else if(value == MQTT_PROTOCOL_V311){
|
||||
mosq->protocol = mosq_p_mqtt311;
|
||||
}else if(value == MQTT_PROTOCOL_V5){
|
||||
mosq->protocol = mosq_p_mqtt5;
|
||||
}else{
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case MOSQ_OPT_RECEIVE_MAXIMUM:
|
||||
if(value < 0 || value > 65535){
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
if(value == 0){
|
||||
mosq->msgs_in.inflight_maximum = 65535;
|
||||
}else{
|
||||
mosq->msgs_in.inflight_maximum = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case MOSQ_OPT_SEND_MAXIMUM:
|
||||
if(value < 0 || value > 65535){
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
if(value == 0){
|
||||
mosq->msgs_out.inflight_maximum = 65535;
|
||||
}else{
|
||||
mosq->msgs_out.inflight_maximum = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case MOSQ_OPT_SSL_CTX_WITH_DEFAULTS:
|
||||
#if defined(WITH_TLS) && OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if(value){
|
||||
mosq->ssl_ctx_defaults = true;
|
||||
}else{
|
||||
mosq->ssl_ctx_defaults = false;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
|
||||
case MOSQ_OPT_TLS_OCSP_REQUIRED:
|
||||
#ifdef WITH_TLS
|
||||
mosq->tls_ocsp_required = (bool)value;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value)
|
||||
{
|
||||
if(!mosq || !value) return MOSQ_ERR_INVAL;
|
||||
|
||||
switch(option){
|
||||
case MOSQ_OPT_SSL_CTX:
|
||||
#ifdef WITH_TLS
|
||||
mosq->ssl_ctx = (SSL_CTX *)value;
|
||||
if(mosq->ssl_ctx){
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
SSL_CTX_up_ref(mosq->ssl_ctx);
|
||||
#else
|
||||
CRYPTO_add(&(mosq->ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
default:
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void mosquitto_user_data_set(struct mosquitto *mosq, void *userdata)
|
||||
{
|
||||
if(mosq){
|
||||
mosq->userdata = userdata;
|
||||
}
|
||||
}
|
||||
|
||||
void *mosquitto_userdata(struct mosquitto *mosq)
|
||||
{
|
||||
return mosq->userdata;
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
/*
|
||||
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 <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
# include "mosquitto_broker_internal.h"
|
||||
# ifdef WITH_WEBSOCKETS
|
||||
# include <libwebsockets.h>
|
||||
# endif
|
||||
#else
|
||||
# include "read_handle.h"
|
||||
#endif
|
||||
|
||||
#include "memory_mosq.h"
|
||||
#include "mqtt_protocol.h"
|
||||
#include "net_mosq.h"
|
||||
#include "packet_mosq.h"
|
||||
#include "read_handle.h"
|
||||
#ifdef WITH_BROKER
|
||||
# include "sys_tree.h"
|
||||
#else
|
||||
# define G_BYTES_RECEIVED_INC(A)
|
||||
# define G_BYTES_SENT_INC(A)
|
||||
# define G_MSGS_SENT_INC(A)
|
||||
# define G_PUB_MSGS_SENT_INC(A)
|
||||
#endif
|
||||
|
||||
|
||||
int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte)
|
||||
{
|
||||
assert(packet);
|
||||
if(packet->pos+1 > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
|
||||
|
||||
*byte = packet->payload[packet->pos];
|
||||
packet->pos++;
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte)
|
||||
{
|
||||
assert(packet);
|
||||
assert(packet->pos+1 <= packet->packet_length);
|
||||
|
||||
packet->payload[packet->pos] = byte;
|
||||
packet->pos++;
|
||||
}
|
||||
|
||||
|
||||
int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count)
|
||||
{
|
||||
assert(packet);
|
||||
if(packet->pos+count > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
|
||||
|
||||
memcpy(bytes, &(packet->payload[packet->pos]), count);
|
||||
packet->pos += count;
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count)
|
||||
{
|
||||
assert(packet);
|
||||
assert(packet->pos+count <= packet->packet_length);
|
||||
|
||||
memcpy(&(packet->payload[packet->pos]), bytes, count);
|
||||
packet->pos += count;
|
||||
}
|
||||
|
||||
|
||||
int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, int *length)
|
||||
{
|
||||
uint16_t slen;
|
||||
int rc;
|
||||
|
||||
assert(packet);
|
||||
rc = packet__read_uint16(packet, &slen);
|
||||
if(rc) return rc;
|
||||
|
||||
if(slen == 0){
|
||||
*data = NULL;
|
||||
*length = 0;
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
if(packet->pos+slen > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
|
||||
|
||||
*data = mosquitto__malloc(slen+1);
|
||||
if(*data){
|
||||
memcpy(*data, &(packet->payload[packet->pos]), slen);
|
||||
((uint8_t *)(*data))[slen] = '\0';
|
||||
packet->pos += slen;
|
||||
}else{
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
|
||||
*length = slen;
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int packet__read_string(struct mosquitto__packet *packet, char **str, int *length)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = packet__read_binary(packet, (uint8_t **)str, length);
|
||||
if(rc) return rc;
|
||||
if(*length == 0) return MOSQ_ERR_SUCCESS;
|
||||
|
||||
if(mosquitto_validate_utf8(*str, *length)){
|
||||
mosquitto__free(*str);
|
||||
*str = NULL;
|
||||
*length = -1;
|
||||
return MOSQ_ERR_MALFORMED_UTF8;
|
||||
}
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length)
|
||||
{
|
||||
assert(packet);
|
||||
packet__write_uint16(packet, length);
|
||||
packet__write_bytes(packet, str, length);
|
||||
}
|
||||
|
||||
|
||||
int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word)
|
||||
{
|
||||
uint8_t msb, lsb;
|
||||
|
||||
assert(packet);
|
||||
if(packet->pos+2 > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
|
||||
|
||||
msb = packet->payload[packet->pos];
|
||||
packet->pos++;
|
||||
lsb = packet->payload[packet->pos];
|
||||
packet->pos++;
|
||||
|
||||
*word = (msb<<8) + lsb;
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word)
|
||||
{
|
||||
packet__write_byte(packet, MOSQ_MSB(word));
|
||||
packet__write_byte(packet, MOSQ_LSB(word));
|
||||
}
|
||||
|
||||
|
||||
int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
int i;
|
||||
|
||||
assert(packet);
|
||||
if(packet->pos+4 > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
|
||||
|
||||
for(i=0; i<4; i++){
|
||||
val = (val << 8) + packet->payload[packet->pos];
|
||||
packet->pos++;
|
||||
}
|
||||
|
||||
*word = val;
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word)
|
||||
{
|
||||
packet__write_byte(packet, (word & 0xFF000000) >> 24);
|
||||
packet__write_byte(packet, (word & 0x00FF0000) >> 16);
|
||||
packet__write_byte(packet, (word & 0x0000FF00) >> 8);
|
||||
packet__write_byte(packet, (word & 0x000000FF));
|
||||
}
|
||||
|
||||
|
||||
int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes)
|
||||
{
|
||||
int i;
|
||||
uint8_t byte;
|
||||
int remaining_mult = 1;
|
||||
int32_t lword = 0;
|
||||
uint8_t lbytes = 0;
|
||||
|
||||
for(i=0; i<4; i++){
|
||||
if(packet->pos < packet->remaining_length){
|
||||
lbytes++;
|
||||
byte = packet->payload[packet->pos];
|
||||
lword += (byte & 127) * remaining_mult;
|
||||
remaining_mult *= 128;
|
||||
packet->pos++;
|
||||
if((byte & 128) == 0){
|
||||
if(lbytes > 1 && byte == 0){
|
||||
/* Catch overlong encodings */
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}else{
|
||||
*word = lword;
|
||||
if(bytes) (*bytes) = lbytes;
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}
|
||||
}
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
|
||||
int packet__write_varint(struct mosquitto__packet *packet, int32_t word)
|
||||
{
|
||||
uint8_t byte;
|
||||
int count = 0;
|
||||
|
||||
do{
|
||||
byte = word % 128;
|
||||
word = word / 128;
|
||||
/* If there are more digits to encode, set the top bit of this digit */
|
||||
if(word > 0){
|
||||
byte = byte | 0x80;
|
||||
}
|
||||
packet__write_byte(packet, byte);
|
||||
count++;
|
||||
}while(word > 0 && count < 5);
|
||||
|
||||
if(count == 5){
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int packet__varint_bytes(int32_t word)
|
||||
{
|
||||
if(word < 128){
|
||||
return 1;
|
||||
}else if(word < 16384){
|
||||
return 2;
|
||||
}else if(word < 2097152){
|
||||
return 3;
|
||||
}else if(word < 268435456){
|
||||
return 4;
|
||||
}else{
|
||||
return 5;
|
||||
}
|
||||
}
|
@ -0,0 +1,498 @@
|
||||
/*
|
||||
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 <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
# include "mosquitto_broker_internal.h"
|
||||
# ifdef WITH_WEBSOCKETS
|
||||
# include <libwebsockets.h>
|
||||
# endif
|
||||
#else
|
||||
# include "read_handle.h"
|
||||
#endif
|
||||
|
||||
#include "memory_mosq.h"
|
||||
#include "mqtt_protocol.h"
|
||||
#include "net_mosq.h"
|
||||
#include "packet_mosq.h"
|
||||
#include "read_handle.h"
|
||||
#include "util_mosq.h"
|
||||
#ifdef WITH_BROKER
|
||||
# include "sys_tree.h"
|
||||
# include "send_mosq.h"
|
||||
#else
|
||||
# define G_BYTES_RECEIVED_INC(A)
|
||||
# define G_BYTES_SENT_INC(A)
|
||||
# define G_MSGS_SENT_INC(A)
|
||||
# define G_PUB_MSGS_SENT_INC(A)
|
||||
#endif
|
||||
|
||||
int packet__alloc(struct mosquitto__packet *packet)
|
||||
{
|
||||
uint8_t remaining_bytes[5], byte;
|
||||
uint32_t remaining_length;
|
||||
int i;
|
||||
|
||||
assert(packet);
|
||||
|
||||
remaining_length = packet->remaining_length;
|
||||
packet->payload = NULL;
|
||||
packet->remaining_count = 0;
|
||||
do{
|
||||
byte = remaining_length % 128;
|
||||
remaining_length = remaining_length / 128;
|
||||
/* If there are more digits to encode, set the top bit of this digit */
|
||||
if(remaining_length > 0){
|
||||
byte = byte | 0x80;
|
||||
}
|
||||
remaining_bytes[packet->remaining_count] = byte;
|
||||
packet->remaining_count++;
|
||||
}while(remaining_length > 0 && packet->remaining_count < 5);
|
||||
if(packet->remaining_count == 5) return MOSQ_ERR_PAYLOAD_SIZE;
|
||||
packet->packet_length = packet->remaining_length + 1 + packet->remaining_count;
|
||||
#ifdef WITH_WEBSOCKETS
|
||||
packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
|
||||
#else
|
||||
packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length);
|
||||
#endif
|
||||
if(!packet->payload) return MOSQ_ERR_NOMEM;
|
||||
|
||||
packet->payload[0] = packet->command;
|
||||
for(i=0; i<packet->remaining_count; i++){
|
||||
packet->payload[i+1] = remaining_bytes[i];
|
||||
}
|
||||
packet->pos = 1 + packet->remaining_count;
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void packet__cleanup(struct mosquitto__packet *packet)
|
||||
{
|
||||
if(!packet) return;
|
||||
|
||||
/* Free data and reset values */
|
||||
packet->command = 0;
|
||||
packet->remaining_count = 0;
|
||||
packet->remaining_mult = 1;
|
||||
packet->remaining_length = 0;
|
||||
mosquitto__free(packet->payload);
|
||||
packet->payload = NULL;
|
||||
packet->to_process = 0;
|
||||
packet->pos = 0;
|
||||
}
|
||||
|
||||
|
||||
void packet__cleanup_all(struct mosquitto *mosq)
|
||||
{
|
||||
struct mosquitto__packet *packet;
|
||||
|
||||
pthread_mutex_lock(&mosq->current_out_packet_mutex);
|
||||
pthread_mutex_lock(&mosq->out_packet_mutex);
|
||||
|
||||
/* 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);
|
||||
|
||||
pthread_mutex_unlock(&mosq->out_packet_mutex);
|
||||
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
|
||||
}
|
||||
|
||||
|
||||
int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet)
|
||||
{
|
||||
#ifndef WITH_BROKER
|
||||
char sockpair_data = 0;
|
||||
#endif
|
||||
assert(mosq);
|
||||
assert(packet);
|
||||
|
||||
packet->pos = 0;
|
||||
packet->to_process = packet->packet_length;
|
||||
|
||||
packet->next = NULL;
|
||||
pthread_mutex_lock(&mosq->out_packet_mutex);
|
||||
if(mosq->out_packet){
|
||||
mosq->out_packet_last->next = packet;
|
||||
}else{
|
||||
mosq->out_packet = packet;
|
||||
}
|
||||
mosq->out_packet_last = packet;
|
||||
pthread_mutex_unlock(&mosq->out_packet_mutex);
|
||||
#ifdef WITH_BROKER
|
||||
# ifdef WITH_WEBSOCKETS
|
||||
if(mosq->wsi){
|
||||
libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}else{
|
||||
return packet__write(mosq);
|
||||
}
|
||||
# else
|
||||
return packet__write(mosq);
|
||||
# endif
|
||||
#else
|
||||
|
||||
/* Write a single byte to sockpairW (connected to sockpairR) to break out
|
||||
* of select() if in threaded mode. */
|
||||
if(mosq->sockpairW != INVALID_SOCKET){
|
||||
#ifndef WIN32
|
||||
if(write(mosq->sockpairW, &sockpair_data, 1)){
|
||||
}
|
||||
#else
|
||||
send(mosq->sockpairW, &sockpair_data, 1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
if(mosq->in_callback == false && mosq->threaded == mosq_ts_none){
|
||||
return packet__write(mosq);
|
||||
}else{
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length)
|
||||
{
|
||||
uint32_t len;
|
||||
|
||||
if(mosq->maximum_packet_size == 0) return MOSQ_ERR_SUCCESS;
|
||||
|
||||
len = remaining_length + packet__varint_bytes(remaining_length);
|
||||
if(len > mosq->maximum_packet_size){
|
||||
return MOSQ_ERR_OVERSIZE_PACKET;
|
||||
}else{
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int packet__write(struct mosquitto *mosq)
|
||||
{
|
||||
ssize_t write_length;
|
||||
struct mosquitto__packet *packet;
|
||||
int state;
|
||||
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
|
||||
|
||||
pthread_mutex_lock(&mosq->current_out_packet_mutex);
|
||||
pthread_mutex_lock(&mosq->out_packet_mutex);
|
||||
if(mosq->out_packet && !mosq->current_out_packet){
|
||||
mosq->current_out_packet = 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);
|
||||
|
||||
state = mosquitto__get_state(mosq);
|
||||
#if defined(WITH_TLS) && !defined(WITH_BROKER)
|
||||
if((state == mosq_cs_connect_pending) || mosq->want_connect){
|
||||
#else
|
||||
if(state == mosq_cs_connect_pending){
|
||||
#endif
|
||||
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
while(mosq->current_out_packet){
|
||||
packet = mosq->current_out_packet;
|
||||
|
||||
while(packet->to_process > 0){
|
||||
write_length = net__write(mosq, &(packet->payload[packet->pos]), packet->to_process);
|
||||
if(write_length > 0){
|
||||
G_BYTES_SENT_INC(write_length);
|
||||
packet->to_process -= write_length;
|
||||
packet->pos += write_length;
|
||||
}else{
|
||||
#ifdef WIN32
|
||||
errno = WSAGetLastError();
|
||||
#endif
|
||||
if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK
|
||||
#ifdef WIN32
|
||||
|| errno == WSAENOTCONN
|
||||
#endif
|
||||
){
|
||||
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}else{
|
||||
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
|
||||
switch(errno){
|
||||
case COMPAT_ECONNRESET:
|
||||
return MOSQ_ERR_CONN_LOST;
|
||||
default:
|
||||
return MOSQ_ERR_ERRNO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
G_MSGS_SENT_INC(1);
|
||||
if(((packet->command)&0xF6) == CMD_PUBLISH){
|
||||
G_PUB_MSGS_SENT_INC(1);
|
||||
#ifndef WITH_BROKER
|
||||
pthread_mutex_lock(&mosq->callback_mutex);
|
||||
if(mosq->on_publish){
|
||||
/* This is a QoS=0 message */
|
||||
mosq->in_callback = true;
|
||||
mosq->on_publish(mosq, mosq->userdata, packet->mid);
|
||||
mosq->in_callback = false;
|
||||
}
|
||||
if(mosq->on_publish_v5){
|
||||
/* This is a QoS=0 message */
|
||||
mosq->in_callback = true;
|
||||
mosq->on_publish_v5(mosq, mosq->userdata, packet->mid, 0, NULL);
|
||||
mosq->in_callback = false;
|
||||
}
|
||||
pthread_mutex_unlock(&mosq->callback_mutex);
|
||||
}else if(((packet->command)&0xF0) == CMD_DISCONNECT){
|
||||
do_client_disconnect(mosq, MOSQ_ERR_SUCCESS, NULL);
|
||||
packet__cleanup(packet);
|
||||
mosquitto__free(packet);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
packet__cleanup(packet);
|
||||
mosquitto__free(packet);
|
||||
|
||||
pthread_mutex_lock(&mosq->msgtime_mutex);
|
||||
mosq->next_msg_out = mosquitto_time() + mosq->keepalive;
|
||||
pthread_mutex_unlock(&mosq->msgtime_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
int packet__read(struct mosquitto_db *db, struct mosquitto *mosq)
|
||||
#else
|
||||
int packet__read(struct mosquitto *mosq)
|
||||
#endif
|
||||
{
|
||||
uint8_t byte;
|
||||
ssize_t read_length;
|
||||
int rc = 0;
|
||||
int state;
|
||||
|
||||
if(!mosq){
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
if(mosq->sock == INVALID_SOCKET){
|
||||
return MOSQ_ERR_NO_CONN;
|
||||
}
|
||||
|
||||
state = mosquitto__get_state(mosq);
|
||||
if(state == mosq_cs_connect_pending){
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* This gets called if pselect() indicates that there is network data
|
||||
* available - ie. at least one byte. What we do depends on what data we
|
||||
* already have.
|
||||
* If we've not got a command, attempt to read one and save it. This should
|
||||
* always work because it's only a single byte.
|
||||
* Then try to read the remaining length. This may fail because it is may
|
||||
* be more than one byte - will need to save data pending next read if it
|
||||
* does fail.
|
||||
* Then try to read the remaining payload, where 'payload' here means the
|
||||
* combined variable header and actual payload. This is the most likely to
|
||||
* fail due to longer length, so save current data and current position.
|
||||
* After all data is read, send to mosquitto__handle_packet() to deal with.
|
||||
* Finally, free the memory and reset everything to starting conditions.
|
||||
*/
|
||||
if(!mosq->in_packet.command){
|
||||
read_length = net__read(mosq, &byte, 1);
|
||||
if(read_length == 1){
|
||||
mosq->in_packet.command = byte;
|
||||
#ifdef WITH_BROKER
|
||||
G_BYTES_RECEIVED_INC(1);
|
||||
/* Clients must send CONNECT as their first command. */
|
||||
if(!(mosq->bridge) && mosq->state == mosq_cs_connected && (byte&0xF0) != CMD_CONNECT){
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}
|
||||
#endif
|
||||
}else{
|
||||
if(read_length == 0){
|
||||
return MOSQ_ERR_CONN_LOST; /* EOF */
|
||||
}
|
||||
#ifdef WIN32
|
||||
errno = WSAGetLastError();
|
||||
#endif
|
||||
if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}else{
|
||||
switch(errno){
|
||||
case COMPAT_ECONNRESET:
|
||||
return MOSQ_ERR_CONN_LOST;
|
||||
default:
|
||||
return MOSQ_ERR_ERRNO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* remaining_count is the number of bytes that the remaining_length
|
||||
* parameter occupied in this incoming packet. We don't use it here as such
|
||||
* (it is used when allocating an outgoing packet), but we must be able to
|
||||
* determine whether all of the remaining_length parameter has been read.
|
||||
* remaining_count has three states here:
|
||||
* 0 means that we haven't read any remaining_length bytes
|
||||
* <0 means we have read some remaining_length bytes but haven't finished
|
||||
* >0 means we have finished reading the remaining_length bytes.
|
||||
*/
|
||||
if(mosq->in_packet.remaining_count <= 0){
|
||||
do{
|
||||
read_length = net__read(mosq, &byte, 1);
|
||||
if(read_length == 1){
|
||||
mosq->in_packet.remaining_count--;
|
||||
/* Max 4 bytes length for remaining length as defined by protocol.
|
||||
* Anything more likely means a broken/malicious client.
|
||||
*/
|
||||
if(mosq->in_packet.remaining_count < -4){
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
G_BYTES_RECEIVED_INC(1);
|
||||
mosq->in_packet.remaining_length += (byte & 127) * mosq->in_packet.remaining_mult;
|
||||
mosq->in_packet.remaining_mult *= 128;
|
||||
}else{
|
||||
if(read_length == 0){
|
||||
return MOSQ_ERR_CONN_LOST; /* EOF */
|
||||
}
|
||||
#ifdef WIN32
|
||||
errno = WSAGetLastError();
|
||||
#endif
|
||||
if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}else{
|
||||
switch(errno){
|
||||
case COMPAT_ECONNRESET:
|
||||
return MOSQ_ERR_CONN_LOST;
|
||||
default:
|
||||
return MOSQ_ERR_ERRNO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}while((byte & 128) != 0);
|
||||
/* We have finished reading remaining_length, so make remaining_count
|
||||
* positive. */
|
||||
mosq->in_packet.remaining_count *= -1;
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
if(db->config->max_packet_size > 0 && mosq->in_packet.remaining_length+1 > db->config->max_packet_size){
|
||||
log__printf(NULL, MOSQ_LOG_INFO, "Client %s sent too large packet %d, disconnecting.", mosq->id, mosq->in_packet.remaining_length+1);
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
send__disconnect(mosq, MQTT_RC_PACKET_TOO_LARGE, NULL);
|
||||
}
|
||||
return MOSQ_ERR_OVERSIZE_PACKET;
|
||||
}
|
||||
#else
|
||||
// FIXME - client case for incoming message received from broker too large
|
||||
#endif
|
||||
if(mosq->in_packet.remaining_length > 0){
|
||||
mosq->in_packet.payload = mosquitto__malloc(mosq->in_packet.remaining_length*sizeof(uint8_t));
|
||||
if(!mosq->in_packet.payload){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
mosq->in_packet.to_process = mosq->in_packet.remaining_length;
|
||||
}
|
||||
}
|
||||
while(mosq->in_packet.to_process>0){
|
||||
read_length = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process);
|
||||
if(read_length > 0){
|
||||
G_BYTES_RECEIVED_INC(read_length);
|
||||
mosq->in_packet.to_process -= read_length;
|
||||
mosq->in_packet.pos += read_length;
|
||||
}else{
|
||||
#ifdef WIN32
|
||||
errno = WSAGetLastError();
|
||||
#endif
|
||||
if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
|
||||
if(mosq->in_packet.to_process > 1000){
|
||||
/* Update last_msg_in time if more than 1000 bytes left to
|
||||
* receive. Helps when receiving large messages.
|
||||
* This is an arbitrary limit, but with some consideration.
|
||||
* If a client can't send 1000 bytes in a second it
|
||||
* probably shouldn't be using a 1 second keep alive. */
|
||||
pthread_mutex_lock(&mosq->msgtime_mutex);
|
||||
mosq->last_msg_in = mosquitto_time();
|
||||
pthread_mutex_unlock(&mosq->msgtime_mutex);
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}else{
|
||||
switch(errno){
|
||||
case COMPAT_ECONNRESET:
|
||||
return MOSQ_ERR_CONN_LOST;
|
||||
default:
|
||||
return MOSQ_ERR_ERRNO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* All data for this packet is read. */
|
||||
mosq->in_packet.pos = 0;
|
||||
#ifdef WITH_BROKER
|
||||
G_MSGS_RECEIVED_INC(1);
|
||||
if(((mosq->in_packet.command)&0xF5) == CMD_PUBLISH){
|
||||
G_PUB_MSGS_RECEIVED_INC(1);
|
||||
}
|
||||
rc = handle__packet(db, mosq);
|
||||
#else
|
||||
rc = handle__packet(mosq);
|
||||
#endif
|
||||
|
||||
/* Free data and reset values */
|
||||
packet__cleanup(&mosq->in_packet);
|
||||
|
||||
pthread_mutex_lock(&mosq->msgtime_mutex);
|
||||
mosq->last_msg_in = mosquitto_time();
|
||||
pthread_mutex_unlock(&mosq->msgtime_mutex);
|
||||
return rc;
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 PACKET_MOSQ_H
|
||||
#define PACKET_MOSQ_H
|
||||
|
||||
#include "mosquitto_internal.h"
|
||||
#include "mosquitto.h"
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
struct mosquitto_db;
|
||||
#endif
|
||||
|
||||
int packet__alloc(struct mosquitto__packet *packet);
|
||||
void packet__cleanup(struct mosquitto__packet *packet);
|
||||
void packet__cleanup_all(struct mosquitto *mosq);
|
||||
int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet);
|
||||
|
||||
int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length);
|
||||
|
||||
int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte);
|
||||
int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count);
|
||||
int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, int *length);
|
||||
int packet__read_string(struct mosquitto__packet *packet, char **str, int *length);
|
||||
int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word);
|
||||
int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word);
|
||||
int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes);
|
||||
|
||||
void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte);
|
||||
void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count);
|
||||
void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length);
|
||||
void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word);
|
||||
void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word);
|
||||
int packet__write_varint(struct mosquitto__packet *packet, int32_t word);
|
||||
|
||||
int packet__varint_bytes(int32_t word);
|
||||
|
||||
int packet__write(struct mosquitto *mosq);
|
||||
#ifdef WITH_BROKER
|
||||
int packet__read(struct mosquitto_db *db, struct mosquitto *mosq);
|
||||
#else
|
||||
int packet__read(struct mosquitto *mosq);
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef PROPERTY_MOSQ_H
|
||||
#define PROPERTY_MOSQ_H
|
||||
|
||||
#include "mosquitto_internal.h"
|
||||
#include "mosquitto.h"
|
||||
|
||||
struct mqtt__string {
|
||||
char *v;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct mqtt5__property {
|
||||
struct mqtt5__property *next;
|
||||
union {
|
||||
uint8_t i8;
|
||||
uint16_t i16;
|
||||
uint32_t i32;
|
||||
uint32_t varint;
|
||||
struct mqtt__string bin;
|
||||
struct mqtt__string s;
|
||||
} value;
|
||||
struct mqtt__string name;
|
||||
int32_t identifier;
|
||||
bool client_generated;
|
||||
};
|
||||
|
||||
|
||||
int property__read_all(int command, struct mosquitto__packet *packet, mosquitto_property **property);
|
||||
int property__write_all(struct mosquitto__packet *packet, const mosquitto_property *property, bool write_len);
|
||||
void property__free(mosquitto_property **property);
|
||||
|
||||
int property__get_length(const mosquitto_property *property);
|
||||
int property__get_length_all(const mosquitto_property *property);
|
||||
|
||||
#endif
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
#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 "time_mosq.h"
|
||||
#include "util_mosq.h"
|
||||
|
||||
int handle__packet(struct mosquitto *mosq)
|
||||
{
|
||||
assert(mosq);
|
||||
|
||||
switch((mosq->in_packet.command)&0xF0){
|
||||
case CMD_PINGREQ:
|
||||
return handle__pingreq(mosq);
|
||||
case CMD_PINGRESP:
|
||||
return handle__pingresp(mosq);
|
||||
case CMD_PUBACK:
|
||||
return handle__pubackcomp(mosq, "PUBACK");
|
||||
case CMD_PUBCOMP:
|
||||
return handle__pubackcomp(mosq, "PUBCOMP");
|
||||
case CMD_PUBLISH:
|
||||
return handle__publish(mosq);
|
||||
case CMD_PUBREC:
|
||||
return handle__pubrec(NULL, mosq);
|
||||
case CMD_PUBREL:
|
||||
return handle__pubrel(NULL, mosq);
|
||||
case CMD_CONNACK:
|
||||
return handle__connack(mosq);
|
||||
case CMD_SUBACK:
|
||||
return handle__suback(mosq);
|
||||
case CMD_UNSUBACK:
|
||||
return handle__unsuback(mosq);
|
||||
case CMD_DISCONNECT:
|
||||
return handle__disconnect(mosq);
|
||||
case CMD_AUTH:
|
||||
return handle__auth(mosq);
|
||||
default:
|
||||
/* If we don't recognise the command, return an error straight away. */
|
||||
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unrecognised command %d\n", (mosq->in_packet.command)&0xF0);
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 READ_HANDLE_H
|
||||
#define READ_HANDLE_H
|
||||
|
||||
#include "mosquitto.h"
|
||||
struct mosquitto_db;
|
||||
|
||||
int handle__pingreq(struct mosquitto *mosq);
|
||||
int handle__pingresp(struct mosquitto *mosq);
|
||||
#ifdef WITH_BROKER
|
||||
int handle__pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const char *type);
|
||||
#else
|
||||
int handle__packet(struct mosquitto *mosq);
|
||||
int handle__connack(struct mosquitto *mosq);
|
||||
int handle__disconnect(struct mosquitto *mosq);
|
||||
int handle__pubackcomp(struct mosquitto *mosq, const char *type);
|
||||
int handle__publish(struct mosquitto *mosq);
|
||||
int handle__auth(struct mosquitto *mosq);
|
||||
#endif
|
||||
int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq);
|
||||
int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq);
|
||||
int handle__suback(struct mosquitto *mosq);
|
||||
int handle__unsuback(struct mosquitto *mosq);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,205 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
# include "mosquitto_broker_internal.h"
|
||||
#endif
|
||||
|
||||
#include "logging_mosq.h"
|
||||
#include "memory_mosq.h"
|
||||
#include "mosquitto.h"
|
||||
#include "mosquitto_internal.h"
|
||||
#include "mqtt_protocol.h"
|
||||
#include "packet_mosq.h"
|
||||
#include "property_mosq.h"
|
||||
|
||||
int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties)
|
||||
{
|
||||
struct mosquitto__packet *packet = NULL;
|
||||
int payloadlen;
|
||||
uint8_t will = 0;
|
||||
uint8_t byte;
|
||||
int rc;
|
||||
uint8_t version;
|
||||
char *clientid, *username, *password;
|
||||
int headerlen;
|
||||
int proplen = 0, will_proplen, varbytes;
|
||||
mosquitto_property *local_props = NULL;
|
||||
uint16_t receive_maximum;
|
||||
|
||||
assert(mosq);
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt31 && !mosq->id) return MOSQ_ERR_PROTOCOL;
|
||||
|
||||
#if defined(WITH_BROKER) && defined(WITH_BRIDGE)
|
||||
if(mosq->bridge){
|
||||
clientid = mosq->bridge->remote_clientid;
|
||||
username = mosq->bridge->remote_username;
|
||||
password = mosq->bridge->remote_password;
|
||||
}else{
|
||||
clientid = mosq->id;
|
||||
username = mosq->username;
|
||||
password = mosq->password;
|
||||
}
|
||||
#else
|
||||
clientid = mosq->id;
|
||||
username = mosq->username;
|
||||
password = mosq->password;
|
||||
#endif
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
/* Generate properties from options */
|
||||
if(!mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &receive_maximum, false)){
|
||||
rc = mosquitto_property_add_int16(&local_props, MQTT_PROP_RECEIVE_MAXIMUM, mosq->msgs_in.inflight_maximum);
|
||||
if(rc) return rc;
|
||||
}else{
|
||||
mosq->msgs_in.inflight_maximum = receive_maximum;
|
||||
mosq->msgs_in.inflight_quota = receive_maximum;
|
||||
}
|
||||
|
||||
version = MQTT_PROTOCOL_V5;
|
||||
headerlen = 10;
|
||||
proplen = 0;
|
||||
proplen += property__get_length_all(properties);
|
||||
proplen += property__get_length_all(local_props);
|
||||
varbytes = packet__varint_bytes(proplen);
|
||||
headerlen += proplen + varbytes;
|
||||
}else if(mosq->protocol == mosq_p_mqtt311){
|
||||
version = MQTT_PROTOCOL_V311;
|
||||
headerlen = 10;
|
||||
}else if(mosq->protocol == mosq_p_mqtt31){
|
||||
version = MQTT_PROTOCOL_V31;
|
||||
headerlen = 12;
|
||||
}else{
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
|
||||
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
|
||||
if(!packet) return MOSQ_ERR_NOMEM;
|
||||
|
||||
if(clientid){
|
||||
payloadlen = 2+strlen(clientid);
|
||||
}else{
|
||||
payloadlen = 2;
|
||||
}
|
||||
if(mosq->will){
|
||||
will = 1;
|
||||
assert(mosq->will->msg.topic);
|
||||
|
||||
payloadlen += 2+strlen(mosq->will->msg.topic) + 2+mosq->will->msg.payloadlen;
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
will_proplen = property__get_length_all(mosq->will->properties);
|
||||
varbytes = packet__varint_bytes(will_proplen);
|
||||
payloadlen += will_proplen + varbytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* After this check we can be sure that the username and password are
|
||||
* always valid for the current protocol, so there is no need to check
|
||||
* username before checking password. */
|
||||
if(mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311){
|
||||
if(password != NULL && username == NULL){
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if(username){
|
||||
payloadlen += 2+strlen(username);
|
||||
}
|
||||
if(password){
|
||||
payloadlen += 2+strlen(password);
|
||||
}
|
||||
|
||||
packet->command = CMD_CONNECT;
|
||||
packet->remaining_length = headerlen + payloadlen;
|
||||
rc = packet__alloc(packet);
|
||||
if(rc){
|
||||
mosquitto__free(packet);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Variable header */
|
||||
if(version == MQTT_PROTOCOL_V31){
|
||||
packet__write_string(packet, PROTOCOL_NAME_v31, strlen(PROTOCOL_NAME_v31));
|
||||
}else{
|
||||
packet__write_string(packet, PROTOCOL_NAME, strlen(PROTOCOL_NAME));
|
||||
}
|
||||
#if defined(WITH_BROKER) && defined(WITH_BRIDGE)
|
||||
if(mosq->bridge && mosq->bridge->try_private && mosq->bridge->try_private_accepted){
|
||||
version |= 0x80;
|
||||
}else{
|
||||
}
|
||||
#endif
|
||||
packet__write_byte(packet, version);
|
||||
byte = (clean_session&0x1)<<1;
|
||||
if(will){
|
||||
byte = byte | ((mosq->will->msg.retain&0x1)<<5) | ((mosq->will->msg.qos&0x3)<<3) | ((will&0x1)<<2);
|
||||
}
|
||||
if(username){
|
||||
byte = byte | 0x1<<7;
|
||||
}
|
||||
if(mosq->password){
|
||||
byte = byte | 0x1<<6;
|
||||
}
|
||||
packet__write_byte(packet, byte);
|
||||
packet__write_uint16(packet, keepalive);
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
/* Write properties */
|
||||
packet__write_varint(packet, proplen);
|
||||
property__write_all(packet, properties, false);
|
||||
property__write_all(packet, local_props, false);
|
||||
}
|
||||
mosquitto_property_free_all(&local_props);
|
||||
|
||||
/* Payload */
|
||||
if(clientid){
|
||||
packet__write_string(packet, clientid, strlen(clientid));
|
||||
}else{
|
||||
packet__write_uint16(packet, 0);
|
||||
}
|
||||
if(will){
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
/* Write will properties */
|
||||
property__write_all(packet, mosq->will->properties, true);
|
||||
}
|
||||
packet__write_string(packet, mosq->will->msg.topic, strlen(mosq->will->msg.topic));
|
||||
packet__write_string(packet, (const char *)mosq->will->msg.payload, mosq->will->msg.payloadlen);
|
||||
}
|
||||
|
||||
if(username){
|
||||
packet__write_string(packet, username, strlen(username));
|
||||
}
|
||||
if(password){
|
||||
packet__write_string(packet, password, strlen(password));
|
||||
}
|
||||
|
||||
mosq->keepalive = keepalive;
|
||||
#ifdef WITH_BROKER
|
||||
# ifdef WITH_BRIDGE
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending CONNECT", clientid);
|
||||
# endif
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending CONNECT", clientid);
|
||||
#endif
|
||||
return packet__queue(mosq, packet);
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
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 "send_mosq.h"
|
||||
|
||||
|
||||
int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties)
|
||||
{
|
||||
struct mosquitto__packet *packet = NULL;
|
||||
int rc;
|
||||
int proplen, varbytes;
|
||||
|
||||
assert(mosq);
|
||||
#ifdef WITH_BROKER
|
||||
# ifdef WITH_BRIDGE
|
||||
if(mosq->bridge){
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending DISCONNECT", mosq->id);
|
||||
}else
|
||||
# else
|
||||
{
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Sending DISCONNECT to %s (rc%d)", mosq->id, reason_code);
|
||||
}
|
||||
# endif
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending DISCONNECT", mosq->id);
|
||||
#endif
|
||||
assert(mosq);
|
||||
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
|
||||
if(!packet) return MOSQ_ERR_NOMEM;
|
||||
|
||||
packet->command = CMD_DISCONNECT;
|
||||
if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){
|
||||
packet->remaining_length = 1;
|
||||
if(properties){
|
||||
proplen = property__get_length_all(properties);
|
||||
varbytes = packet__varint_bytes(proplen);
|
||||
packet->remaining_length += proplen + varbytes;
|
||||
}
|
||||
}else{
|
||||
packet->remaining_length = 0;
|
||||
}
|
||||
|
||||
rc = packet__alloc(packet);
|
||||
if(rc){
|
||||
mosquitto__free(packet);
|
||||
return rc;
|
||||
}
|
||||
if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){
|
||||
packet__write_byte(packet, reason_code);
|
||||
if(properties){
|
||||
property__write_all(packet, properties, true);
|
||||
}
|
||||
}
|
||||
|
||||
return packet__queue(mosq, packet);
|
||||
}
|
||||
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
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"
|
||||
# include "sys_tree.h"
|
||||
#else
|
||||
# define G_PUB_BYTES_SENT_INC(A)
|
||||
#endif
|
||||
|
||||
#include "mosquitto.h"
|
||||
#include "mosquitto_internal.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 "time_mosq.h"
|
||||
#include "util_mosq.h"
|
||||
|
||||
int send__pingreq(struct mosquitto *mosq)
|
||||
{
|
||||
int rc;
|
||||
assert(mosq);
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGREQ to %s", mosq->id);
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGREQ", mosq->id);
|
||||
#endif
|
||||
rc = send__simple_command(mosq, CMD_PINGREQ);
|
||||
if(rc == MOSQ_ERR_SUCCESS){
|
||||
mosq->ping_t = mosquitto_time();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int send__pingresp(struct mosquitto *mosq)
|
||||
{
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGRESP to %s", mosq->id);
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGRESP", mosq->id);
|
||||
#endif
|
||||
return send__simple_command(mosq, CMD_PINGRESP);
|
||||
}
|
||||
|
||||
int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code)
|
||||
{
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBACK to %s (m%d, rc%d)", mosq->id, mid, reason_code);
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBACK (m%d, rc%d)", mosq->id, mid, reason_code);
|
||||
#endif
|
||||
util__increment_receive_quota(mosq);
|
||||
/* We don't use Reason String or User Property yet. */
|
||||
return send__command_with_mid(mosq, CMD_PUBACK, mid, false, reason_code, NULL);
|
||||
}
|
||||
|
||||
int send__pubcomp(struct mosquitto *mosq, uint16_t mid)
|
||||
{
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBCOMP to %s (m%d)", mosq->id, mid);
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBCOMP (m%d)", mosq->id, mid);
|
||||
#endif
|
||||
util__increment_receive_quota(mosq);
|
||||
/* We don't use Reason String or User Property yet. */
|
||||
return send__command_with_mid(mosq, CMD_PUBCOMP, mid, false, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code)
|
||||
{
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREC to %s (m%d, rc%d)", mosq->id, mid, reason_code);
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREC (m%d, rc%d)", mosq->id, mid, reason_code);
|
||||
#endif
|
||||
if(reason_code >= 0x80 && mosq->protocol == mosq_p_mqtt5){
|
||||
util__increment_receive_quota(mosq);
|
||||
}
|
||||
/* We don't use Reason String or User Property yet. */
|
||||
return send__command_with_mid(mosq, CMD_PUBREC, mid, false, reason_code, NULL);
|
||||
}
|
||||
|
||||
int send__pubrel(struct mosquitto *mosq, uint16_t mid)
|
||||
{
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREL to %s (m%d)", mosq->id, mid);
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREL (m%d)", mosq->id, mid);
|
||||
#endif
|
||||
/* We don't use Reason String or User Property yet. */
|
||||
return send__command_with_mid(mosq, CMD_PUBREL|2, mid, false, 0, NULL);
|
||||
}
|
||||
|
||||
/* For PUBACK, PUBCOMP, PUBREC, and PUBREL */
|
||||
int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties)
|
||||
{
|
||||
struct mosquitto__packet *packet = NULL;
|
||||
int rc;
|
||||
int proplen, varbytes;
|
||||
|
||||
assert(mosq);
|
||||
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
|
||||
if(!packet) return MOSQ_ERR_NOMEM;
|
||||
|
||||
packet->command = command;
|
||||
if(dup){
|
||||
packet->command |= 8;
|
||||
}
|
||||
packet->remaining_length = 2;
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
if(reason_code != 0 || properties){
|
||||
packet->remaining_length += 1;
|
||||
}
|
||||
|
||||
if(properties){
|
||||
proplen = property__get_length_all(properties);
|
||||
varbytes = packet__varint_bytes(proplen);
|
||||
packet->remaining_length += varbytes + proplen;
|
||||
}
|
||||
}
|
||||
|
||||
rc = packet__alloc(packet);
|
||||
if(rc){
|
||||
mosquitto__free(packet);
|
||||
return rc;
|
||||
}
|
||||
|
||||
packet__write_uint16(packet, mid);
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
if(reason_code != 0 || properties){
|
||||
packet__write_byte(packet, reason_code);
|
||||
}
|
||||
if(properties){
|
||||
property__write_all(packet, properties, true);
|
||||
}
|
||||
}
|
||||
|
||||
return packet__queue(mosq, packet);
|
||||
}
|
||||
|
||||
/* For DISCONNECT, PINGREQ and PINGRESP */
|
||||
int send__simple_command(struct mosquitto *mosq, uint8_t command)
|
||||
{
|
||||
struct mosquitto__packet *packet = NULL;
|
||||
int rc;
|
||||
|
||||
assert(mosq);
|
||||
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
|
||||
if(!packet) return MOSQ_ERR_NOMEM;
|
||||
|
||||
packet->command = command;
|
||||
packet->remaining_length = 0;
|
||||
|
||||
rc = packet__alloc(packet);
|
||||
if(rc){
|
||||
mosquitto__free(packet);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return packet__queue(mosq, packet);
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
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 SEND_MOSQ_H
|
||||
#define SEND_MOSQ_H
|
||||
|
||||
#include "mosquitto.h"
|
||||
#include "property_mosq.h"
|
||||
|
||||
int send__simple_command(struct mosquitto *mosq, uint8_t command);
|
||||
int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties);
|
||||
int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval);
|
||||
|
||||
int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties);
|
||||
int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties);
|
||||
int send__pingreq(struct mosquitto *mosq);
|
||||
int send__pingresp(struct mosquitto *mosq);
|
||||
int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code);
|
||||
int send__pubcomp(struct mosquitto *mosq, uint16_t mid);
|
||||
int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval);
|
||||
int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code);
|
||||
int send__pubrel(struct mosquitto *mosq, uint16_t mid);
|
||||
int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties);
|
||||
int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties);
|
||||
|
||||
#endif
|
@ -0,0 +1,215 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
# include "mosquitto_broker_internal.h"
|
||||
# include "sys_tree.h"
|
||||
#else
|
||||
# define G_PUB_BYTES_SENT_INC(A)
|
||||
#endif
|
||||
|
||||
#include "mosquitto.h"
|
||||
#include "mosquitto_internal.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"
|
||||
|
||||
|
||||
int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval)
|
||||
{
|
||||
#ifdef WITH_BROKER
|
||||
size_t len;
|
||||
#ifdef WITH_BRIDGE
|
||||
int i;
|
||||
struct mosquitto__bridge_topic *cur_topic;
|
||||
bool match;
|
||||
int rc;
|
||||
char *mapped_topic = NULL;
|
||||
char *topic_temp = NULL;
|
||||
#endif
|
||||
#endif
|
||||
assert(mosq);
|
||||
|
||||
#if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS)
|
||||
if(mosq->sock == INVALID_SOCKET && !mosq->wsi) return MOSQ_ERR_NO_CONN;
|
||||
#else
|
||||
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
if(mosq->listener && mosq->listener->mount_point){
|
||||
len = strlen(mosq->listener->mount_point);
|
||||
if(len < strlen(topic)){
|
||||
topic += len;
|
||||
}else{
|
||||
/* Invalid topic string. Should never happen, but silently swallow the message anyway. */
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
}
|
||||
#ifdef WITH_BRIDGE
|
||||
if(mosq->bridge && mosq->bridge->topics && mosq->bridge->topic_remapping){
|
||||
for(i=0; i<mosq->bridge->topic_count; i++){
|
||||
cur_topic = &mosq->bridge->topics[i];
|
||||
if((cur_topic->direction == bd_both || cur_topic->direction == bd_out)
|
||||
&& (cur_topic->remote_prefix || cur_topic->local_prefix)){
|
||||
/* Topic mapping required on this topic if the message matches */
|
||||
|
||||
rc = mosquitto_topic_matches_sub(cur_topic->local_topic, topic, &match);
|
||||
if(rc){
|
||||
return rc;
|
||||
}
|
||||
if(match){
|
||||
mapped_topic = mosquitto__strdup(topic);
|
||||
if(!mapped_topic) return MOSQ_ERR_NOMEM;
|
||||
if(cur_topic->local_prefix){
|
||||
/* This prefix needs removing. */
|
||||
if(!strncmp(cur_topic->local_prefix, mapped_topic, strlen(cur_topic->local_prefix))){
|
||||
topic_temp = mosquitto__strdup(mapped_topic+strlen(cur_topic->local_prefix));
|
||||
mosquitto__free(mapped_topic);
|
||||
if(!topic_temp){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
mapped_topic = topic_temp;
|
||||
}
|
||||
}
|
||||
|
||||
if(cur_topic->remote_prefix){
|
||||
/* This prefix needs adding. */
|
||||
len = strlen(mapped_topic) + strlen(cur_topic->remote_prefix)+1;
|
||||
topic_temp = mosquitto__malloc(len+1);
|
||||
if(!topic_temp){
|
||||
mosquitto__free(mapped_topic);
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
snprintf(topic_temp, len, "%s%s", cur_topic->remote_prefix, mapped_topic);
|
||||
topic_temp[len] = '\0';
|
||||
mosquitto__free(mapped_topic);
|
||||
mapped_topic = topic_temp;
|
||||
}
|
||||
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, mapped_topic, (long)payloadlen);
|
||||
G_PUB_BYTES_SENT_INC(payloadlen);
|
||||
rc = send__real_publish(mosq, mid, mapped_topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval);
|
||||
mosquitto__free(mapped_topic);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, topic, (long)payloadlen);
|
||||
G_PUB_BYTES_SENT_INC(payloadlen);
|
||||
#else
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, topic, (long)payloadlen);
|
||||
#endif
|
||||
|
||||
return send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval);
|
||||
}
|
||||
|
||||
|
||||
int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval)
|
||||
{
|
||||
struct mosquitto__packet *packet = NULL;
|
||||
int packetlen;
|
||||
int proplen = 0, varbytes;
|
||||
int rc;
|
||||
mosquitto_property expiry_prop;
|
||||
|
||||
assert(mosq);
|
||||
|
||||
if(topic){
|
||||
packetlen = 2+strlen(topic) + payloadlen;
|
||||
}else{
|
||||
packetlen = 2 + payloadlen;
|
||||
}
|
||||
if(qos > 0) packetlen += 2; /* For message id */
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
proplen = 0;
|
||||
proplen += property__get_length_all(cmsg_props);
|
||||
proplen += property__get_length_all(store_props);
|
||||
if(expiry_interval > 0){
|
||||
expiry_prop.next = NULL;
|
||||
expiry_prop.value.i32 = expiry_interval;
|
||||
expiry_prop.identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL;
|
||||
expiry_prop.client_generated = false;
|
||||
|
||||
proplen += property__get_length_all(&expiry_prop);
|
||||
}
|
||||
|
||||
varbytes = packet__varint_bytes(proplen);
|
||||
if(varbytes > 4){
|
||||
/* FIXME - Properties too big, don't publish any - should remove some first really */
|
||||
cmsg_props = NULL;
|
||||
store_props = NULL;
|
||||
expiry_interval = 0;
|
||||
}else{
|
||||
packetlen += proplen + varbytes;
|
||||
}
|
||||
}
|
||||
if(packet__check_oversize(mosq, packetlen)){
|
||||
#ifdef WITH_BROKER
|
||||
log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH for %s (%d bytes)", mosq->id, packetlen);
|
||||
#else
|
||||
log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH (%d bytes)", packetlen);
|
||||
#endif
|
||||
return MOSQ_ERR_OVERSIZE_PACKET;
|
||||
}
|
||||
|
||||
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
|
||||
if(!packet) return MOSQ_ERR_NOMEM;
|
||||
|
||||
packet->mid = mid;
|
||||
packet->command = CMD_PUBLISH | ((dup&0x1)<<3) | (qos<<1) | retain;
|
||||
packet->remaining_length = packetlen;
|
||||
rc = packet__alloc(packet);
|
||||
if(rc){
|
||||
mosquitto__free(packet);
|
||||
return rc;
|
||||
}
|
||||
/* Variable header (topic string) */
|
||||
if(topic){
|
||||
packet__write_string(packet, topic, strlen(topic));
|
||||
}else{
|
||||
packet__write_uint16(packet, 0);
|
||||
}
|
||||
if(qos > 0){
|
||||
packet__write_uint16(packet, mid);
|
||||
}
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
packet__write_varint(packet, proplen);
|
||||
property__write_all(packet, cmsg_props, false);
|
||||
property__write_all(packet, store_props, false);
|
||||
if(expiry_interval > 0){
|
||||
property__write_all(packet, &expiry_prop, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Payload */
|
||||
if(payloadlen){
|
||||
packet__write_bytes(packet, payload, payloadlen);
|
||||
}
|
||||
|
||||
return packet__queue(mosq, packet);
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
#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 send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const char **topic, int topic_qos, const mosquitto_property *properties)
|
||||
{
|
||||
struct mosquitto__packet *packet = NULL;
|
||||
uint32_t packetlen;
|
||||
uint16_t local_mid;
|
||||
int rc;
|
||||
int i;
|
||||
int proplen, varbytes;
|
||||
|
||||
assert(mosq);
|
||||
assert(topic);
|
||||
|
||||
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
|
||||
if(!packet) return MOSQ_ERR_NOMEM;
|
||||
|
||||
packetlen = 2;
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
proplen = property__get_length_all(properties);
|
||||
varbytes = packet__varint_bytes(proplen);
|
||||
packetlen += proplen + varbytes;
|
||||
}
|
||||
for(i=0; i<topic_count; i++){
|
||||
packetlen += 2+strlen(topic[i]) + 1;
|
||||
}
|
||||
|
||||
packet->command = CMD_SUBSCRIBE | (1<<1);
|
||||
packet->remaining_length = packetlen;
|
||||
rc = packet__alloc(packet);
|
||||
if(rc){
|
||||
mosquitto__free(packet);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Variable header */
|
||||
local_mid = mosquitto__mid_generate(mosq);
|
||||
if(mid) *mid = (int)local_mid;
|
||||
packet__write_uint16(packet, local_mid);
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
property__write_all(packet, properties, true);
|
||||
}
|
||||
|
||||
/* Payload */
|
||||
for(i=0; i<topic_count; i++){
|
||||
packet__write_string(packet, topic[i], strlen(topic[i]));
|
||||
packet__write_byte(packet, topic_qos);
|
||||
}
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
# ifdef WITH_BRIDGE
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", mosq->id, local_mid, topic[0], topic_qos&0x03, topic_qos&0xFC);
|
||||
# endif
|
||||
#else
|
||||
for(i=0; i<topic_count; i++){
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", mosq->id, local_mid, topic[i], topic_qos&0x03, topic_qos&0xFC);
|
||||
}
|
||||
#endif
|
||||
|
||||
return packet__queue(mosq, packet);
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
# include "mosquitto_broker_internal.h"
|
||||
#endif
|
||||
|
||||
#include "mosquitto.h"
|
||||
#include "logging_mosq.h"
|
||||
#include "memory_mosq.h"
|
||||
#include "mqtt_protocol.h"
|
||||
#include "packet_mosq.h"
|
||||
#include "property_mosq.h"
|
||||
#include "send_mosq.h"
|
||||
#include "util_mosq.h"
|
||||
|
||||
|
||||
int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties)
|
||||
{
|
||||
/* FIXME - only deals with a single topic */
|
||||
struct mosquitto__packet *packet = NULL;
|
||||
uint32_t packetlen;
|
||||
uint16_t local_mid;
|
||||
int rc;
|
||||
int proplen, varbytes;
|
||||
int i;
|
||||
|
||||
assert(mosq);
|
||||
assert(topic);
|
||||
|
||||
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
|
||||
if(!packet) return MOSQ_ERR_NOMEM;
|
||||
|
||||
packetlen = 2;
|
||||
|
||||
for(i=0; i<topic_count; i++){
|
||||
packetlen += 2+strlen(topic[i]);
|
||||
}
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
proplen = property__get_length_all(properties);
|
||||
varbytes = packet__varint_bytes(proplen);
|
||||
packetlen += proplen + varbytes;
|
||||
}
|
||||
|
||||
packet->command = CMD_UNSUBSCRIBE | (1<<1);
|
||||
packet->remaining_length = packetlen;
|
||||
rc = packet__alloc(packet);
|
||||
if(rc){
|
||||
mosquitto__free(packet);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Variable header */
|
||||
local_mid = mosquitto__mid_generate(mosq);
|
||||
if(mid) *mid = (int)local_mid;
|
||||
packet__write_uint16(packet, local_mid);
|
||||
|
||||
if(mosq->protocol == mosq_p_mqtt5){
|
||||
/* We don't use User Property yet. */
|
||||
property__write_all(packet, properties, true);
|
||||
}
|
||||
|
||||
/* Payload */
|
||||
for(i=0; i<topic_count; i++){
|
||||
packet__write_string(packet, topic[i], strlen(topic[i]));
|
||||
}
|
||||
|
||||
#ifdef WITH_BROKER
|
||||
# ifdef WITH_BRIDGE
|
||||
for(i=0; i<topic_count; i++){
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)", mosq->id, local_mid, topic[i]);
|
||||
}
|
||||
# endif
|
||||
#else
|
||||
for(i=0; i<topic_count; i++){
|
||||
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)", mosq->id, local_mid, topic[i]);
|
||||
}
|
||||
#endif
|
||||
return packet__queue(mosq, packet);
|
||||
}
|
||||
|