--- /dev/null
+[patterns]
+**.c = native
+**.h = native
+**.pl = native
+**.py = native
+**.txt = native
+**.svg = native
+**.png = BIN
+**.xml = native
+**.nsi = native
+Makefile = LF
+config.mk = LF
+mosquitto.conf = native
+
--- /dev/null
+syntax: glob
+
+*.o
+*.exe
+*.db
+c/*.test
+cpp/*.test
+*.pyc
+
+client/mosquitto_pub
+client/mosquitto_sub
+examples/mysql_log/mosquitto_mysql_log
+examples/temperature_conversion/mqtt_temperature_conversion
+man/mosquitto.8
+man/mosquitto-tls.7
+man/mosquitto.conf.5
+man/libmosquitto.3
+man/mosquitto_passwd.1
+man/mosquitto_pub.1
+man/mosquitto_sub.1
+man/mqtt.7
+src/db_dump/mosquitto_db_dump
+src/mosquitto
+src/mosquitto_passwd
+test/broker/broker.pid
+test/test_client
+test/fake_user
+test/msgsps_pub
+test/msgsps_sub
+test/msgsps_pub.dat
+test/msgsps_sub.dat
+test/broker/c/auth_plugin.so
+
+lib/cpp/libmosquittopp.so*
+lib/libmosquitto.so*
+lib/libmosquitto.a
+
+build/
+dist/
--- /dev/null
+============================================================================
+uthash.h license
+============================================================================
+
+Copyright (c) 2005-2010, Troy D. Hanson http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+============================================================================
+OpenSSL license
+============================================================================
+
+ LICENSE ISSUES
+ ==============
+
+ The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
+ the OpenSSL License and the original SSLeay license apply to the toolkit.
+ See below for the actual license texts. Actually both licenses are BSD-style
+ Open Source licenses. In case of any license issues related to OpenSSL
+ please contact openssl-core@openssl.org.
+
+ OpenSSL License
+ ---------------
+
+/* ====================================================================
+ * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+ Original SSLeay License
+ -----------------------
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+
+
+============================================================================
+pthreads-win32 license
+============================================================================
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
--- /dev/null
+Copyright (c) 2009-2012 Roger Light <roger@atchoo.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of mosquitto nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+
+
+
+uthash.h license:
+
+Copyright (c) 2005-2010, Troy D. Hanson http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
--- /dev/null
+include config.mk
+
+DIRS=lib client src
+DOCDIRS=man
+DISTDIRS=man
+
+.PHONY : all mosquitto docs binary clean reallyclean test install uninstall dist sign copy
+
+all : mosquitto docs
+
+docs :
+ for d in ${DOCDIRS}; do $(MAKE) -C $${d}; done
+
+binary : mosquitto
+
+mosquitto :
+ for d in ${DIRS}; do $(MAKE) -C $${d}; done
+
+clean :
+ for d in ${DIRS}; do $(MAKE) -C $${d} clean; done
+ for d in ${DOCDIRS}; do $(MAKE) -C $${d} clean; done
+ $(MAKE) -C test clean
+
+reallyclean :
+ for d in ${DIRS}; do $(MAKE) -C $${d} reallyclean; done
+ for d in ${DOCDIRS}; do $(MAKE) -C $${d} reallyclean; done
+ $(MAKE) -C test reallyclean
+ -rm -f *.orig
+
+test : mosquitto
+ $(MAKE) -C test test
+
+install : mosquitto
+ @for d in ${DIRS}; do $(MAKE) -C $${d} install; done
+ @for d in ${DOCDIRS}; do $(MAKE) -C $${d} install; done
+ $(INSTALL) -d ${DESTDIR}/etc/mosquitto
+ $(INSTALL) -m 644 mosquitto.conf ${DESTDIR}/etc/mosquitto/mosquitto.conf.example
+ $(INSTALL) -m 644 aclfile.example ${DESTDIR}/etc/mosquitto/aclfile.example
+ $(INSTALL) -m 644 pwfile.example ${DESTDIR}/etc/mosquitto/pwfile.example
+ $(INSTALL) -m 644 pskfile.example ${DESTDIR}/etc/mosquitto/pskfile.example
+
+uninstall :
+ @for d in ${DIRS}; do $(MAKE) -C $${d} uninstall; done
+ rm -f ${DESTDIR}/etc/mosquitto/mosquitto.conf
+ rm -f ${DESTDIR}/etc/mosquitto/aclfile.example
+ rm -f ${DESTDIR}/etc/mosquitto/pwfile.example
+ rm -f ${DESTDIR}/etc/mosquitto/pskfile.example
+
+dist : reallyclean
+ @for d in ${DISTDIRS}; do $(MAKE) -C $${d} dist; done
+
+ mkdir -p dist/mosquitto-${VERSION}
+ cp -r client examples installer lib logo man misc security service src test ChangeLog.txt CMakeLists.txt LICENSE.txt LICENSE-3rd-party.txt Makefile compiling.txt config.h config.mk readme.txt readme-windows.txt mosquitto.conf aclfile.example pskfile.example pwfile.example dist/mosquitto-${VERSION}/
+ cd dist; tar -zcf mosquitto-${VERSION}.tar.gz mosquitto-${VERSION}/
+ for m in man/*.xml; \
+ do \
+ hfile=$$(echo $${m} | sed -e 's#man/\(.*\)\.xml#\1#' | sed -e 's/\./-/g'); \
+ $(XSLTPROC) $(DB_HTML_XSL) $${m} > dist/$${hfile}.html; \
+ done
+
+
+sign : dist
+ cd dist; gpg --detach-sign -a mosquitto-${VERSION}.tar.gz
+
+copy : sign
+ cd dist; scp mosquitto-${VERSION}.tar.gz mosquitto-${VERSION}.tar.gz.asc mosquitto:site/mosquitto.org/files/source/
+ cd dist; scp *.html mosquitto:site/mosquitto.org/man/
+ scp ChangeLog.txt mosquitto:site/mosquitto.org/
+
--- /dev/null
+include ../../config.mk
+
+# Set DESTDIR if it isn't given
+DESTDIR?=/
+
+.PHONY : all clean install
+
+all : mosquitto.pyc
+
+install : all
+ python ./setup.py install --prefix=${prefix} --root=${DESTDIR}
+
+mosquitto.pyc : mosquitto.py
+ python ./setup.py build
+
+clean :
+ -rm -rf build mosquitto.pyc __pycache__
--- /dev/null
+# Copyright (c) 2012 Roger Light <roger@atchoo.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of mosquitto nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+"""
+This is an MQTT v3.1 client module. MQTT is a lightweight pub/sub messaging
+protocol that is easy to implement and suitable for low powered devices.
+"""
+import errno
+import random
+import select
+import socket
+import ssl
+import struct
+import sys
+import threading
+import time
+
+if sys.version_info[0] < 3:
+ PROTOCOL_NAME = "MQIsdp"
+else:
+ PROTOCOL_NAME = b"MQIsdp"
+
+PROTOCOL_VERSION = 3
+
+# Message types
+CONNECT = 0x10
+CONNACK = 0x20
+PUBLISH = 0x30
+PUBACK = 0x40
+PUBREC = 0x50
+PUBREL = 0x60
+PUBCOMP = 0x70
+SUBSCRIBE = 0x80
+SUBACK = 0x90
+UNSUBSCRIBE = 0xA0
+UNSUBACK = 0xB0
+PINGREQ = 0xC0
+PINGRESP = 0xD0
+DISCONNECT = 0xE0
+
+# Log levels
+MOSQ_LOG_INFO = 0x01
+MOSQ_LOG_NOTICE = 0x02
+MOSQ_LOG_WARNING = 0x04
+MOSQ_LOG_ERR = 0x08
+MOSQ_LOG_DEBUG = 0x10
+
+# 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
+
+# Connection state
+mosq_cs_new = 0
+mosq_cs_connected = 1
+mosq_cs_disconnecting = 2
+mosq_cs_connect_async = 3
+
+# Message direction
+mosq_md_invalid = 0
+mosq_md_in = 1
+mosq_md_out = 2
+
+# Message state
+mosq_ms_invalid = 0,
+mosq_ms_wait_puback = 1
+mosq_ms_wait_pubrec = 2
+mosq_ms_wait_pubrel = 3
+mosq_ms_wait_pubcomp = 4
+
+# Error values
+MOSQ_ERR_AGAIN = -1
+MOSQ_ERR_SUCCESS = 0
+MOSQ_ERR_NOMEM = 1
+MOSQ_ERR_PROTOCOL = 2
+MOSQ_ERR_INVAL = 3
+MOSQ_ERR_NO_CONN = 4
+MOSQ_ERR_CONN_REFUSED = 5
+MOSQ_ERR_NOT_FOUND = 6
+MOSQ_ERR_CONN_LOST = 7
+MOSQ_ERR_TLS = 8
+MOSQ_ERR_PAYLOAD_SIZE = 9
+MOSQ_ERR_NOT_SUPPORTED = 10
+MOSQ_ERR_AUTH = 11
+MOSQ_ERR_ACL_DENIED = 12
+MOSQ_ERR_UNKNOWN = 13
+MOSQ_ERR_ERRNO = 14
+
+def _fix_sub_topic(subtopic):
+ # Convert ////some////over/slashed///topic/etc/etc//
+ # into some/over/slashed/topic/etc/etc
+ if subtopic[0] == '/':
+ return '/'+'/'.join(filter(None, subtopic.split('/')))
+ else:
+ return '/'.join(filter(None, subtopic.split('/')))
+
+def error_string(mosq_errno):
+ """Return the error string associated with a mosquitto error number."""
+ if mosq_errno == MOSQ_ERR_SUCCESS:
+ return "No error."
+ elif mosq_errno == MOSQ_ERR_NOMEM:
+ return "Out of memory."
+ elif mosq_errno == MOSQ_ERR_PROTOCOL:
+ return "A network protocol error occurred when communicating with the broker."
+ elif mosq_errno == MOSQ_ERR_INVAL:
+ return "Invalid function arguments provided."
+ elif mosq_errno == MOSQ_ERR_NO_CONN:
+ return "The client is not currently connected."
+ elif mosq_errno == MOSQ_ERR_CONN_REFUSED:
+ return "The connection was refused."
+ elif mosq_errno == MOSQ_ERR_NOT_FOUND:
+ return "Message not found (internal error)."
+ elif mosq_errno == MOSQ_ERR_CONN_LOST:
+ return "The connection was lost."
+ elif mosq_errno == MOSQ_ERR_TLS:
+ return "A TLS error occurred."
+ elif mosq_errno == MOSQ_ERR_PAYLOAD_SIZE:
+ return "Payload too large."
+ elif mosq_errno == MOSQ_ERR_NOT_SUPPORTED:
+ return "This feature is not supported."
+ elif mosq_errno == MOSQ_ERR_AUTH:
+ return "Authorisation failed."
+ elif mosq_errno == MOSQ_ERR_ACL_DENIED:
+ return "Access denied by ACL."
+ elif mosq_errno == MOSQ_ERR_UNKNOWN:
+ return "Unknown error."
+ elif mosq_errno == MOSQ_ERR_ERRNO:
+ return "Error defined by errno."
+ else:
+ return "Unknown error."
+
+def connack_string(connack_code):
+ """Return the string associated with a CONNACK result."""
+ if connack_code == 0:
+ return "Connection Accepted."
+ elif connack_code == 1:
+ return "Connection Refused: unacceptable protocol version."
+ elif connack_code == 2:
+ return "Connection Refused: identifier rejected."
+ elif connack_code == 3:
+ return "Connection Refused: broker unavailable."
+ elif connack_code == 4:
+ return "Connection Refused: bad user name or password."
+ elif connack_code == 5:
+ return "Connection Refused: not authorised."
+ else:
+ return "Connection Refused: unknown reason."
+
+def topic_matches_sub(sub, topic):
+ """Check whether a topic matches a subscription.
+
+ For example:
+
+ foo/bar would match the subscription foo/# or +/bar
+ non/matching would not match the subscription non/+/+
+ """
+ result = True
+ local_sub = _fix_sub_topic(sub)
+ local_topic = _fix_sub_topic(topic)
+ multilevel_wildcard = False
+
+ slen = len(local_sub)
+ tlen = len(local_topic)
+
+ spos = 0;
+ tpos = 0;
+
+ while spos < slen and tpos < tlen:
+ if local_sub[spos] == local_topic[tpos]:
+ spos += 1
+ tpos += 1
+ else:
+ if local_sub[spos] == '+':
+ spos += 1
+ while tpos < tlen and local_topic[tpos] != '/':
+ tpos += 1
+ if tpos == tlen and spos == slen:
+ result = True
+ break
+
+ elif local_sub[spos] == '#':
+ multilevel_wildcard = True
+ if spos+1 != slen:
+ result = False
+ break
+ else:
+ result = True
+ break
+
+ else:
+ result = False
+ break
+
+ if tpos == tlen-1:
+ # Check for e.g. foo matching foo/#
+ if spos == slen-3 and local_sub[spos+1] == '/' and local_sub[spos+2] == '#':
+ result = True
+ multilevel_wildcard = True
+ break
+
+ if multilevel_wildcard == False and (tpos < tlen or spos < slen):
+ result = False
+
+ return result
+
+
+class MosquittoMessage:
+ """ This is a class that describes an incoming message. It is passed to the
+ on_message callback as the message parameter.
+
+ Members:
+
+ topic : String. topic that the message was published on.
+ payload : String/bytes the message payload.
+ qos : Integer. The message Quality of Service 0, 1 or 2.
+ retain : Boolean. If true, the message is a retained message and not fresh.
+ mid : Integer. The message id.
+ """
+ def __init__(self):
+ self.timestamp = 0
+ self.direction = mosq_md_invalid
+ self.state = mosq_ms_invalid
+ self.dup = False
+ self.mid = 0
+ self.topic = ""
+ self.payload = None
+ self.qos = 0
+ self.retain = False
+
+class MosquittoInPacket:
+ """Internal datatype."""
+ def __init__(self):
+ self.command = 0
+ self.have_remaining = 0
+ self.remaining_count = []
+ self.remaining_mult = 1
+ self.remaining_length = 0
+ self.packet = b""
+ self.to_process = 0
+ self.pos = 0
+
+ def cleanup(self):
+ self.__init__()
+
+class MosquittoPacket:
+ """Internal datatype."""
+ def __init__(self, command, packet, mid, qos):
+ self.command = command
+ self.mid = mid
+ self.qos = qos
+ self.pos = 0
+ self.to_process = len(packet)
+ self.packet = packet
+
+class Mosquitto:
+ """MQTT version 3.1 client class.
+
+ This is the main class for use communicating with an MQTT broker.
+
+ General usage flow:
+
+ * Use connect()/connect_async() to connect to a broker
+ * Call loop() frequently to maintain network traffic flow with the broker
+ * Or use loop_start() to set a thread running to call loop() for you.
+ * Or use loop_forever() to handle calling loop() for you in a blocking
+ * function.
+ * Use subscribe() to subscribe to a topic and receive messages
+ * Use publish() to send messages
+ * Use disconnect() to disconnect from the broker
+
+ Data returned from the broker is made available with the use of callback
+ functions as described below.
+
+ Callbacks
+ =========
+
+ A number of callback functions are available to receive data back from the
+ broker. To use a callback, define a function and then assign it to the
+ client:
+
+ def on_connect(mosq, userdata, rc):
+ print("Connection returned " + str(rc))
+
+ client.on_connect = on_connect
+
+ All of the callbacks as described below have a "mosq" and an "userdata"
+ argument. "mosq" is the Mosquitto instance that is calling the callback.
+ "userdata" is user data of any type and can be set when creating a new client
+ instance or with user_data_set(userdata).
+
+ The callbacks:
+
+ on_connect(mosq, userdata, rc): called when the broker responds to our connection
+ request. The value of rc determines success or not:
+ 0: Connection successful
+ 1: Connection refused - incorrect protocol version
+ 2: Connection refused - invalid client identifier
+ 3: Connection refused - server unavailable
+ 4: Connection refused - bad username or password
+ 5: Connection refused - not authorised
+ 6-255: Currently unused.
+
+ on_disconnect(mosq, userdata, rc): called when the client disconnects from the broker.
+ The rc parameter indicates the disconnection state. If MOSQ_ERR_SUCCESS
+ (0), the callback was called in response to a disconnect() call. If any
+ other value the disconnection was unexpected, such as might be caused by
+ a network error.
+
+ on_message(mosq, userdata, message): called when a message has been received on a
+ topic that the client subscribes to. The message variable is a
+ MosquittoMessage that describes all of the message parameters.
+
+ on_publish(mosq, userdata, mid): called when a message that was to be sent using the
+ publish() call has completed transmission to the broker. For messages
+ with QoS levels 1 and 2, this means that the appropriate handshakes have
+ completed. For QoS 0, this simply means that the message has left the
+ client. The mid variable matches the mid variable returned from the
+ corresponding publish() call, to allow outgoing messages to be tracked.
+ This callback is important because even if the publish() call returns
+ success, it does not always mean that the message has been sent.
+
+ on_subscribe(mosq, userdata, mid, granted_qos): called when the broker responds to a
+ subscribe request. The mid variable matches the mid variable returned
+ from the corresponding subscribe() call. The granted_qos variable is a
+ list of integers that give the QoS level the broker has granted for each
+ of the different subscription requests.
+
+ on_unsubscribe(mosq, userdata, mid): called when the broker responds to an unsubscribe
+ request. The mid variable matches the mid variable returned from the
+ corresponding unsubscribe() call.
+
+ on_log(mosq, userdata, level, buf): called when the client has log information. Define
+ to allow debugging. The level variable gives the severity of the message
+ and will be one of MOSQ_LOG_INFO, MOSQ_LOG_NOTICE, MOSQ_LOG_WARNING,
+ MOSQ_LOG_ERR, and MOSQ_LOG_DEBUG. The message itself is in buf.
+
+ """
+ def __init__(self, client_id="", clean_session=True, userdata=None):
+ """client_id is the unique client id string used when connecting to the
+ broker. If client_id is zero length or None, then one will be randomly
+ generated. In this case, clean_session must be True. If this is not the
+ case a ValueError will be raised.
+
+ clean_session is a boolean that determines the client type. If True,
+ the broker will remove all information about this client when it
+ disconnects. If False, the client is a persistent client and
+ subscription information and queued messages will be retained when the
+ client disconnects.
+ Note that a client will never discard its own outgoing messages on
+ disconnect. Calling connect() or reconnect() will cause the messages to
+ be resent. Use reinitialise() to reset a client to its original state.
+
+ userdata is user defined data of any type that is passed as the "userdata"
+ parameter to callbacks. It may be updated at a later point with the
+ user_data_set() function.
+ """
+ if clean_session == False and (client_id == "" or client_id == None):
+ raise ValueError('A client id must be provided if clean session is False.')
+
+ self._userdata = userdata
+ self._sock = None
+ self._keepalive = 60
+ self._message_retry = 20
+ self._last_retry_check = 0
+ self._clean_session = clean_session
+ if client_id == "":
+ self._client_id = "mosq/" + "".join(random.choice("0123456789ADCDEF") for x in range(23-5))
+ else:
+ self._client_id = client_id
+
+ self._username = ""
+ self._password = ""
+ self._in_packet = MosquittoInPacket()
+ self._out_packet = []
+ self._current_out_packet = None
+ self._last_msg_in = time.time()
+ self._last_msg_out = time.time()
+ self._ping_t = 0
+ self._last_mid = 0
+ self._state = mosq_cs_new
+ self._messages = []
+ self._will = False
+ self._will_topic = ""
+ self._will_payload = None
+ self._will_qos = 0
+ self._will_retain = False
+ self.on_disconnect = None
+ self.on_connect = None
+ self.on_publish = None
+ self.on_message = None
+ self.on_subscribe = None
+ self.on_unsubscribe = None
+ self.on_log = None
+ self._host = ""
+ self._port = 1883
+ self._in_callback = False
+ self._strict_protocol = False
+ self._callback_mutex = threading.Lock()
+ self._state_mutex = threading.Lock()
+ self._out_packet_mutex = threading.Lock()
+ self._current_out_packet_mutex = threading.Lock()
+ self._msgtime_mutex = threading.Lock()
+ self._thread = None
+ self._thread_terminate = False
+ self._ssl = None
+ self._tls_certfile = None
+ self._tls_keyfile = None
+ self._tls_ca_certs = None
+ self._tls_cert_reqs = None
+ self._tls_ciphers = None
+
+ def __del__(self):
+ pass
+
+ def reinitialise(self, client_id="", clean_session=True, userdata=None):
+ if self._ssl:
+ self._ssl.close()
+ self._ssl = None
+ self._sock = None
+ elif self._sock:
+ self._sock.close()
+ self._sock = None
+ self.__init__(client_id, clean_session, userdata)
+
+ def tls_set(self, ca_certs, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1, ciphers=None):
+ """Configure network encryption and authentication options. Enables SSL/TLS support.
+
+ ca_certs : a string path to the Certificate Authority certificate files
+ that are to be treated as trusted by this client. If this is the only
+ option given then the client will operate in a similar manner to a web
+ browser. That is to say it will require the broker to have a
+ certificate signed by the Certificate Authorities in ca_certs and will
+ communicate using TLS v1, but will not attempt any form of
+ authentication. This provides basic network encryption but may not be
+ sufficient depending on how the broker is configured.
+
+ certfile and keyfile are strings pointing to the PEM encoded client
+ certificate and private keys respectively. If these arguments are not
+ None then they will be used as client information for TLS based
+ authentication. Support for this feature is broker dependent. Note
+ that if either of these files in encrypted and needs a password to
+ decrypt it, Python will ask for the password at the command line. It is
+ not currently possible to define a callback to provide the password.
+
+ cert_reqs allows the certificate requirements that the client imposes
+ on the broker to be changed. By default this is ssl.CERT_REQUIRED,
+ which means that the broker must provide a certificate. See the ssl
+ pydoc for more information on this parameter.
+
+ tls_version allows the version of the SSL/TLS protocol used to be
+ specified. By default TLS v1 is used. Previous versions (all versions
+ beginning with SSL) are possible but not recommended due to possible
+ security problems.
+
+ ciphers is a string specifying which encryption ciphers are allowable
+ for this connection, or None to use the defaults. See the ssl pydoc for
+ more information.
+
+ Must be called before connect() or connect_async()."""
+ if sys.version < '2.7':
+ raise ValueError('Python 2.7 is the minimum supported version for TLS.')
+
+ if ca_certs == None:
+ raise ValueError('ca_certs must not be None.')
+
+ try:
+ f = open(ca_certs, "r")
+ except IOError as err:
+ raise IOError(ca_certs+": "+err.strerror)
+ else:
+ f.close()
+ if certfile != None:
+ try:
+ f = open(certfile, "r")
+ except IOError as err:
+ raise IOError(certfile+": "+err.strerror)
+ else:
+ f.close()
+ if keyfile != None:
+ try:
+ f = open(keyfile, "r")
+ except IOError as err:
+ raise IOError(keyfile+": "+err.strerror)
+ else:
+ f.close()
+
+ self._tls_ca_certs = ca_certs
+ self._tls_certfile = certfile
+ self._tls_keyfile = keyfile
+ self._tls_cert_reqs = cert_reqs
+ self._tls_version = tls_version
+ self._tls_ciphers = ciphers
+
+ def connect(self, host, port=1883, keepalive=60):
+ """Connect to a remote broker.
+
+ host is the hostname or IP address of the remote broker.
+ port is the network port of the server host to connect to. Defaults to
+ 1883. Note that the default port for MQTT over SSL/TLS is 8883 so if you
+ are using tls_set() the port may need providing.
+ keepalive: Maximum period in seconds between communications with the
+ broker. If no other messages are being exchanged, this controls the
+ rate at which the client will send ping messages to the broker.
+ """
+ self.connect_async(host, port, keepalive)
+ return self.reconnect()
+
+ def connect_async(self, host, port=1883, keepalive=60):
+ """Connect to a remote broker asynchronously. This is a non-blocking
+ connect call that can be used with loop_start() to provide very quick
+ start.
+
+ host is the hostname or IP address of the remote broker.
+ port is the network port of the server host to connect to. Defaults to
+ 1883. Note that the default port for MQTT over SSL/TLS is 8883 so if you
+ are using tls_set() the port may need providing.
+ keepalive: Maximum period in seconds between communications with the
+ broker. If no other messages are being exchanged, this controls the
+ rate at which the client will send ping messages to the broker.
+ """
+ if host == None or len(host) == 0:
+ raise ValueError('Invalid host.')
+ if port <= 0:
+ raise ValueError('Invalid port number.')
+ if keepalive < 0:
+ raise ValueError('Keepalive must be >=0.')
+
+ self._host = host
+ self._port = port
+ self._keepalive = keepalive
+
+ self._state_mutex.acquire()
+ self._state = mosq_cs_connect_async
+ self._state_mutex.release()
+
+ def reconnect(self):
+ """Reconnect the client after a disconnect. Can only be called after
+ connect()/connect_async()."""
+ if len(self._host) == 0:
+ raise ValueError('Invalid host.')
+ if self._port <= 0:
+ raise ValueError('Invalid port number.')
+
+ self._in_packet.cleanup()
+ self._out_packet_mutex.acquire()
+ self._out_packet = []
+ self._out_packet_mutex.release()
+
+ self._current_out_packet_mutex.acquire()
+ self._current_out_packet = None
+ self._current_out_packet_mutex.release()
+
+ self._msgtime_mutex.acquire()
+ self._last_msg_in = time.time()
+ self._last_msg_out = time.time()
+ self._msgtime_mutex.release()
+
+ self._ping_t = 0
+ self._state_mutex.acquire()
+ self._state = mosq_cs_new
+ self._state_mutex.release()
+ if self._ssl:
+ self._ssl.close()
+ self._ssl = None
+ self._sock = None
+ elif self._sock:
+ self._sock.close()
+ self._sock = None
+
+ # Put messages in progress in a valid state.
+ self._messages_reconnect_reset()
+
+ try:
+ self._sock = socket.create_connection((self._host, self._port))
+ except socket.error as err:
+ (msg) = err
+ if msg.errno != errno.EINPROGRESS:
+ raise
+
+ if self._tls_ca_certs != None:
+ self._ssl = ssl.wrap_socket(self._sock,
+ certfile=self._tls_certfile,
+ keyfile=self._tls_keyfile,
+ ca_certs=self._tls_ca_certs,
+ cert_reqs=self._tls_cert_reqs,
+ ssl_version=self._tls_version,
+ ciphers=self._tls_ciphers)
+
+ self._sock.setblocking(0)
+
+ return self._send_connect(self._keepalive, self._clean_session)
+
+ def loop(self, timeout=1.0, max_packets=1):
+ """Process network events.
+
+ This function must be called regularly to ensure communication with the
+ broker is carried out. It calls select() on the network socket to wait
+ for network events. If incoming data is present it will then be
+ processed. Outgoing commands, from e.g. publish(), are normally sent
+ immediately that their function is called, but this is not always
+ possible. loop() will also attempt to send any remaining outgoing
+ messages, which also includes commands that are part of the flow for
+ messages with QoS>0.
+
+ timeout: The time in seconds to wait for incoming/outgoing network
+ traffic before timing out and returning.
+ max_packets: Not currently used.
+
+ Returns MOSQ_ERR_SUCCESS on success.
+ Returns >0 on error.
+
+ A ValueError will be raised if timeout < 0"""
+ if timeout < 0.0:
+ raise ValueError('Invalid timeout.')
+
+ self._current_out_packet_mutex.acquire()
+ self._out_packet_mutex.acquire()
+ if self._current_out_packet == None and len(self._out_packet) > 0:
+ self._current_out_packet = self._out_packet.pop(0)
+
+ if self._current_out_packet:
+ wlist = [self.socket()]
+ else:
+ wlist = []
+ self._out_packet_mutex.release()
+ self._current_out_packet_mutex.release()
+
+ rlist = [self.socket()]
+ try:
+ socklist = select.select(rlist, wlist, [], timeout)
+ except TypeError:
+ # Socket isn't correct type, in likelihood connection is lost
+ return MOSQ_ERR_CONN_LOST
+
+ if self.socket() in socklist[0]:
+ rc = self.loop_read(max_packets)
+ if rc or (self._ssl == None and self._sock == None):
+ return rc
+
+ if self.socket() in socklist[1]:
+ rc = self.loop_write(max_packets)
+ if rc or (self._ssl == None and self._sock == None):
+ return rc
+
+ return self.loop_misc()
+
+ def publish(self, topic, payload=None, qos=0, retain=False):
+ """Publish a message on a topic.
+
+ This causes a message to be sent to the broker and subsequently from
+ the broker to any clients subscribing to matching topics.
+
+ topic: The topic that the message should be published on.
+ payload: The actual message to send. If not given, or set to None a
+ zero length message will be used. Passing an int or float will result
+ in the payload being converted to a string representing that number. If
+ you wish to send a true int/float, use struct.pack() to create the
+ payload you require.
+ qos: The quality of service level to use.
+ retain: If set to true, the message will be set as the "last known
+ good"/retained message for the topic.
+
+ Returns a tuple (result, mid), where result is MOSQ_ERR_SUCCESS to
+ indicate success or MOSQ_ERR_NO_CONN if the client is not currently
+ connected. mid is the message ID for the publish request. The mid
+ value can be used to track the publish request by checking against the
+ mid argument in the on_publish() callback if it is defined.
+
+ A ValueError will be raised if topic == None, has zero length or is
+ invalid (contains a wildcard), if qos is not one of 0, 1 or 2, or if
+ the length of the payload is greater than 268435455 bytes."""
+ if topic == None or len(topic) == 0:
+ raise ValueError('Invalid topic.')
+ if qos<0 or qos>2:
+ raise ValueError('Invalid QoS level.')
+ if isinstance(payload, str) == True or isinstance(payload, bytearray) == True:
+ local_payload = payload
+ elif isinstance(payload, int) == True or isinstance(payload, float) == True:
+ local_payload = str(payload)
+ elif payload == None:
+ local_payload = None
+ else:
+ raise TypeError('payload must be a string, bytearray, int, float or None.')
+
+ if local_payload != None and len(local_payload) > 268435455:
+ raise ValueError('Payload too large.')
+
+ if self._topic_wildcard_len_check(topic) != MOSQ_ERR_SUCCESS:
+ raise ValueError('Publish topic cannot contain wildcards.')
+
+ local_mid = self._mid_generate()
+
+ if qos == 0:
+ rc = self._send_publish(local_mid, topic, local_payload, qos, retain, False)
+ return (rc, local_mid)
+ else:
+ message = MosquittoMessage()
+ message.timestamp = time.time()
+ message.direction = mosq_md_out
+ if qos == 1:
+ message.state = mosq_ms_wait_puback
+ elif qos == 2:
+ message.state = mosq_ms_wait_pubrec
+
+ message.mid = local_mid
+ message.topic = topic
+ if local_payload == None or len(local_payload) == 0:
+ message.payload = None
+ else:
+ message.payload = local_payload
+
+ message.qos = qos
+ message.retain = retain
+ message.dup = False
+
+ self._messages.append(message)
+ rc = self._send_publish(message.mid, message.topic, message.payload, message.qos, message.retain, message.dup)
+ return (rc, local_mid)
+
+ def username_pw_set(self, username, password=None):
+ """Set a username and optionally a password for broker authentication.
+
+ Must be called before connect() to have any effect.
+ Requires a broker that supports MQTT v3.1.
+
+ username: The username to authenticate with. Need have no relationship to the client id.
+ password: The password to authenticate with. Optional, set to None if not required.
+ """
+ self._username = username
+ self._password = password
+
+ def disconnect(self):
+ """Disconnect a connected client from the broker."""
+ if self._sock == None and self._ssl == None:
+ return MOSQ_ERR_NO_CONN
+
+ self._state_mutex.acquire()
+ self._state = mosq_cs_disconnecting
+ self._state_mutex.release()
+
+ return self._send_disconnect()
+
+ def subscribe(self, topic, qos=0):
+ """Subscribe the client to a topic.
+
+ sub: The subscription topic to subscribe to.
+ qos: The desired quality of service level for the subscription.
+
+ Returns a tuple (result, mid), where result is MOSQ_ERR_SUCCESS
+ to indicate success or MOSQ_ERR_NO_CONN if the client is not currently connected.
+ mid is the message ID for the subscribe request. The mid value can be
+ used to track the subscribe request by checking against the mid
+ argument in the on_subscribe() callback if it is defined.
+
+ Raises a ValueError if qos is not 0, 1 or 2, or if topic is None or has
+ zero string length.
+ """
+ if qos<0 or qos>2:
+ raise ValueError('Invalid QoS level.')
+ if topic == None or len(topic) == 0:
+ raise ValueError('Invalid topic.')
+ topic = _fix_sub_topic(topic)
+
+ if self._sock == None and self._ssl == None:
+ return MOSQ_ERR_NO_CONN
+
+ return self._send_subscribe(False, topic, qos)
+
+ def unsubscribe(self, topic):
+ """Unsubscribe the client from a topic.
+
+ sub: The subscription topic to unsubscribe from.
+
+ Returns a tuple (result, mid), where result is MOSQ_ERR_SUCCESS
+ to indicate success or MOSQ_ERR_NO_CONN if the client is not currently connected.
+ mid is the message ID for the unsubscribe request. The mid value can be
+ used to track the unsubscribe request by checking against the mid
+ argument in the on_unsubscribe() callback if it is defined.
+
+ Raises a ValueError if topic is None or has zero string length.
+ """
+ if topic == None or len(topic) == 0:
+ raise ValueError('Invalid topic.')
+ topic = _fix_sub_topic(topic)
+ if self._sock == None and self._ssl == None:
+ return MOSQ_ERR_NO_CONN
+
+ return self._send_unsubscribe(False, topic)
+
+ def loop_read(self, max_packets=1):
+ """Process read network events. Use in place of calling loop() if you
+ wish to handle your client reads as part of your own application.
+
+ Use socket() to obtain the client socket to call select() or equivalent
+ on.
+
+ Do not use if you are using the threaded interface loop_start()."""
+ if self._sock == None and self._ssl == None:
+ return MOSQ_ERR_NO_CONN
+
+ max_packets = len(self._messages)
+ if max_packets < 1:
+ max_packets = 1
+
+ for i in range(0, max_packets):
+ rc = self._packet_read()
+ if rc > 0:
+ return self._loop_rc_handle(rc)
+ elif rc == MOSQ_ERR_AGAIN:
+ return MOSQ_ERR_SUCCESS
+ return MOSQ_ERR_SUCCESS
+
+ def loop_write(self, max_packets=1):
+ """Process read network events. Use in place of calling loop() if you
+ wish to handle your client reads as part of your own application.
+
+ Use socket() to obtain the client socket to call select() or equivalent
+ on.
+
+ Use want_write() to determine if there is data waiting to be written.
+
+ Do not use if you are using the threaded interface loop_start()."""
+ if self._sock == None and self._ssl == None:
+ return MOSQ_ERR_NO_CONN
+
+ max_packets = len(self._messages)
+ if max_packets < 1:
+ max_packets = 1
+
+ for i in range(0, max_packets):
+ rc = self._packet_write()
+ if rc > 0:
+ return self._loop_rc_handle(rc)
+ elif rc == MOSQ_ERR_AGAIN:
+ return MOSQ_ERR_SUCCESS
+ return MOSQ_ERR_SUCCESS
+
+ def want_write(self):
+ """Call to determine if there is network data waiting to be written.
+ Useful if you are calling select() yourself rather than using loop().
+ """
+ if self._current_out_packet or len(self._out_packet) > 0:
+ return True
+ else:
+ return False
+
+ def loop_misc(self):
+ """Process miscellaneous network events. Use in place of calling loop() if you
+ wish to call select() or equivalent on.
+
+ Do not use if you are using the threaded interface loop_start()."""
+ if self._sock == None and self._ssl == None:
+ return MOSQ_ERR_NO_CONN
+
+ now = time.time()
+ self._check_keepalive()
+ if self._last_retry_check+1 < now:
+ # Only check once a second at most
+ self._message_retry_check()
+ self._last_retry_check = now
+
+ if self._ping_t > 0 and now - self._ping_t >= self._keepalive:
+ # mosq->ping_t != 0 means we are waiting for a pingresp.
+ # This hasn't happened in the keepalive time so we should disconnect.
+ if self._ssl:
+ self._ssl.close()
+ self._ssl = None
+ elif self._sock:
+ self._sock.close()
+ self._sock = None
+
+ self._callback_mutex.acquire()
+ if self._state == mosq_cs_disconnecting:
+ rc = MOSQ_ERR_SUCCESS
+ else:
+ rc = 1
+ if self.on_disconnect:
+ self._in_callback = True
+ self.on_disconnect(self, self._userdata, rc)
+ self._in_callback = False
+ self._callback_mutex.release()
+ return MOSQ_ERR_CONN_LOST
+
+ return MOSQ_ERR_SUCCESS
+
+ def message_retry_set(self, retry):
+ """Set the timeout in seconds before a message with QoS>0 is retried.
+ 20 seconds by default."""
+ if retry < 0:
+ raise ValueError('Invalid retry.')
+
+ self._message_retry = retry
+
+ def user_data_set(self, userdata):
+ """Set the user data variable passed to callbacks. May be any data type."""
+ self._userdata = userdata
+
+ def will_set(self, topic, payload=None, qos=0, retain=False):
+ """Set a Will to be sent by the broker in case the client disconnects unexpectedly.
+
+ This must be called before connect() to have any effect.
+
+ topic: The topic that the will message should be published on.
+ payload: The message to send as a will. If not given, or set to None a
+ zero length message will be used as the will. Passing an int or float
+ will result in the payload being converted to a string representing
+ that number. If you wish to send a true int/float, use struct.pack() to
+ create the payload you require.
+ qos: The quality of service level to use for the will.
+ retain: If set to true, the will message will be set as the "last known
+ good"/retained message for the topic.
+
+ Raises a ValueError if qos is not 0, 1 or 2, or if topic is None or has
+ zero string length.
+ """
+ if topic == None or len(topic) == 0:
+ raise ValueError('Invalid topic.')
+ if qos<0 or qos>2:
+ raise ValueError('Invalid QoS level.')
+ if isinstance(payload, str) == True or isinstance(payload, bytearray) == True:
+ self._will_payload = payload
+ elif isinstance(payload, int) == True or isinstance(payload, float) == True:
+ self._will_payload = str(payload)
+ elif payload == None:
+ self._will_payload = None
+ else:
+ raise TypeError('payload must be a string, bytearray, int, float or None.')
+
+ self._will = True
+ self._will_topic = topic
+ self._will_qos = qos
+ self._will_retain = retain
+
+ def will_clear(self):
+ """ Removes a will that was previously configured with will_set().
+
+ Must be called before connect() to have any effect."""
+ self._will = False
+ self._will_topic = ""
+ self._will_payload = None
+ self._will_qos = 0
+ self._will_retain = False
+
+ def socket(self):
+ """Return the socket or ssl object for this client."""
+ if self._ssl:
+ return self._ssl
+ else:
+ return self._sock
+
+ def loop_forever(self, timeout=1.0, max_packets=1):
+ """This function call loop() for you in an infinite blocking loop. It
+ is useful for the case where you only want to run the MQTT client loop
+ in your program.
+
+ loop_forever() will handle reconnecting for you. If you call
+ disconnect() in a callback it will return."""
+
+ run = True
+ if self._state == mosq_cs_connect_async:
+ self.reconnect()
+
+ while run == True:
+ rc = MOSQ_ERR_SUCCESS
+ while rc == MOSQ_ERR_SUCCESS:
+ rc = self.loop(timeout, max_packets)
+
+ if self._state == mosq_cs_disconnecting:
+ run = False
+ else:
+ time.sleep(1)
+ self.reconnect()
+ return rc
+
+ def loop_start(self):
+ """This is part of the threaded client interface. Call this once to
+ start a new thread to process network traffic. This provides an
+ alternative to repeatedly calling loop() yourself.
+ """
+ if self._thread != None:
+ return MOSQ_ERR_INVAL
+
+ self._thread = threading.Thread(target=self._thread_main)
+ self._thread.daemon = True
+ self._thread.start()
+
+ def loop_stop(self, force=False):
+ """This is part of the threaded client interface. Call this once to
+ stop the network thread previously created with loop_start(). This call
+ will block until the network thread finishes.
+
+ The force parameter is currently ignored.
+ """
+ if self._thread == None:
+ return MOSQ_ERR_INVAL
+
+ self._thread_terminate = True
+ self._thread.join()
+ self._thread = None
+
+ # ============================================================
+ # Private functions
+ # ============================================================
+
+ def _loop_rc_handle(self, rc):
+ if rc:
+ if self._ssl:
+ self._ssl.close()
+ self._ssl = None
+ elif self._sock:
+ self._sock.close()
+ self._sock = None
+
+ self._state_mutex.acquire()
+ if self._state == mosq_cs_disconnecting:
+ rc = MOSQ_ERR_SUCCESS
+ self._state_mutex.release()
+ self._callback_mutex.acquire()
+ if self.on_disconnect:
+ self._in_callback = True
+ self.on_disconnect(self, self._userdata, rc)
+ self._in_callback = False
+
+ self._callback_mutex.release()
+ return rc
+
+ def _packet_read(self):
+ # 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 self._in_packet.command == 0:
+ try:
+ if self._ssl:
+ command = self._ssl.read(1)
+ else:
+ command = self._sock.recv(1)
+ except socket.error as err:
+ (msg) = err
+ if self._ssl and (msg.errno == ssl.SSL_ERROR_WANT_READ or msg.errno == ssl.SSL_ERROR_WANT_WRITE):
+ return MOSQ_ERR_AGAIN
+ if msg.errno == errno.EAGAIN:
+ return MOSQ_ERR_AGAIN
+ raise
+ else:
+ if len(command) == 0:
+ return 1
+ command = struct.unpack("!B", command)
+ self._in_packet.command = command[0]
+
+ if self._in_packet.have_remaining == 0:
+ # Read remaining
+ # Algorithm for decoding taken from pseudo code at
+ # http://publib.boulder.ibm.com/infocenter/wmbhelp/v6r0m0/topic/com.ibm.etools.mft.doc/ac10870_.htm
+ while True:
+ try:
+ if self._ssl:
+ byte = self._ssl.read(1)
+ else:
+ byte = self._sock.recv(1)
+ except socket.error as err:
+ (msg) = err
+ if self._ssl and (msg.errno == ssl.SSL_ERROR_WANT_READ or msg.errno == ssl.SSL_ERROR_WANT_WRITE):
+ return MOSQ_ERR_AGAIN
+ if msg.errno == errno.EAGAIN:
+ return MOSQ_ERR_AGAIN
+ raise
+ else:
+ byte = struct.unpack("!B", byte)
+ byte = byte[0]
+ self._in_packet.remaining_count.append(byte)
+ # Max 4 bytes length for remaining length as defined by protocol.
+ # Anything more likely means a broken/malicious client.
+ if len(self._in_packet.remaining_count) > 4:
+ return MOSQ_ERR_PROTOCOL
+
+ self._in_packet.remaining_length = self._in_packet.remaining_length + (byte & 127)*self._in_packet.remaining_mult
+ self._in_packet.remaining_mult = self._in_packet.remaining_mult * 128
+
+ if (byte & 128) == 0:
+ break
+
+ self._in_packet.have_remaining = 1
+ self._in_packet.to_process = self._in_packet.remaining_length
+
+ while self._in_packet.to_process > 0:
+ try:
+ if self._ssl:
+ data = self._ssl.read(self._in_packet.to_process)
+ else:
+ data = self._sock.recv(self._in_packet.to_process)
+ except socket.error as err:
+ (msg) = err
+ if self._ssl and (msg.errno == ssl.SSL_ERROR_WANT_READ or msg.errno == ssl.SSL_ERROR_WANT_WRITE):
+ return MOSQ_ERR_AGAIN
+ if msg.errno == errno.EAGAIN:
+ return MOSQ_ERR_AGAIN
+ raise
+ else:
+ self._in_packet.to_process = self._in_packet.to_process - len(data)
+ self._in_packet.packet = self._in_packet.packet + data
+
+ # All data for this packet is read.
+ self._in_packet.pos = 0
+ rc = self._packet_handle()
+
+ # Free data and reset values
+ self._in_packet.cleanup()
+
+ self._msgtime_mutex.acquire()
+ self._last_msg_in = time.time()
+ self._msgtime_mutex.release()
+ return rc
+
+ def _packet_write(self):
+ self._current_out_packet_mutex.acquire()
+
+ while self._current_out_packet:
+ packet = self._current_out_packet
+
+ try:
+ if self._ssl:
+ write_length = self._ssl.write(packet.packet[packet.pos:])
+ else:
+ write_length = self._sock.send(packet.packet[packet.pos:])
+ except AttributeError:
+ self._current_out_packet_mutex.release()
+ return MOSQ_ERR_SUCCESS
+ except socket.error as err:
+ self._current_out_packet_mutex.release()
+ (msg) = err
+ if self._ssl and (msg.errno == ssl.SSL_ERROR_WANT_READ or msg.errno == ssl.SSL_ERROR_WANT_WRITE):
+ return MOSQ_ERR_AGAIN
+ if msg.errno == errno.EAGAIN:
+ return MOSQ_ERR_AGAIN
+ raise
+
+ if write_length > 0:
+ packet.to_process = packet.to_process - write_length
+ packet.pos = packet.pos + write_length
+
+ if packet.to_process == 0:
+ if (packet.command & 0xF0) == PUBLISH and packet.qos == 0:
+ self._callback_mutex.acquire()
+ if self.on_publish:
+ self._in_callback = True
+ self.on_publish(self, self._userdata, packet.mid)
+ self._in_callback = False
+
+ self._callback_mutex.release()
+
+ self._out_packet_mutex.acquire()
+ if len(self._out_packet) > 0:
+ self._current_out_packet = self._out_packet.pop(0)
+ else:
+ self._current_out_packet = None
+ self._out_packet_mutex.release()
+ else:
+ pass # FIXME
+
+ self._current_out_packet_mutex.release()
+
+ self._msgtime_mutex.acquire()
+ self._last_msg_out = time.time()
+ self._msgtime_mutex.release()
+
+ return MOSQ_ERR_SUCCESS
+
+ def _easy_log(self, level, buf):
+ if self.on_log:
+ self.on_log(self, self._userdata, level, buf)
+
+ def _check_keepalive(self):
+ now = time.time()
+ self._msgtime_mutex.acquire()
+ last_msg_out = self._last_msg_out
+ last_msg_in = self._last_msg_in
+ self._msgtime_mutex.release()
+ if (self._sock != None or self._ssl != None) and (now - last_msg_out >= self._keepalive or now - last_msg_in >= self._keepalive):
+ if self._state == mosq_cs_connected and self._ping_t == 0:
+ self._send_pingreq()
+ self._msgtime_mutex.acquire()
+ self._last_msg_out = now
+ self._last_msg_in = now
+ self._msgtime_mutex.release()
+ else:
+ if self._ssl:
+ self._ssl.close()
+ self._ssl = None
+ elif self._sock:
+ self._sock.close()
+ self._sock = None
+
+ if self._state == mosq_cs_disconnecting:
+ rc = MOSQ_ERR_SUCCESS
+ else:
+ rc = 1
+ self._callback_mutex.acquire()
+ if self.on_disconnect:
+ self._in_callback = True
+ self.on_disconnect(self, self._userdata, rc)
+ self._in_callback = False
+ self._callback_mutex.release()
+
+ def _mid_generate(self):
+ self._last_mid = self._last_mid + 1
+ if self._last_mid == 65536:
+ self._last_mid = 1
+ return self._last_mid
+
+ def _topic_wildcard_len_check(self, topic):
+ # Search for + or # in a topic. Return MOSQ_ERR_INVAL if found.
+ # Also returns MOSQ_ERR_INVAL if the topic string is too long.
+ # Returns MOSQ_ERR_SUCCESS if everything is fine.
+ if '+' in topic or '#' in topic or len(topic) == 0 or len(topic) > 65535:
+ return MOSQ_ERR_INVAL
+ else:
+ return MOSQ_ERR_SUCCESS
+
+ def _send_pingreq(self):
+ self._easy_log(MOSQ_LOG_DEBUG, "Sending PINGREQ")
+ rc = self._send_simple_command(PINGREQ)
+ if rc == MOSQ_ERR_SUCCESS:
+ self._ping_t = time.time()
+ return rc
+
+ def _send_pingresp(self):
+ self._easy_log(MOSQ_LOG_DEBUG, "Sending PINGRESP")
+ return self._send_simple_command(PINGRESP)
+
+ def _send_puback(self, mid):
+ self._easy_log(MOSQ_LOG_DEBUG, "Sending PUBACK (Mid: "+str(mid)+")")
+ return self._send_command_with_mid(PUBACK, mid, False)
+
+ def _send_pubcomp(self, mid):
+ self._easy_log(MOSQ_LOG_DEBUG, "Sending PUBCOMP (Mid: "+str(mid)+")")
+ return self._send_command_with_mid(PUBCOMP, mid, False)
+
+ def _pack_remaining_length(self, packet, remaining_length):
+ remaining_bytes = []
+ while True:
+ 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.append(byte)
+ packet.extend(struct.pack("!B", byte))
+ if remaining_length == 0:
+ # FIXME - this doesn't deal with incorrectly large payloads
+ return packet
+
+ def _pack_str16(self, packet, data):
+ if sys.version_info[0] < 3:
+ if isinstance(data, bytearray):
+ packet.extend(struct.pack("!H", len(data)))
+ packet.extend(data)
+ elif isinstance(data, str):
+ pack_format = "!H" + str(len(data)) + "s"
+ packet.extend(struct.pack(pack_format, len(data), data))
+ elif isinstance(data, unicode):
+ udata = data.encode('utf-8')
+ pack_format = "!H" + str(len(udata)) + "s"
+ packet.extend(struct.pack(pack_format, len(udata), udata))
+ else:
+ raise TypeError
+ else:
+ if isinstance(data, bytearray):
+ packet.extend(struct.pack("!H", len(data)))
+ packet.extend(data)
+ elif isinstance(data, str):
+ udata = data.encode('utf-8')
+ pack_format = "!H" + str(len(udata)) + "s"
+ packet.extend(struct.pack(pack_format, len(udata), udata))
+ else:
+ raise TypeError
+
+ def _send_publish(self, mid, topic, payload=None, qos=0, retain=False, dup=False):
+ if self._sock == None and self._ssl == None:
+ return MOSQ_ERR_NO_CONN
+
+ command = PUBLISH | ((dup&0x1)<<3) | (qos<<1) | retain
+ packet = bytearray()
+ packet.extend(struct.pack("!B", command))
+ if payload == None:
+ remaining_length = 2+len(topic)
+ self._easy_log(MOSQ_LOG_DEBUG, "Sending PUBLISH (d"+str(dup)+", q"+str(qos)+", r"+str(retain)+", m"+str(mid)+", '"+topic+"' (NULL payload)")
+ else:
+ remaining_length = 2+len(topic) + len(payload)
+ self._easy_log(MOSQ_LOG_DEBUG, "Sending PUBLISH (d"+str(dup)+", q"+str(qos)+", r"+str(retain)+", m"+str(mid)+", '"+topic+"', ... ("+str(len(payload))+" bytes)")
+
+ if qos > 0:
+ # For message id
+ remaining_length = remaining_length + 2
+
+ self._pack_remaining_length(packet, remaining_length)
+ self._pack_str16(packet, topic)
+
+ if qos > 0:
+ # For message id
+ packet.extend(struct.pack("!H", mid))
+
+ if payload != None:
+ if isinstance(payload, str):
+ if sys.version_info[0] < 3:
+ pack_format = str(len(payload)) + "s"
+ packet.extend(struct.pack(pack_format, payload))
+ else:
+ upayload = payload.encode('utf-8')
+ pack_format = str(len(upayload)) + "s"
+ packet.extend(struct.pack(pack_format, upayload))
+ elif isinstance(payload, bytearray):
+ packet.extend(payload)
+ elif isinstance(payload, unicode):
+ upayload = payload.encode('utf-8')
+ pack_format = str(len(upayload)) + "s"
+ packet.extend(struct.pack(pack_format, upayload))
+ else:
+ raise TypeError('payload must be a string, unicode or a bytearray.')
+
+ return self._packet_queue(PUBLISH, packet, mid, qos)
+
+ def _send_pubrec(self, mid):
+ self._easy_log(MOSQ_LOG_DEBUG, "Sending PUBREC (Mid: "+str(mid)+")")
+ return self._send_command_with_mid(PUBREC, mid, False)
+
+ def _send_pubrel(self, mid, dup=False):
+ self._easy_log(MOSQ_LOG_DEBUG, "Sending PUBREL (Mid: "+str(mid)+")")
+ return self._send_command_with_mid(PUBREL|2, mid, dup)
+
+ def _send_command_with_mid(self, command, mid, dup):
+ # For PUBACK, PUBCOMP, PUBREC, and PUBREL
+ if dup:
+ command = command | 8
+
+ remaining_length = 2
+ packet = struct.pack('!BBH', command, remaining_length, mid)
+ return self._packet_queue(command, packet, mid, 1)
+
+ def _send_simple_command(self, command):
+ # For DISCONNECT, PINGREQ and PINGRESP
+ remaining_length = 0
+ packet = struct.pack('!BB', command, remaining_length)
+ return self._packet_queue(command, packet, 0, 0)
+
+ def _send_connect(self, keepalive, clean_session):
+ remaining_length = 12 + 2+len(self._client_id)
+ connect_flags = 0
+ if clean_session:
+ connect_flags = connect_flags | 0x02
+
+ if self._will:
+ remaining_length = remaining_length + 2+len(self._will_topic) + 2+len(self._will_payload)
+ connect_flags = connect_flags | 0x04 | ((self._will_qos&0x03) << 3) | ((self._will_retain&0x01) << 5)
+
+ if self._username:
+ remaining_length = remaining_length + 2+len(self._username)
+ connect_flags = connect_flags | 0x80
+ if self._password:
+ connect_flags = connect_flags | 0x40
+ remaining_length = remaining_length + 2+len(self._password)
+
+ command = CONNECT
+ packet = bytearray()
+ packet.extend(struct.pack("!B", command))
+ self._pack_remaining_length(packet, remaining_length)
+ packet.extend(struct.pack("!H6sBBH", len(PROTOCOL_NAME), PROTOCOL_NAME, PROTOCOL_VERSION, connect_flags, keepalive))
+
+ self._pack_str16(packet, self._client_id)
+
+ if self._will:
+ self._pack_str16(packet, self._will_topic)
+ if len(self._will_payload) > 0:
+ self._pack_str16(packet, self._will_payload)
+ else:
+ packet.extend(struct.pack("!H", 0))
+
+ if self._username:
+ self._pack_str16(packet, self._username)
+
+ if self._password:
+ self._pack_str16(packet, self._password)
+
+ self._keepalive = keepalive
+ return self._packet_queue(command, packet, 0, 0)
+
+ def _send_disconnect(self):
+ return self._send_simple_command(DISCONNECT)
+
+ def _send_subscribe(self, dup, topic, topic_qos):
+ remaining_length = 2 + 2+len(topic) + 1
+ command = SUBSCRIBE | (dup<<3) | (1<<1)
+ packet = bytearray()
+ packet.extend(struct.pack("!B", command))
+ self._pack_remaining_length(packet, remaining_length)
+ local_mid = self._mid_generate()
+ pack_format = "!HH" + str(len(topic)) + "sB"
+ packet.extend(struct.pack("!H", local_mid))
+ self._pack_str16(packet, topic)
+ packet.extend(struct.pack("B", topic_qos))
+ return (self._packet_queue(command, packet, local_mid, 1), local_mid)
+
+ def _send_unsubscribe(self, dup, topic):
+ remaining_length = 2 + 2+len(topic)
+ command = UNSUBSCRIBE | (dup<<3) | (1<<1)
+ packet = bytearray()
+ packet.extend(struct.pack("!B", command))
+ self._pack_remaining_length(packet, remaining_length)
+ local_mid = self._mid_generate()
+ pack_format = "!HH" + str(len(topic)) + "sB"
+ packet.extend(struct.pack("!H", local_mid))
+ self._pack_str16(packet, topic)
+ return (self._packet_queue(command, packet, local_mid, 1), local_mid)
+
+ def _message_update(self, mid, direction, state):
+ for m in self._messages:
+ if m.mid == mid and m.direction == direction:
+ m.state = state
+ m.timestamp = time.time()
+ return MOSQ_ERR_SUCCESS
+
+ return MOSQ_ERR_NOT_FOUND
+
+ def _message_retry_check(self):
+ now = time.time()
+ for m in self._messages:
+ if m.timestamp + self._message_retry < now:
+ if m.state == mosq_ms_wait_puback or m.state == mosq_ms_wait_pubrec:
+ m.timestamp = now
+ m.dup = True
+ self._send_publish(m.mid, m.topic, m.payload, m.qos, m.retain, m.dup)
+ elif m.state == mosq_ms_wait_pubrel:
+ m.timestamp = now
+ m.dup = True
+ self._send_pubrec(m.mid)
+ elif m.state == mosq_ms_wait_pubcomp:
+ m.timestamp = now
+ m.dup = True
+ self._send_pubrel(m.mid, True)
+
+ def _messages_reconnect_reset(self):
+ for m in self._messages:
+ m.timestamp = 0
+ if m.direction == mosq_md_out:
+ if m.qos == 1:
+ m.state = mosq_ms_wait_puback
+ elif m.qos == 2:
+ m.state = mosq_ms_wait_pubrec
+ else:
+ self._messages.pop(self._messages.index(m))
+
+ def _packet_queue(self, command, packet, mid, qos):
+ mpkt = MosquittoPacket(command, packet, mid, qos)
+
+ self._out_packet_mutex.acquire()
+ self._out_packet.append(mpkt)
+ if self._current_out_packet_mutex.acquire(False) == True:
+ if self._current_out_packet == None and len(self._out_packet) > 0:
+ self._current_out_packet = self._out_packet.pop(0)
+ self._current_out_packet_mutex.release()
+ self._out_packet_mutex.release()
+
+ if self._in_callback == False:
+ return self.loop_write()
+ else:
+ return MOSQ_ERR_SUCCESS
+
+ def _packet_handle(self):
+ cmd = self._in_packet.command&0xF0
+ if cmd == PINGREQ:
+ return self._handle_pingreq()
+ elif cmd == PINGRESP:
+ return self._handle_pingresp()
+ elif cmd == PUBACK:
+ return self._handle_pubackcomp("PUBACK")
+ elif cmd == PUBCOMP:
+ return self._handle_pubackcomp("PUBCOMP")
+ elif cmd == PUBLISH:
+ return self._handle_publish()
+ elif cmd == PUBREC:
+ return self._handle_pubrec()
+ elif cmd == PUBREL:
+ return self._handle_pubrel()
+ elif cmd == CONNACK:
+ return self._handle_connack()
+ elif cmd == SUBACK:
+ return self._handle_suback()
+ elif cmd == UNSUBACK:
+ return self._handle_unsuback()
+ else:
+ # If we don't recognise the command, return an error straight away.
+ self._easy_log(MOSQ_LOG_ERR, "Error: Unrecognised command "+str(cmd))
+ return MOSQ_ERR_PROTOCOL
+
+ def _handle_pingreq(self):
+ if self._strict_protocol:
+ if self._in_packet.remaining_length != 0:
+ return MOSQ_ERR_PROTOCOL
+
+ self._easy_log(MOSQ_LOG_DEBUG, "Received PINGREQ")
+ return self._send_pingresp()
+
+ def _handle_pingresp(self):
+ if self._strict_protocol:
+ if self._in_packet.remaining_length != 0:
+ return MOSQ_ERR_PROTOCOL
+
+ # No longer waiting for a PINGRESP.
+ self._ping_t = 0
+ self._easy_log(MOSQ_LOG_DEBUG, "Received PINGRESP")
+ return MOSQ_ERR_SUCCESS
+
+ def _handle_connack(self):
+ if self._strict_protocol:
+ if self._in_packet.remaining_length != 2:
+ return MOSQ_ERR_PROTOCOL
+
+ if len(self._in_packet.packet) != 2:
+ return MOSQ_ERR_PROTOCOL
+
+ (resvd, result) = struct.unpack("!BB", self._in_packet.packet)
+ self._easy_log(MOSQ_LOG_DEBUG, "Received CONNACK ("+str(resvd)+", "+str(result)+")")
+ self._callback_mutex.acquire()
+ if self.on_connect:
+ self._in_callback = True
+ self.on_connect(self, self._userdata, result)
+ self._in_callback = False
+ self._callback_mutex.release()
+ if result == 0:
+ self._state = mosq_cs_connected
+ return MOSQ_ERR_SUCCESS
+ elif result > 0 and result < 6:
+ return MOSQ_ERR_CONN_REFUSED
+ else:
+ return MOSQ_ERR_PROTOCOL
+
+ def _handle_suback(self):
+ self._easy_log(MOSQ_LOG_DEBUG, "Received SUBACK")
+ pack_format = "!H" + str(len(self._in_packet.packet)-2) + 's'
+ (mid, packet) = struct.unpack(pack_format, self._in_packet.packet)
+ pack_format = "!" + "B"*len(packet)
+ granted_qos = struct.unpack(pack_format, packet)
+
+ self._callback_mutex.acquire()
+ if self.on_subscribe:
+ self._in_callback = True
+ self.on_subscribe(self, self._userdata, mid, granted_qos)
+ self._in_callback = False
+ self._callback_mutex.release()
+
+ return MOSQ_ERR_SUCCESS
+
+ def _handle_publish(self):
+ rc = 0
+
+ header = self._in_packet.command
+ message = MosquittoMessage()
+ message.direction = mosq_md_in
+ message.dup = (header & 0x08)>>3
+ message.qos = (header & 0x06)>>1
+ message.retain = (header & 0x01)
+
+ pack_format = "!H" + str(len(self._in_packet.packet)-2) + 's'
+ (slen, packet) = struct.unpack(pack_format, self._in_packet.packet)
+ pack_format = '!' + str(slen) + 's' + str(len(packet)-slen) + 's'
+ (message.topic, packet) = struct.unpack(pack_format, packet)
+
+ if len(message.topic) == 0:
+ return MOSQ_ERR_PROTOCOL
+
+ if sys.version_info[0] >= 3:
+ message.topic = message.topic.decode('utf-8')
+ message.topic = _fix_sub_topic(message.topic)
+
+ if message.qos > 0:
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (message.mid, packet) = struct.unpack(pack_format, packet)
+
+ message.payload = packet
+
+ self._easy_log(MOSQ_LOG_DEBUG, "Received PUBLISH (d"+str(message.dup)+
+ ", q"+str(message.qos)+", r"+str(message.retain)+
+ ", m"+str(message.mid)+", '"+message.topic+
+ "', ... ("+str(len(message.payload))+" bytes)")
+
+ message.timestamp = time.time()
+ if message.qos == 0:
+ self._callback_mutex.acquire()
+ if self.on_message:
+ self._in_callback = True
+ self.on_message(self, self._userdata, message)
+ self._in_callback = False
+
+ self._callback_mutex.release()
+ return MOSQ_ERR_SUCCESS
+ elif message.qos == 1:
+ rc = self._send_puback(message.mid)
+ self._callback_mutex.acquire()
+ if self.on_message:
+ self._in_callback = True
+ self.on_message(self, self._userdata, message)
+ self._in_callback = False
+
+ self._callback_mutex.release()
+ return rc
+ elif message.qos == 2:
+ rc = self._send_pubrec(message.mid)
+ message.state = mosq_ms_wait_pubrel
+ self._messages.append(message)
+ return rc
+ else:
+ return MOSQ_ERR_PROTOCOL
+
+ def _handle_pubrel(self):
+ if self._strict_protocol:
+ if self._in_packet.remaining_length != 2:
+ return MOSQ_ERR_PROTOCOL
+
+ if len(self._in_packet.packet) != 2:
+ return MOSQ_ERR_PROTOCOL
+
+ mid = struct.unpack("!H", self._in_packet.packet)
+ mid = mid[0]
+ self._easy_log(MOSQ_LOG_DEBUG, "Received PUBREL (Mid: "+str(mid)+")")
+
+ for i in range(len(self._messages)):
+ if self._messages[i].direction == mosq_md_in and self._messages[i].mid == mid:
+
+ # Only pass the message on if we have removed it from the queue - this
+ # prevents multiple callbacks for the same message.
+ self._callback_mutex.acquire()
+ if self.on_message:
+ self._in_callback = True
+ self.on_message(self, self._userdata, self._messages[i])
+ self._in_callback = False
+ self._callback_mutex.release()
+ self._messages.pop(i)
+
+ return self._send_pubcomp(mid)
+
+ return MOSQ_ERR_SUCCESS
+
+ def _handle_pubrec(self):
+ if self._strict_protocol:
+ if self._in_packet.remaining_length != 2:
+ return MOSQ_ERR_PROTOCOL
+
+ mid = struct.unpack("!H", self._in_packet.packet)
+ mid = mid[0]
+ self._easy_log(MOSQ_LOG_DEBUG, "Received PUBREC (Mid: "+str(mid)+")")
+
+ for i in range(len(self._messages)):
+ if self._messages[i].direction == mosq_md_out and self._messages[i].mid == mid:
+ self._messages[i].state = mosq_ms_wait_pubcomp
+ self._messages[i].timestamp = time.time()
+ return self._send_pubrel(mid, False)
+
+ return MOSQ_ERR_SUCCESS
+
+ def _handle_unsuback(self):
+ if self._strict_protocol:
+ if self._in_packet.remaining_length != 2:
+ return MOSQ_ERR_PROTOCOL
+
+ mid = struct.unpack("!H", self._in_packet.packet)
+ mid = mid[0]
+ self._easy_log(MOSQ_LOG_DEBUG, "Received UNSUBACK (Mid: "+str(mid)+")")
+ self._callback_mutex.acquire()
+ if self.on_unsubscribe:
+ self._in_callback = True
+ self.on_unsubscribe(self, self._userdata, mid)
+ self._in_callback = False
+ self._callback_mutex.release()
+ return MOSQ_ERR_SUCCESS
+
+ def _handle_pubackcomp(self, cmd):
+ if self._strict_protocol:
+ if self._in_packet.remaining_length != 2:
+ return MOSQ_ERR_PROTOCOL
+
+ mid = struct.unpack("!H", self._in_packet.packet)
+ mid = mid[0]
+ self._easy_log(MOSQ_LOG_DEBUG, "Received "+cmd+" (Mid: "+str(mid)+")")
+
+ for i in range(len(self._messages)):
+ try:
+ if self._messages[i].direction == mosq_md_out and self._messages[i].mid == mid:
+ # Only inform the client the message has been sent once.
+ self._callback_mutex.acquire()
+ if self.on_publish:
+ self._in_callback = True
+ self.on_publish(self, self._userdata, mid)
+ self._in_callback = False
+
+ self._callback_mutex.release()
+ self._messages.pop(i)
+ except IndexError:
+ # Have removed item so i>count.
+ # Not really an error.
+ pass
+
+ return MOSQ_ERR_SUCCESS
+
+ def _thread_main(self):
+ run = True
+ self._thread_terminate = False
+ self._state_mutex.acquire()
+ if self._state == mosq_cs_connect_async:
+ self._state_mutex.release()
+ self.reconnect()
+ else:
+ self._state_mutex.release()
+
+ while run == True:
+ rc = MOSQ_ERR_SUCCESS
+ while rc == MOSQ_ERR_SUCCESS:
+ rc = self.loop()
+ if self._thread_terminate == True:
+ rc = 1
+ run = False
+
+ self._state_mutex.acquire()
+ if self._state == mosq_cs_disconnecting:
+ run = False
+ self._state_mutex.release()
+ else:
+ self._state_mutex.release()
+ time.sleep(1)
+ self.reconnect()
+
--- /dev/null
+from sys import version
+
+from distutils.core import setup
+setup(name='mosquitto',
+ version='1.1.90',
+ description='MQTT version 3.1 client class',
+ author='Roger Light',
+ author_email='roger@atchoo.org',
+ url='http://mosquitto.org/',
+ download_url='http://mosquitto.org/files/',
+ license='BSD License',
+ py_modules=['mosquitto'],
+
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: POSIX',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Topic :: Communications',
+ 'Topic :: Internet',
+ ]
+ )
--- /dev/null
+#!/usr/bin/python
+
+# Copyright (c) 2010,2011 Roger Light <roger@atchoo.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of mosquitto nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import mosquitto
+
+def on_connect(mosq, obj, rc):
+ print("rc: "+str(rc))
+
+def on_message(mosq, obj, msg):
+ print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
+
+def on_publish(mosq, obj, mid):
+ print("mid: "+str(mid))
+
+def on_subscribe(mosq, obj, mid, granted_qos):
+ print("Subscribed: "+str(mid)+" "+str(granted_qos))
+
+def on_log(mosq, obj, level, string):
+ print(string)
+
+mqttc = mosquitto.Mosquitto()
+mqttc.on_message = on_message
+mqttc.on_connect = on_connect
+mqttc.on_publish = on_publish
+mqttc.on_subscribe = on_subscribe
+# Uncomment to enable debug messages
+#mqttc.on_log = on_log
+mqttc.connect("test.mosquitto.org", 1883, 60)
+mqttc.subscribe("$SYS/#", 0)
+
+
+rc = 0
+while rc == 0:
+ rc = mqttc.loop()
+
+print("rc: "+str(rc))
--- /dev/null
+include ../config.mk
+
+CC=cc
+CFLAGS=-I../src -I../lib -I. -I.. -Wall -ggdb -DDEBUG -DWITH_CLIENT
+LDFLAGS=
+OBJS=context.o database.o logging.o memory.o net.o raw_send.o raw_send_client.o read_handle.o read_handle_client.o util.o
+SOVERSION=1
+
+.PHONY: all test clean reallyclean
+
+all : fake_user msgsps_pub msgsps_sub
+#packet-gen qos
+
+test :
+ $(MAKE) -C broker test
+ $(MAKE) -C lib test
+
+fake_user : fake_user.o
+ ${CC} $^ -o $@ ../lib/libmosquitto.so.${SOVERSION}
+ #${CC} $^ -o $@ -lmosquitto
+
+fake_user.o : fake_user.c
+ ${CC} $(CFLAGS) -c $< -o $@
+
+msgsps_pub : msgsps_pub.o
+ ${CC} $^ -o $@ ../lib/libmosquitto.so.${SOVERSION}
+
+msgsps_pub.o : msgsps_pub.c msgsps_common.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+msgsps_sub : msgsps_sub.o
+ ${CC} $^ -o $@ ../lib/libmosquitto.so.${SOVERSION}
+
+msgsps_sub.o : msgsps_sub.c msgsps_common.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+packet-gen : packet-gen.o
+ ${CC} $^ -o $@ ../lib/libmosquitto.so.${SOVERSION}
+
+packet-gen.o : packet-gen.c
+ ${CC} $(CFLAGS) -c $< -o $@
+
+qos : qos.o
+ ${CC} $^ -o $@ ../lib/libmosquitto.so.${SOVERSION}
+
+qos.o : qos.c
+ ${CC} $(CFLAGS) -c $< -o $@
+
+random_client : random_client.o ${OBJS}
+ ${CC} $^ -o $@ ${LDFLAGS}
+
+random_client.o : random_client.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+context.o : ../src/context.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+database.o : ../src/database.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+logging.o : ../src/logging.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+memory.o : ../src/memory.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+net.o : ../src/net.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+raw_send.o : ../src/raw_send.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+raw_send_client.o : ../src/raw_send_client.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+read_handle.o : ../src/read_handle.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+read_handle_client.o : ../src/read_handle_client.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+util.o : ../src/util.c ../src/mqtt3.h
+ ${CC} $(CFLAGS) -c $< -o $@
+
+reallyclean : clean
+ -rm -f *.orig
+
+clean :
+ -rm -f *.o random_client qos msgsps_pub msgsps_sub fake_user test_client *.pyc
+ $(MAKE) -C lib clean
+ $(MAKE) -C broker clean
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client produces a correct connect and subsequent disconnect.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id 01-con-discon-success
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK and verifying that rc=0, the client should send a DISCONNECT
+# message. If rc!=0, the client should exit with an error.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("01-con-discon-success", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
+
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a pingreq after the keepalive time
+
+# The client should connect to port 1888 with keepalive=4, clean session set,
+# and client id 01-keepalive-pingreq
+# The client should send a PINGREQ message after the appropriate amount of time
+# (4 seconds after no traffic).
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 4
+connect_packet = mosq_test.gen_connect("01-keepalive-pingreq", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+pingreq_packet = mosq_test.gen_pingreq()
+pingresp_packet = mosq_test.gen_pingresp()
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(keepalive+10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "pingreq", pingreq_packet):
+ time.sleep(1.0)
+ conn.send(pingresp_packet)
+
+ if mosq_test.expect_packet(conn, "pingreq", pingreq_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
+
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client produces a correct connect with clean session not set.
+
+# The client should connect to port 1888 with keepalive=60, clean session not
+# set, and client id 01-no-clean-session.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("01-no-clean-session", clean_session=False, keepalive=keepalive)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
+
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client produces a correct connect with a username and password.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# client id 01-unpwd-set, username set to uname and password set to ;'[08gn=#
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("01-unpwd-set", keepalive=keepalive, username="uname", password=";'[08gn=#")
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
+
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client produces a correct connect with a will.
+# Will QoS=1, will retain=1.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# client id 01-will-set will topic set to topic/on/unexpected/disconnect , will
+# payload set to "will message", will qos set to 1 and will retain set.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("01-will-set", keepalive=keepalive, will_topic="topic/on/unexpected/disconnect", will_qos=1, will_retain=True, will_payload="will message")
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
+
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client produces a correct connect with a will, username and password.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# client id 01-will-unpwd-set , will topic set to "will-topic", will payload
+# set to "will message", will qos=2, will retain not set, username set to
+# "oibvvwqw" and password set to "#'^2hg9a&nm38*us".
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("01-will-unpwd-set",
+ keepalive=keepalive, username="oibvvwqw", password="#'^2hg9a&nm38*us",
+ will_topic="will-topic", will_qos=2, will_payload="will message")
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
+
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 0.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id subscribe-qos0-test
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE
+# message to subscribe to topic "qos0/test" with QoS=0. If rc!=0, the client
+# should exit with an error.
+# Upon receiving the correct SUBSCRIBE message, the test will reply with a
+# SUBACK message with the accepted QoS set to 0. On receiving the SUBACK
+# message, the client should send a DISCONNECT message.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("subscribe-qos0-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 1
+subscribe_packet = mosq_test.gen_subscribe(mid, "qos0/test", 0)
+suback_packet = mosq_test.gen_suback(mid, 0)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "subscribe", subscribe_packet):
+ conn.send(suback_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 1.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id subscribe-qos1-test
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE
+# message to subscribe to topic "qos1/test" with QoS=1. If rc!=0, the client
+# should exit with an error.
+# Upon receiving the correct SUBSCRIBE message, the test will reply with a
+# SUBACK message with the accepted QoS set to 1. On receiving the SUBACK
+# message, the client should send a DISCONNECT message.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("subscribe-qos1-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 1
+subscribe_packet = mosq_test.gen_subscribe(mid, "qos1/test", 1)
+suback_packet = mosq_test.gen_suback(mid, 1)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "subscribe", subscribe_packet):
+ conn.send(suback_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 2.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id subscribe-qos2-test
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE
+# message to subscribe to topic "qos2/test" with QoS=2. If rc!=0, the client
+# should exit with an error.
+# Upon receiving the correct SUBSCRIBE message, the test will reply with a
+# SUBACK message with the accepted QoS set to 2. On receiving the SUBACK
+# message, the client should send a DISCONNECT message.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("subscribe-qos2-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 1
+subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/test", 2)
+suback_packet = mosq_test.gen_suback(mid, 2)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "subscribe", subscribe_packet):
+ conn.send(suback_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct UNSUBSCRIBE packet.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("unsubscribe-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 1
+unsubscribe_packet = mosq_test.gen_unsubscribe(mid, "unsubscribe/test")
+unsuback_packet = mosq_test.gen_unsuback(mid)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "unsubscribe", unsubscribe_packet):
+ conn.send(unsuback_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client responds correctly to a PUBLISH with QoS 1.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id publish-qos1-test
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK the client should verify that rc==0.
+# The test will send the client a PUBLISH message with topic
+# "pub/qos1/receive", payload of "message", QoS=1 and mid=123. The client
+# should handle this as per the spec by sending a PUBACK message.
+# The client should then exit with return code==0.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 123
+publish_packet = mosq_test.gen_publish("pub/qos1/receive", qos=1, mid=mid, payload="message")
+puback_packet = mosq_test.gen_puback(mid)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+ conn.send(publish_packet)
+
+ if mosq_test.expect_packet(conn, "puback", puback_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ for i in range(0, 5):
+ if client.returncode != None:
+ break
+ time.sleep(0.1)
+
+ client.terminate()
+ client.wait()
+ sock.close()
+ if client.returncode != 0:
+ exit(1)
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client responds correctly to a PUBLISH with QoS 1.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id publish-qos2-test
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK the client should verify that rc==0.
+# The test will send the client a PUBLISH message with topic
+# "pub/qos2/receive", payload of "message", QoS=2 and mid=13423. The client
+# should handle this as per the spec by sending a PUBREC message.
+# The test will not respond to the first PUBREC message, so the client must
+# resend the PUBREC message with dup=1. Note that to keep test durations low, a
+# message retry timeout of less than 10 seconds is required for this test.
+# On receiving the second PUBREC with dup==1, the test will send the correct
+# PUBREL message. The client should respond to this with the correct PUBCOMP
+# message and then exit with return code=0.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 13423
+publish_packet = mosq_test.gen_publish("pub/qos2/receive", qos=2, mid=mid, payload="message")
+pubrec_packet = mosq_test.gen_pubrec(mid)
+pubrel_packet = mosq_test.gen_pubrel(mid)
+pubcomp_packet = mosq_test.gen_pubcomp(mid)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+ conn.send(publish_packet)
+
+ if mosq_test.expect_packet(conn, "pubrec", pubrec_packet):
+ # Should be repeated due to timeout
+ if mosq_test.expect_packet(conn, "pubrec", pubrec_packet):
+ conn.send(pubrel_packet)
+
+ if mosq_test.expect_packet(conn, "pubcomp", pubcomp_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ for i in range(0, 5):
+ if client.returncode != None:
+ break
+ time.sleep(0.1)
+
+ client.terminate()
+ client.wait()
+ sock.close()
+ if client.returncode != 0:
+ exit(1)
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct PUBLISH to a topic with QoS 1, then responds correctly to a disconnect.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 1
+publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message")
+publish_packet_dup = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", dup=True)
+puback_packet = mosq_test.gen_puback(mid)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(15)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "publish", publish_packet):
+ # Disconnect client. It should reconnect.
+ conn.close()
+
+ (conn, address) = sock.accept()
+ conn.settimeout(15)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "retried publish", publish_packet_dup):
+ conn.send(puback_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct PUBLISH to a topic with QoS 1 and responds to a delay.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id publish-qos1-test
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK the client should verify that rc==0. If not, it should exit with
+# return code=1.
+# On a successful CONNACK, the client should send a PUBLISH message with topic
+# "pub/qos1/test", payload "message" and QoS=1.
+# The test will not respond to the first PUBLISH message, so the client must
+# resend the PUBLISH message with dup=1. Note that to keep test durations low, a
+# message retry timeout of less than 10 seconds is required for this test.
+# On receiving the second PUBLISH message, the test will send the correct
+# PUBACK response. On receiving the correct PUBACK response, the client should
+# send a DISCONNECT message.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 1
+publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message")
+publish_packet_dup = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", dup=True)
+puback_packet = mosq_test.gen_puback(mid)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "publish", publish_packet):
+ # Delay for > 3 seconds (message retry time)
+
+ if mosq_test.expect_packet(conn, "dup publish", publish_packet_dup):
+ conn.send(puback_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct PUBLISH to a topic with QoS 2 and responds to a disconnect.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 1
+publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message")
+publish_dup_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", dup=True)
+pubrec_packet = mosq_test.gen_pubrec(mid)
+pubrel_packet = mosq_test.gen_pubrel(mid)
+pubrel_dup_packet = mosq_test.gen_pubrel(mid, dup=True)
+pubcomp_packet = mosq_test.gen_pubcomp(mid)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "publish", publish_packet):
+ # Disconnect client. It should reconnect.
+ conn.close()
+
+ (conn, address) = sock.accept()
+ conn.settimeout(15)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "retried publish", publish_dup_packet):
+ conn.send(pubrec_packet)
+
+ if mosq_test.expect_packet(conn, "pubrel", pubrel_packet):
+ # Disconnect client. It should reconnect.
+ conn.close()
+
+ (conn, address) = sock.accept()
+ conn.settimeout(15)
+
+ # Complete connection and message flow.
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "2nd retried publish", publish_dup_packet):
+ conn.send(pubrec_packet)
+
+ if mosq_test.expect_packet(conn, "pubrel", pubrel_packet):
+ conn.send(pubcomp_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct PUBLISH to a topic with QoS 1 and responds to a delay.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id publish-qos2-test
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK the client should verify that rc==0. If not, it should exit with
+# return code=1.
+# On a successful CONNACK, the client should send a PUBLISH message with topic
+# "pub/qos2/test", payload "message" and QoS=2.
+# The test will not respond to the first PUBLISH message, so the client must
+# resend the PUBLISH message with dup=1. Note that to keep test durations low, a
+# message retry timeout of less than 10 seconds is required for this test.
+# On receiving the second PUBLISH message, the test will send the correct
+# PUBREC response. On receiving the correct PUBREC response, the client should
+# send a PUBREL message.
+# The test will not respond to the first PUBREL message, so the client must
+# resend the PUBREL message with dup=1. On receiving the second PUBREL message,
+# the test will send the correct PUBCOMP response. On receiving the correct
+# PUBCOMP response, the client should send a DISCONNECT message.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+mid = 1
+publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message")
+publish_dup_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", dup=True)
+pubrec_packet = mosq_test.gen_pubrec(mid)
+pubrel_packet = mosq_test.gen_pubrel(mid)
+pubrel_dup_packet = mosq_test.gen_pubrel(mid, dup=True)
+pubcomp_packet = mosq_test.gen_pubcomp(mid)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "publish", publish_packet):
+ # Delay for > 3 seconds (message retry time)
+
+ if mosq_test.expect_packet(conn, "dup publish", publish_dup_packet):
+ conn.send(pubrec_packet)
+
+ if mosq_test.expect_packet(conn, "pubrel", pubrel_packet):
+ if mosq_test.expect_packet(conn, "dup pubrel", pubrel_dup_packet):
+ conn.send(pubcomp_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct PUBLISH to a topic with QoS 0 and no payload.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id publish-qos0-test-np
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK and verifying that rc=0, the client should send a PUBLISH message
+# to topic "pub/qos0/no-payload/test" with zero length payload and QoS=0. If
+# rc!=0, the client should exit with an error.
+# After sending the PUBLISH message, the client should send a DISCONNECT message.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("publish-qos0-test-np", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+publish_packet = mosq_test.gen_publish("pub/qos0/no-payload/test", qos=0)
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "publish", publish_packet):
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct PUBLISH to a topic with QoS 0.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id publish-qos0-test
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK and verifying that rc=0, the client should send a PUBLISH message
+# to topic "pub/qos0/test" with payload "message" and QoS=0. If rc!=0, the
+# client should exit with an error.
+# After sending the PUBLISH message, the client should send a DISCONNECT message.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("publish-qos0-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+publish_packet = mosq_test.gen_publish("pub/qos0/test", qos=0, payload="message")
+
+disconnect_packet = mosq_test.gen_disconnect()
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "publish", publish_packet):
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client sends a correct retained PUBLISH to a topic with QoS 0.
+
+import inspect
+import os
+import subprocess
+import socket
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+rc = 1
+keepalive = 60
+mid = 16
+connect_packet = mosq_test.gen_connect("retain-qos0-test", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+
+publish_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", retain=True)
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.settimeout(10)
+sock.bind(('', 1888))
+sock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = sock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "publish", publish_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ sock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+import inspect
+import os
+import subprocess
+import socket
+import ssl
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+if sys.version < '2.7':
+ print("WARNING: SSL not supported on Python 2.6")
+ exit(0)
+
+rc = 1
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+
+client = subprocess.Popen(client_args, env=env)
+client.wait()
+
+rc = client.returncode
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client produces a correct connect and subsequent disconnect when using SSL.
+# Client must provide a certificate.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id 08-ssl-connect-crt-auth
+# It should use the CA certificate ssl/test-ca.crt for verifying the server.
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK and verifying that rc=0, the client should send a DISCONNECT
+# message. If rc!=0, the client should exit with an error.
+
+import inspect
+import os
+import subprocess
+import socket
+import ssl
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+if sys.version < '2.7':
+ print("WARNING: SSL not supported on Python 2.6")
+ exit(0)
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("08-ssl-connect-crt-auth", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+disconnect_packet = mosq_test.gen_disconnect()
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-ca.crt",
+ keyfile="../ssl/server.key", certfile="../ssl/server.crt",
+ server_side=True, ssl_version=ssl.PROTOCOL_TLSv1, cert_reqs=ssl.CERT_REQUIRED)
+ssock.settimeout(10)
+ssock.bind(('', 1888))
+ssock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = ssock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ ssock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+# Test whether a client produces a correct connect and subsequent disconnect when using SSL.
+
+# The client should connect to port 1888 with keepalive=60, clean session set,
+# and client id 08-ssl-connect-no-auth
+# It should use the CA certificate ssl/test-ca.crt for verifying the server.
+# The test will send a CONNACK message to the client with rc=0. Upon receiving
+# the CONNACK and verifying that rc=0, the client should send a DISCONNECT
+# message. If rc!=0, the client should exit with an error.
+
+import inspect
+import os
+import subprocess
+import socket
+import ssl
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+import mosq_test
+
+if sys.version < '2.7':
+ print("WARNING: SSL not supported on Python 2.6")
+ exit(0)
+
+rc = 1
+keepalive = 60
+connect_packet = mosq_test.gen_connect("08-ssl-connect-no-auth", keepalive=keepalive)
+connack_packet = mosq_test.gen_connack(rc=0)
+disconnect_packet = mosq_test.gen_disconnect()
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-ca.crt", keyfile="../ssl/server.key", certfile="../ssl/server.crt", server_side=True, ssl_version=ssl.PROTOCOL_TLSv1)
+ssock.settimeout(10)
+ssock.bind(('', 1888))
+ssock.listen(5)
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+client = subprocess.Popen(client_args, env=env)
+
+try:
+ (conn, address) = ssock.accept()
+ conn.settimeout(10)
+
+ if mosq_test.expect_packet(conn, "connect", connect_packet):
+ conn.send(connack_packet)
+
+ if mosq_test.expect_packet(conn, "disconnect", disconnect_packet):
+ rc = 0
+
+ conn.close()
+finally:
+ client.terminate()
+ client.wait()
+ ssock.close()
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+import inspect
+import os
+import subprocess
+import sys
+import time
+
+# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
+cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
+if cmd_subfolder not in sys.path:
+ sys.path.insert(0, cmd_subfolder)
+
+rc = 1
+
+client_args = sys.argv[1:]
+env = dict(os.environ)
+env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp'
+try:
+ pp = env['PYTHONPATH']
+except KeyError:
+ pp = ''
+env['PYTHONPATH'] = '../../lib/python:'+pp
+
+client = subprocess.Popen(client_args, env=env)
+client.wait()
+exit(client.returncode)
--- /dev/null
+.PHONY: all test test-compile test-compile-c test-compile-cpp c cpp python python3
+.NOTPARALLEL:
+
+LD_LIBRARY_PATH=../../lib
+
+all :
+
+test : c cpp python python3
+
+test-compile : test-compile-c test-compile-cpp
+
+test-compile-c :
+ $(MAKE) -C c
+
+test-compile-cpp :
+ $(MAKE) -C cpp
+
+c cpp python python3 : test-compile
+ ./01-con-discon-success.py $@/01-con-discon-success.test
+ ./01-will-set.py $@/01-will-set.test
+ ./01-unpwd-set.py $@/01-unpwd-set.test
+ ./01-will-unpwd-set.py $@/01-will-unpwd-set.test
+ ./01-no-clean-session.py $@/01-no-clean-session.test
+ ./01-keepalive-pingreq.py $@/01-keepalive-pingreq.test
+ ./02-subscribe-qos0.py $@/02-subscribe-qos0.test
+ ./02-subscribe-qos1.py $@/02-subscribe-qos1.test
+ ./02-subscribe-qos2.py $@/02-subscribe-qos2.test
+ ./02-unsubscribe.py $@/02-unsubscribe.test
+ ./03-publish-qos0.py $@/03-publish-qos0.test
+ ./03-publish-qos0-no-payload.py $@/03-publish-qos0-no-payload.test
+ ./03-publish-c2b-qos1-timeout.py $@/03-publish-c2b-qos1-timeout.test
+ ./03-publish-c2b-qos1-disconnect.py $@/03-publish-c2b-qos1-disconnect.test
+ ./03-publish-c2b-qos2-timeout.py $@/03-publish-c2b-qos2-timeout.test
+ ./03-publish-c2b-qos2-disconnect.py $@/03-publish-c2b-qos2-disconnect.test
+ ./03-publish-b2c-qos1.py $@/03-publish-b2c-qos1.test
+ ./03-publish-b2c-qos2.py $@/03-publish-b2c-qos2.test
+ ./04-retain-qos0.py $@/04-retain-qos0.test
+ ./08-ssl-connect-no-auth.py $@/08-ssl-connect-no-auth.test
+ ./08-ssl-connect-cert-auth.py $@/08-ssl-connect-cert-auth.test
+ ./08-ssl-bad-cacert.py $@/08-ssl-bad-cacert.test
+ ./09-util-topic-matching.py $@/09-util-topic-matching.test
+
+clean :
+ $(MAKE) -C c clean
+ $(MAKE) -C cpp clean
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.disconnect()
+
+def on_disconnect(mosq, obj, rc):
+ mosq.loop()
+ obj = rc
+
+
+run = -1
+mosq = mosquitto.Mosquitto("01-con-discon-success", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+
+run = -1
+mosq = mosquitto.Mosquitto("01-keepalive-pingreq")
+mosq.on_connect = on_connect
+
+mosq.connect("localhost", 1888, 4)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import mosquitto
+
+mosq = mosquitto.Mosquitto("01-no-clean-session", False)
+
+run = -1
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import mosquitto
+
+mosq = mosquitto.Mosquitto("01-unpwd-set")
+
+run = -1
+mosq.username_pw_set("uname", ";'[08gn=#")
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import mosquitto
+
+mosq = mosquitto.Mosquitto("01-will-set")
+
+run = -1
+mosq.will_set("topic/on/unexpected/disconnect", "will message", 1, True)
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import mosquitto
+
+mosq = mosquitto.Mosquitto("01-will-unpwd-set")
+
+run = -1
+mosq.username_pw_set("oibvvwqw", "#'^2hg9a&nm38*us")
+mosq.will_set("will-topic", "will message", 2, False)
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.subscribe("qos0/test", 0)
+
+def on_disconnect(mosq, obj, rc):
+ obj = rc
+
+def on_subscribe(mosq, obj, mid, granted_qos):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("subscribe-qos0-test", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_subscribe = on_subscribe
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.subscribe("qos1/test", 1)
+
+def on_disconnect(mosq, obj, rc):
+ obj = rc
+
+def on_subscribe(mosq, obj, mid, granted_qos):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("subscribe-qos1-test", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_subscribe = on_subscribe
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.subscribe("qos2/test", 2)
+
+def on_disconnect(mosq, obj, rc):
+ obj = rc
+
+def on_subscribe(mosq, obj, mid, granted_qos):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("subscribe-qos2-test", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_subscribe = on_subscribe
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.unsubscribe("unsubscribe/test")
+
+def on_disconnect(mosq, obj, rc):
+ obj = rc
+
+def on_unsubscribe(mosq, obj, mid):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("unsubscribe-test", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_unsubscribe = on_unsubscribe
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+def on_message(mosq, obj, msg):
+ if msg.mid != 123:
+ print("Invalid mid: ("+str(msg.mid)+")")
+ exit(1)
+ if msg.topic != "pub/qos1/receive":
+ print("Invalid topic: ("+str(msg.topic)+")")
+ exit(1)
+ if msg.payload != "message":
+ print("Invalid payload: ("+str(msg.payload)+")")
+ exit(1)
+ if msg.qos != 1:
+ print("Invalid qos: ("+str(msg.qos)+")")
+ exit(1)
+ if msg.retain != False:
+ print("Invalid retain: ("+str(msg.retain)+")")
+ exit(1)
+ exit(0)
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ print("Connect failed ("+str(rc)+")")
+ exit(rc)
+
+mosq = mosquitto.Mosquitto("publish-qos1-test")
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_message = on_message
+
+mosq.connect("localhost", 1888)
+rc = 0
+while rc == 0:
+ rc = mosq.loop()
+print("rc: "+str(rc))
+exit(1)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_message(mosq, obj, msg):
+ global run
+ if msg.mid != 13423:
+ print("Invalid mid ("+str(msg.mid)+")")
+ exit(1)
+ if msg.topic != "pub/qos2/receive":
+ print("Invalid topic ("+str(msg.topic)+")")
+ exit(1)
+ if msg.payload != "message":
+ print("Invalid payload ("+str(msg.payload)+")")
+ exit(1)
+ if msg.qos != 2:
+ print("Invalid qos ("+str(msg.qos)+")")
+ exit(1)
+ if msg.retain != False:
+ print("Invalid retain ("+str(msg.retain)+")")
+ exit(1)
+
+ run = 0
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos2-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_message = on_message
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1 and rc == 0:
+ rc = mosq.loop(0.3)
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+sent_mid = -1
+
+def on_connect(mosq, obj, rc):
+ global sent_mid
+ if rc != 0:
+ exit(rc)
+ else:
+ if sent_mid == -1:
+ res = mosq.publish("pub/qos1/test", "message", 1)
+ sent_mid = res[1]
+
+def on_disconnect(mosq, obj, rc):
+ if rc == 1:
+ mosq.reconnect()
+ else:
+ run = 0
+
+def on_publish(mosq, obj, mid):
+ global sent_mid
+ if mid == sent_mid:
+ mosq.disconnect()
+ else:
+ exit(1)
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos1-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1:
+ rc = mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+sent_mid = -1
+
+def on_connect(mosq, obj, rc):
+ global sent_mid
+ if rc != 0:
+ exit(rc)
+ else:
+ res = mosq.publish("pub/qos1/test", "message", 1)
+ sent_mid = res[1]
+
+def on_disconnect(mosq, obj, rc):
+ run = 0
+
+def on_publish(mosq, obj, mid):
+ global sent_mid
+ if mid == sent_mid:
+ mosq.disconnect()
+ else:
+ exit(1)
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos1-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1 and rc == 0:
+ rc = mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+first_connection = 1
+
+def on_connect(mosq, obj, rc):
+ global first_connection
+ if rc != 0:
+ exit(rc)
+ else:
+ if first_connection == 1:
+ mosq.publish("pub/qos2/test", "message", 2)
+ first_connection = 0
+
+def on_disconnect(mosq, obj, rc):
+ if rc == 1:
+ mosq.reconnect()
+ else:
+ run = 0
+
+def on_publish(mosq, obj, mid):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos2-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1:
+ rc = mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.publish("pub/qos2/test", "message", 2)
+
+def on_disconnect(mosq, obj, rc):
+ run = 0
+
+def on_publish(mosq, obj, mid):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos2-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1 and rc == 0:
+ rc = mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+sent_mid = -1
+
+def on_connect(mosq, obj, rc):
+ global sent_mid
+ if rc != 0:
+ exit(rc)
+ else:
+ (res, sent_mid) = mosq.publish("pub/qos0/no-payload/test")
+
+def on_publish(mosq, obj, mid):
+ global sent_mid, run
+ if sent_mid == mid:
+ mosq.disconnect()
+ run = 0
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos0-test-np", run)
+mosq.on_connect = on_connect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+sent_mid = -1
+
+def on_connect(mosq, obj, rc):
+ global sent_mid
+ if rc != 0:
+ exit(rc)
+ else:
+ res = mosq.publish("pub/qos0/test", "message")
+ sent_mid = res[1]
+
+def on_publish(mosq, obj, mid):
+ global sent_mid, run
+ if sent_mid == mid:
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos0-test", run)
+mosq.on_connect = on_connect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.publish("retain/qos0/test", "retained message", 0, True)
+
+run = -1
+mosq = mosquitto.Mosquitto("retain-qos0-test", run)
+mosq.on_connect = on_connect
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+if sys.version < '2.7':
+ print("WARNING: SSL/TLS not supported on Python 2.6")
+ exit(0)
+
+rc = 1
+mosq = mosquitto.Mosquitto("08-ssl-bad-cacert")
+try:
+ mosq.tls_set("this/file/doesnt/exist")
+except IOError as err:
+ rc = 0
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+if sys.version < '2.7':
+ print("WARNING: SSL/TLS not supported on Python 2.6")
+ exit(0)
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.disconnect()
+
+def on_disconnect(mosq, obj, rc):
+ mosq.loop()
+ obj = rc
+
+
+run = -1
+mosq = mosquitto.Mosquitto("08-ssl-connect-crt-auth", run)
+mosq.tls_set("../ssl/test-ca.crt", "../ssl/client.crt", "../ssl/client.key")
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+if sys.version < '2.7':
+ print("WARNING: SSL/TLS not supported on Python 2.6")
+ exit(0)
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.disconnect()
+
+def on_disconnect(mosq, obj, rc):
+ mosq.loop()
+ obj = rc
+
+
+run = -1
+mosq = mosquitto.Mosquitto("08-ssl-connect-no-auth", run)
+mosq.tls_set("../ssl/test-ca.crt")
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+import ssl
+
+import mosquitto
+
+if sys.version < '2.7':
+ print("WARNING: SSL/TLS not supported on Python 2.6")
+ exit(0)
+
+def on_connect(mosq, obj, rc):
+ exit(1)
+
+mosq = mosquitto.Mosquitto("08-ssl-fake-cacert")
+mosq.tls_set("../ssl/fake-ca.crt", "../ssl/client.crt", "../ssl/client.key")
+mosq.on_connect = on_connect
+
+try:
+ mosq.connect("localhost", 1888)
+except ssl.SSLError as msg:
+ if msg.errno == 1 and "certificate verify failed" in msg.strerror:
+ exit(0)
+ else:
+ exit(1)
+else:
+ exit(1)
--- /dev/null
+#!/usr/bin/python
+
+# Copyright (c) 2012 Roger Light <roger@atchoo.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of mosquitto nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import mosquitto
+import sys
+
+if mosquitto.topic_matches_sub("foo/bar", "foo/bar") == False:
+ print("ERROR: foo/bar : foo/bar")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+", "foo/bar") == False:
+ print("ERROR: foo/+ : foo/bar")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+/baz", "foo/bar/baz") == False:
+ print("ERROR: foo/+/baz : foo/bar/baz")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+/#", "foo/bar/baz") == False:
+ print("ERROR: foo/+/# : foo/bar/baz")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("#", "foo/bar/baz") == False:
+ print("ERROR: # : foo/bar/baz")
+ sys.exit(1)
+
+if mosquitto.topic_matches_sub("foo/bar", "foo") == True:
+ print("ERROR: foo/bar : foo")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+", "foo/bar/baz") == True:
+ print("ERROR: foo/+ : foo/bar/baz")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+/baz", "foo/bar/bar") == True:
+ print("ERROR: foo/+/baz : foo/bar/bar")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+/#", "fo2/bar/baz") == True:
+ print("ERROR: foo/+/# : foo/bar/baz")
+ sys.exit(1)
+
+sys.exit(0)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.disconnect()
+
+def on_disconnect(mosq, obj, rc):
+ mosq.loop()
+ obj = rc
+
+
+run = -1
+mosq = mosquitto.Mosquitto("01-con-discon-success", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+
+run = -1
+mosq = mosquitto.Mosquitto("01-keepalive-pingreq")
+mosq.on_connect = on_connect
+
+mosq.connect("localhost", 1888, 4)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import mosquitto
+
+mosq = mosquitto.Mosquitto("01-no-clean-session", False)
+
+run = -1
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import mosquitto
+
+mosq = mosquitto.Mosquitto("01-unpwd-set")
+
+run = -1
+mosq.username_pw_set("uname", ";'[08gn=#")
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import mosquitto
+
+mosq = mosquitto.Mosquitto("01-will-set")
+
+run = -1
+mosq.will_set("topic/on/unexpected/disconnect", "will message", 1, True)
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import mosquitto
+
+mosq = mosquitto.Mosquitto("01-will-unpwd-set")
+
+run = -1
+mosq.username_pw_set("oibvvwqw", "#'^2hg9a&nm38*us")
+mosq.will_set("will-topic", "will message", 2, False)
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.subscribe("qos0/test", 0)
+
+def on_disconnect(mosq, obj, rc):
+ obj = rc
+
+def on_subscribe(mosq, obj, mid, granted_qos):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("subscribe-qos0-test", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_subscribe = on_subscribe
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.subscribe("qos1/test", 1)
+
+def on_disconnect(mosq, obj, rc):
+ obj = rc
+
+def on_subscribe(mosq, obj, mid, granted_qos):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("subscribe-qos1-test", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_subscribe = on_subscribe
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.subscribe("qos2/test", 2)
+
+def on_disconnect(mosq, obj, rc):
+ obj = rc
+
+def on_subscribe(mosq, obj, mid, granted_qos):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("subscribe-qos2-test", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_subscribe = on_subscribe
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.unsubscribe("unsubscribe/test")
+
+def on_disconnect(mosq, obj, rc):
+ obj = rc
+
+def on_unsubscribe(mosq, obj, mid):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("unsubscribe-test", run)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_unsubscribe = on_unsubscribe
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+def on_message(mosq, obj, msg):
+ if msg.mid != 123:
+ print("Invalid mid: ("+str(msg.mid)+")")
+ exit(1)
+ if msg.topic != "pub/qos1/receive":
+ print("Invalid topic: ("+str(msg.topic)+")")
+ exit(1)
+ if msg.payload != b"message":
+ print("Invalid payload: ("+str(msg.payload)+")")
+ exit(1)
+ if msg.qos != 1:
+ print("Invalid qos: ("+str(msg.qos)+")")
+ exit(1)
+ if msg.retain != False:
+ print("Invalid retain: ("+str(msg.retain)+")")
+ exit(1)
+ exit(0)
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ print("Connect failed ("+str(rc)+")")
+ exit(rc)
+
+mosq = mosquitto.Mosquitto("publish-qos1-test")
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_message = on_message
+
+mosq.connect("localhost", 1888)
+rc = 0
+while rc == 0:
+ rc = mosq.loop()
+print("rc: "+str(rc))
+exit(1)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_message(mosq, obj, msg):
+ global run
+ if msg.mid != 13423:
+ print("Invalid mid ("+str(msg.mid)+")")
+ exit(1)
+ if msg.topic != "pub/qos2/receive":
+ print("Invalid topic ("+str(msg.topic)+")")
+ exit(1)
+ if msg.payload != b"message":
+ print("Invalid payload ("+str(msg.payload)+")")
+ exit(1)
+ if msg.qos != 2:
+ print("Invalid qos ("+str(msg.qos)+")")
+ exit(1)
+ if msg.retain != False:
+ print("Invalid retain ("+str(msg.retain)+")")
+ exit(1)
+
+ run = 0
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos2-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_message = on_message
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1 and rc == 0:
+ rc = mosq.loop(0.3)
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+sent_mid = -1
+
+def on_connect(mosq, obj, rc):
+ global sent_mid
+ if rc != 0:
+ exit(rc)
+ else:
+ if sent_mid == -1:
+ res = mosq.publish("pub/qos1/test", "message", 1)
+ sent_mid = res[1]
+
+def on_disconnect(mosq, obj, rc):
+ if rc == 1:
+ mosq.reconnect()
+ else:
+ run = 0
+
+def on_publish(mosq, obj, mid):
+ global sent_mid
+ if mid == sent_mid:
+ mosq.disconnect()
+ else:
+ exit(1)
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos1-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1:
+ rc = mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+sent_mid = -1
+
+def on_connect(mosq, obj, rc):
+ global sent_mid
+ if rc != 0:
+ exit(rc)
+ else:
+ res = mosq.publish("pub/qos1/test", "message", 1)
+ sent_mid = res[1]
+
+def on_disconnect(mosq, obj, rc):
+ run = 0
+
+def on_publish(mosq, obj, mid):
+ global sent_mid
+ if mid == sent_mid:
+ mosq.disconnect()
+ else:
+ exit(1)
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos1-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1 and rc == 0:
+ rc = mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+first_connection = 1
+
+def on_connect(mosq, obj, rc):
+ global first_connection
+ if rc != 0:
+ exit(rc)
+ else:
+ if first_connection == 1:
+ mosq.publish("pub/qos2/test", "message", 2)
+ first_connection = 0
+
+def on_disconnect(mosq, obj, rc):
+ if rc == 1:
+ mosq.reconnect()
+ else:
+ run = 0
+
+def on_publish(mosq, obj, mid):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos2-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1:
+ rc = mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.publish("pub/qos2/test", "message", 2)
+
+def on_disconnect(mosq, obj, rc):
+ run = 0
+
+def on_publish(mosq, obj, mid):
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos2-test", run)
+mosq.message_retry_set(3)
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+rc = 0
+while run == -1 and rc == 0:
+ rc = mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+sent_mid = -1
+
+def on_connect(mosq, obj, rc):
+ global sent_mid
+ if rc != 0:
+ exit(rc)
+ else:
+ (res, sent_mid) = mosq.publish("pub/qos0/no-payload/test")
+
+def on_publish(mosq, obj, mid):
+ global sent_mid, run
+ if sent_mid == mid:
+ mosq.disconnect()
+ run = 0
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos0-test-np", run)
+mosq.on_connect = on_connect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+sent_mid = -1
+
+def on_connect(mosq, obj, rc):
+ global sent_mid
+ if rc != 0:
+ exit(rc)
+ else:
+ res = mosq.publish("pub/qos0/test", "message")
+ sent_mid = res[1]
+
+def on_publish(mosq, obj, mid):
+ global sent_mid, run
+ if sent_mid == mid:
+ mosq.disconnect()
+
+run = -1
+mosq = mosquitto.Mosquitto("publish-qos0-test", run)
+mosq.on_connect = on_connect
+mosq.on_publish = on_publish
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.publish("retain/qos0/test", "retained message", 0, True)
+
+run = -1
+mosq = mosquitto.Mosquitto("retain-qos0-test", run)
+mosq.on_connect = on_connect
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+if sys.version < '2.7':
+ print("WARNING: SSL/TLS not supported on Python 2.6")
+ exit(0)
+
+rc = 1
+mosq = mosquitto.Mosquitto("08-ssl-bad-cacert")
+try:
+ mosq.tls_set("this/file/doesnt/exist")
+except IOError as err:
+ rc = 0
+
+exit(rc)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.disconnect()
+
+def on_disconnect(mosq, obj, rc):
+ mosq.loop()
+ obj = rc
+
+
+run = -1
+mosq = mosquitto.Mosquitto("08-ssl-connect-crt-auth", run)
+mosq.tls_set("../ssl/test-ca.crt", "../ssl/client.crt", "../ssl/client.key")
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+
+import mosquitto
+
+
+def on_connect(mosq, obj, rc):
+ if rc != 0:
+ exit(rc)
+ else:
+ mosq.disconnect()
+
+def on_disconnect(mosq, obj, rc):
+ mosq.loop()
+ obj = rc
+
+
+run = -1
+mosq = mosquitto.Mosquitto("08-ssl-connect-no-auth", run)
+mosq.tls_set("../ssl/test-ca.crt")
+mosq.on_connect = on_connect
+mosq.on_disconnect = on_disconnect
+
+mosq.connect("localhost", 1888)
+while run == -1:
+ mosq.loop()
+
+exit(run)
--- /dev/null
+#!/usr/bin/python3
+
+import os
+import subprocess
+import socket
+import sys
+import time
+from struct import *
+import ssl
+
+import mosquitto
+
+if sys.version < '2.7':
+ print("WARNING: SSL/TLS not supported on Python 2.6")
+ exit(0)
+
+def on_connect(mosq, obj, rc):
+ exit(1)
+
+mosq = mosquitto.Mosquitto("08-ssl-fake-cacert")
+mosq.tls_set("../ssl/fake-ca.crt", "../ssl/client.crt", "../ssl/client.key")
+mosq.on_connect = on_connect
+
+try:
+ mosq.connect("localhost", 1888)
+except ssl.SSLError as msg:
+ if msg.errno == 1 and "certificate verify failed" in msg.strerror:
+ exit(0)
+ else:
+ exit(1)
+else:
+ exit(1)
--- /dev/null
+#!/usr/bin/python
+
+# Copyright (c) 2012 Roger Light <roger@atchoo.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of mosquitto nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import mosquitto
+import sys
+
+if mosquitto.topic_matches_sub("foo/bar", "foo/bar") == False:
+ print("ERROR: foo/bar : foo/bar")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+", "foo/bar") == False:
+ print("ERROR: foo/+ : foo/bar")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+/baz", "foo/bar/baz") == False:
+ print("ERROR: foo/+/baz : foo/bar/baz")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+/#", "foo/bar/baz") == False:
+ print("ERROR: foo/+/# : foo/bar/baz")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("#", "foo/bar/baz") == False:
+ print("ERROR: # : foo/bar/baz")
+ sys.exit(1)
+
+if mosquitto.topic_matches_sub("foo/bar", "foo") == True:
+ print("ERROR: foo/bar : foo")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+", "foo/bar/baz") == True:
+ print("ERROR: foo/+ : foo/bar/baz")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+/baz", "foo/bar/bar") == True:
+ print("ERROR: foo/+/baz : foo/bar/bar")
+ sys.exit(1)
+if mosquitto.topic_matches_sub("foo/+/#", "fo2/bar/baz") == True:
+ print("ERROR: foo/+/# : foo/bar/baz")
+ sys.exit(1)
+
+sys.exit(0)
--- /dev/null
+import struct
+
+def expect_packet(sock, name, expected):
+ if len(expected) > 0:
+ rlen = len(expected)
+ else:
+ rlen = 1
+
+ packet_recvd = sock.recv(rlen)
+ return packet_matches(name, packet_recvd, expected)
+
+def packet_matches(name, recvd, expected):
+ if recvd != expected:
+ print("FAIL: Received incorrect "+name+".")
+ try:
+ print("Received: "+to_string(recvd))
+ except struct.error:
+ print("Received (not decoded): "+recvd)
+ try:
+ print("Expected: "+to_string(expected))
+ except struct.error:
+ print("Expected (not decoded): "+expected)
+
+ return 0
+ else:
+ return 1
+
+def remaining_length(packet):
+ l = min(5, len(packet))
+ all_bytes = struct.unpack("!"+"B"*l, packet[:l])
+ mult = 1
+ rl = 0
+ for i in range(1,l-1):
+ byte = all_bytes[i]
+
+ rl += (byte & 127) * mult
+ mult *= 128
+ if byte & 128 == 0:
+ packet = packet[i+1:]
+ break
+
+ return (packet, rl)
+
+
+def to_string(packet):
+ if len(packet) == 0:
+ return ""
+
+ packet0 = struct.unpack("!B", packet[0])
+ packet0 = packet0[0]
+ cmd = packet0 & 0xF0
+ if cmd == 0x00:
+ # Reserved
+ return "0x00"
+ elif cmd == 0x10:
+ # CONNECT
+ (packet, rl) = remaining_length(packet)
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (slen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(slen)+'sBBH' + str(len(packet)-slen-4) + 's'
+ (protocol, proto_ver, flags, keepalive, packet) = struct.unpack(pack_format, packet)
+ s = "CONNECT, proto="+protocol+str(proto_ver)+", keepalive="+str(keepalive)
+ if flags&2:
+ s = s+", clean-session"
+ else:
+ s = s+", durable"
+
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (slen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's'
+ (client_id, packet) = struct.unpack(pack_format, packet)
+ s = s+", id="+client_id
+
+ if flags&4:
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (slen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's'
+ (will_topic, packet) = struct.unpack(pack_format, packet)
+ s = s+", will-topic="+will_topic
+
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (slen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's'
+ (will_message, packet) = struct.unpack(pack_format, packet)
+ s = s+", will-message="+will_message
+
+ s = s+", will-qos="+str((flags&24)>>3)
+ s = s+", will-retain="+str((flags&32)>>5)
+
+ if flags&128:
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (slen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's'
+ (username, packet) = struct.unpack(pack_format, packet)
+ s = s+", username="+username
+
+ if flags&64:
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (slen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's'
+ (password, packet) = struct.unpack(pack_format, packet)
+ s = s+", password="+password
+
+ return s
+ elif cmd == 0x20:
+ # CONNACK
+ (cmd, rl, resv, rc) = struct.unpack('!BBBB', packet)
+ return "CONNACK, rl="+str(rl)+", res="+str(resv)+", rc="+str(rc)
+ elif cmd == 0x30:
+ # PUBLISH
+ dup = (packet0 & 0x08)>>3
+ qos = (packet0 & 0x06)>>1
+ retain = (packet0 & 0x01)
+ (packet, rl) = remaining_length(packet)
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (tlen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(tlen)+'s' + str(len(packet)-tlen) + 's'
+ (topic, packet) = struct.unpack(pack_format, packet)
+ s = "PUBLISH, rl="+str(rl)+", topic="+topic+", qos="+str(qos)+", retain="+str(retain)+", dup="+str(dup)
+ if qos > 0:
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (mid, packet) = struct.unpack(pack_format, packet)
+ s = s + ", mid="+str(mid)
+
+ s = s + ", payload="+packet
+ return s
+ elif cmd == 0x40:
+ # PUBACK
+ (cmd, rl, mid) = struct.unpack('!BBH', packet)
+ return "PUBACK, rl="+str(rl)+", mid="+str(mid)
+ elif cmd == 0x50:
+ # PUBREC
+ (cmd, rl, mid) = struct.unpack('!BBH', packet)
+ return "PUBREC, rl="+str(rl)+", mid="+str(mid)
+ elif cmd == 0x60:
+ # PUBREL
+ dup = (packet0 & 0x08)>>3
+ (cmd, rl, mid) = struct.unpack('!BBH', packet)
+ return "PUBREL, rl="+str(rl)+", mid="+str(mid)+", dup="+str(dup)
+ elif cmd == 0x70:
+ # PUBCOMP
+ (cmd, rl, mid) = struct.unpack('!BBH', packet)
+ return "PUBCOMP, rl="+str(rl)+", mid="+str(mid)
+ elif cmd == 0x80:
+ # SUBSCRIBE
+ (packet, rl) = remaining_length(packet)
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (mid, packet) = struct.unpack(pack_format, packet)
+ s = "SUBSCRIBE, rl="+str(rl)+", mid="+str(mid)
+ topic_index = 0
+ while len(packet) > 0:
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (tlen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(tlen)+'sB' + str(len(packet)-tlen-1) + 's'
+ (topic, qos, packet) = struct.unpack(pack_format, packet)
+ s = s + ", topic"+str(topic_index)+"="+topic+","+str(qos)
+ return s
+ elif cmd == 0x90:
+ # SUBACK
+ (packet, rl) = remaining_length(packet)
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (mid, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + "B"*len(packet)
+ granted_qos = struct.unpack(pack_format, packet)
+
+ s = "SUBACK, rl="+str(rl)+", mid="+str(mid)+", granted_qos="+str(granted_qos[0])
+ for i in range(1, len(granted_qos)-1):
+ s = s+", "+str(granted_qos[i])
+ return s
+ elif cmd == 0xA0:
+ # UNSUBSCRIBE
+ (packet, rl) = remaining_length(packet)
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (mid, packet) = struct.unpack(pack_format, packet)
+ s = "UNSUBSCRIBE, rl="+str(rl)+", mid="+str(mid)
+ topic_index = 0
+ while len(packet) > 0:
+ pack_format = "!H" + str(len(packet)-2) + 's'
+ (tlen, packet) = struct.unpack(pack_format, packet)
+ pack_format = "!" + str(tlen)+'s' + str(len(packet)-tlen) + 's'
+ (topic, packet) = struct.unpack(pack_format, packet)
+ s = s + ", topic"+str(topic_index)+"="+topic
+ return s
+ elif cmd == 0xB0:
+ # UNSUBACK
+ (cmd, rl, mid) = struct.unpack('!BBH', packet)
+ return "UNSUBACK, rl="+str(rl)+", mid="+str(mid)
+ elif cmd == 0xC0:
+ # PINGREQ
+ (cmd, rl) = struct.unpack('!BB', packet)
+ return "PINGREQ, rl="+str(rl)
+ elif cmd == 0xD0:
+ # PINGRESP
+ (cmd, rl) = struct.unpack('!BB', packet)
+ return "PINGRESP, rl="+str(rl)
+ elif cmd == 0xE0:
+ # DISCONNECT
+ (cmd, rl) = struct.unpack('!BB', packet)
+ return "DISCONNECT, rl="+str(rl)
+ elif cmd == 0xF0:
+ # Reserved
+ return "0xF0"
+
+def gen_connect(client_id, clean_session=True, keepalive=60, username=None, password=None, will_topic=None, will_qos=0, will_retain=False, will_payload="", proto_ver=3):
+ if client_id == None:
+ remaining_length = 12
+ else:
+ remaining_length = 12 + 2+len(client_id)
+ connect_flags = 0
+ if clean_session:
+ connect_flags = connect_flags | 0x02
+
+ if will_topic != None:
+ remaining_length = remaining_length + 2+len(will_topic) + 2+len(will_payload)
+ connect_flags = connect_flags | 0x04 | ((will_qos&0x03) << 3)
+ if will_retain:
+ connect_flags = connect_flags | 32
+
+ if username != None:
+ remaining_length = remaining_length + 2+len(username)
+ connect_flags = connect_flags | 0x80
+ if password != None:
+ connect_flags = connect_flags | 0x40
+ remaining_length = remaining_length + 2+len(password)
+
+ rl = pack_remaining_length(remaining_length)
+ packet = struct.pack("!B"+str(len(rl))+"s", 0x10, rl)
+ packet = packet + struct.pack("!H6sBBH", len("MQIsdp"), "MQIsdp", proto_ver, connect_flags, keepalive)
+ if client_id != None:
+ packet = packet + struct.pack("!H"+str(len(client_id))+"s", len(client_id), client_id)
+
+ if will_topic != None:
+ packet = packet + struct.pack("!H"+str(len(will_topic))+"s", len(will_topic), will_topic)
+ if len(will_payload) > 0:
+ packet = packet + struct.pack("!H"+str(len(will_payload))+"s", len(will_payload), will_payload)
+ else:
+ packet = packet + struct.pack("!H", 0)
+
+ if username != None:
+ packet = packet + struct.pack("!H"+str(len(username))+"s", len(username), username)
+ if password != None:
+ packet = packet + struct.pack("!H"+str(len(password))+"s", len(password), password)
+ return packet
+
+def gen_connack(resv=0, rc=0):
+ return struct.pack('!BBBB', 32, 2, resv, rc);
+
+def gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0):
+ rl = 2+len(topic)
+ pack_format = "!BBH"+str(len(topic))+"s"
+ if qos > 0:
+ rl = rl + 2
+ pack_format = pack_format + "H"
+ if payload != None:
+ rl = rl + len(payload)
+ pack_format = pack_format + str(len(payload))+"s"
+ else:
+ payload = ""
+ pack_format = pack_format + "0s"
+
+ cmd = 48 | (qos<<1)
+ if retain:
+ cmd = cmd + 1
+ if dup:
+ cmd = cmd + 8
+
+ if qos > 0:
+ return struct.pack(pack_format, cmd, rl, len(topic), topic, mid, payload)
+ else:
+ return struct.pack(pack_format, cmd, rl, len(topic), topic, payload)
+
+def gen_puback(mid):
+ return struct.pack('!BBH', 64, 2, mid)
+
+def gen_pubrec(mid):
+ return struct.pack('!BBH', 80, 2, mid)
+
+def gen_pubrel(mid, dup=False):
+ if dup:
+ cmd = 96+8+2
+ else:
+ cmd = 96+2
+ return struct.pack('!BBH', cmd, 2, mid)
+
+def gen_pubcomp(mid):
+ return struct.pack('!BBH', 112, 2, mid)
+
+def gen_subscribe(mid, topic, qos):
+ pack_format = "!BBHH"+str(len(topic))+"sB"
+ return struct.pack(pack_format, 130, 2+2+len(topic)+1, mid, len(topic), topic, qos)
+
+def gen_suback(mid, qos):
+ return struct.pack('!BBHB', 144, 2+1, mid, qos)
+
+def gen_unsubscribe(mid, topic):
+ pack_format = "!BBHH"+str(len(topic))+"s"
+ return struct.pack(pack_format, 162, 2+2+len(topic), mid, len(topic), topic)
+
+def gen_unsuback(mid):
+ return struct.pack('!BBH', 176, 2, mid)
+
+def gen_pingreq():
+ return struct.pack('!BB', 192, 0)
+
+def gen_pingresp():
+ return struct.pack('!BB', 208, 0)
+
+def gen_disconnect():
+ return struct.pack('!BB', 224, 0)
+
+def pack_remaining_length(remaining_length):
+ s = ""
+ while True:
+ 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
+
+ s = s + struct.pack("!B", byte)
+ if remaining_length == 0:
+ return s
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICpDCCAg0CCQDNrg5WSiiRqDANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMC
+R0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEdMBsG
+A1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxDzANBgNVBAsMBkJyb2tlcjEXMBUG
+A1UEAwwOYnJva2VyLXRlc3QtY2ExHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBs
+ZS5jb20wHhcNMTIwNzAzMTQ0MDA3WhcNMTIwNzA0MTQ0MDA3WjCBizELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEd
+MBsGA1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxFDASBgNVBAsMC0Jyb2tlciBU
+ZXN0MR4wHAYDVQQDDBVsb2NhbGhvc3QtY2xpZW50LXRlc3QwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBAM1kzx27D7TooydBNi6tTIMvstPZqFviwN5JGCKxs0wI
+ZAdVP1HNeECioww4HiMO6J6IfcLppLDuWr1pteSG471MjGLPc0Z6UKNejKntM30p
+7649fBZ9DIomVKTJHya/jtU8hIJfSpY29FKGOe3gVjg99nFZtB1dGnycQys5FRVZ
+AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEACkRM53UdHTXhDNv+xZk6DJyNneEm0BWG
+u9IExujL1YKbEIxlOncacDEzAYe3YbcnzN+1rgYel2l8Oq7Esb4OhlM5ftzfNLw5
+p2uM1QWzB1N1pwJ7BMtlkFuL+JI+VikaVdqvV9YhCDxIUUujoMsXLYzVOVMQbToJ
+YedFIjsMj6s=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICpDCCAg0CCQDNrg5WSiiRpzANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMC
+R0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEdMBsG
+A1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxDzANBgNVBAsMBkJyb2tlcjEXMBUG
+A1UEAwwOYnJva2VyLXRlc3QtY2ExHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBs
+ZS5jb20wHhcNMTIwNzAzMTQ0MDAwWhcNMzkxMTE4MTQ0MDAwWjCBizELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEd
+MBsGA1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxFDASBgNVBAsMC0Jyb2tlciBU
+ZXN0MR4wHAYDVQQDDBVsb2NhbGhvc3QtY2xpZW50LXRlc3QwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBAM1kzx27D7TooydBNi6tTIMvstPZqFviwN5JGCKxs0wI
+ZAdVP1HNeECioww4HiMO6J6IfcLppLDuWr1pteSG471MjGLPc0Z6UKNejKntM30p
+7649fBZ9DIomVKTJHya/jtU8hIJfSpY29FKGOe3gVjg99nFZtB1dGnycQys5FRVZ
+AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAXB40zdyBFQ7BjDbDFV/vcx4E2rpVxnQ4
+vtJ8TE7aaBqS9QmxlWYnx8ys/q51mVmOxbA/aIFllaSyR+P0MrgZfbWFtb/PK2IV
+VnCciP7dfwqbnsW3ziRUq+mTaaNDPtT+YJrLJyTYNZPRvGIBHOt0NKzNCyvO37v3
+op7ELGt0I+E=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBzDCCATUCAQAwgYsxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2lu
+Z2RvbTEOMAwGA1UEBwwFRGVyYnkxHTAbBgNVBAoMFE1vc3F1aXR0byBUZXN0IFN1
+aXRlMRQwEgYDVQQLDAtCcm9rZXIgVGVzdDEeMBwGA1UEAwwVbG9jYWxob3N0LWNs
+aWVudC10ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNZM8duw+06KMn
+QTYurUyDL7LT2ahb4sDeSRgisbNMCGQHVT9RzXhAoqMMOB4jDuieiH3C6aSw7lq9
+abXkhuO9TIxiz3NGelCjXoyp7TN9Ke+uPXwWfQyKJlSkyR8mv47VPISCX0qWNvRS
+hjnt4FY4PfZxWbQdXRp8nEMrORUVWQIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEA
+uzoEdsl3JkiNIviQzDtr67k6L/vogtVSgRnCFCel16Q0i1W+mVAwUqYArwf/7fwp
+UhZPd8NLSVT1pn4Nj2a2Q7S3GMpMguiQlhCol7hZOrNpc0fDGg1JSmYjCEmm3TOl
+Z49eyqmE8r4xdDEAyPk3u21HYrZ5RshyS/8vfPwqR9o=
+-----END CERTIFICATE REQUEST-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDNZM8duw+06KMnQTYurUyDL7LT2ahb4sDeSRgisbNMCGQHVT9R
+zXhAoqMMOB4jDuieiH3C6aSw7lq9abXkhuO9TIxiz3NGelCjXoyp7TN9Ke+uPXwW
+fQyKJlSkyR8mv47VPISCX0qWNvRShjnt4FY4PfZxWbQdXRp8nEMrORUVWQIDAQAB
+AoGAdcKxuUMSG1AykwQhk5uKvcBwUGR/0cbte8T+0I1/1j0NVOL8feNHag+VWiEm
+rkUS/CoXqNQat9LBNc5RGmh4U35orG2xi/EqcBnp/Mse2UqnOTYO3xjeP+JQBtR9
+EiutMTabnaOIXox2bfb3olKA5b6phTt9Y0v8Li/jbVAhw3ECQQDslDu6ZvkKoljU
+VICOteQMMPESsrXVPs5brtxyK2LQn+GBwXvy7d655Ql9jUkyops546aTB6JgYOMs
+zDD3oJ7FAkEA3kE1wwap7NxtYSEbtwyIa7r+IKezG9IPwG27EHjTjPBgclOk0ZOf
+W51ZD/CYNbA7fYAbqREeBwzhe5u0jfHFhQJBAOccL/T6nxMqYYibPDMtsSfPr9FK
+T6OQBVs/SQ8nHxMa/NsbPpCkm04SVuEV4onam7VDlPhRHujz/TlICBYADNkCQEaA
+XwJ3ea2mGphF/VmqgxfRYE2RhNJdZxu+cyl9enXpxl5dxBmq/1D7b8YLpuzY83YT
+DjMqN+E6p8gjEzo3qFUCQGaSni6qTT9pT22uT3QwLthOPdVacV6a55Ci6g4XaFUR
+/Es/nQdkZTbCI1ufGV2Usodsqas+lNGqnClGVHqcUg8=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICxjCCAi8CCQDNrg5WSiiRqjANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMC
+R0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEdMBsG
+A1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxDzANBgNVBAsMBkJyb2tlcjEXMBUG
+A1UEAwwOYnJva2VyLXRlc3QtY2ExHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBs
+ZS5jb20wHhcNMTMwMjIyMjEzMDIyWhcNMjMwMjIwMjEzMDIyWjCBrTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEd
+MBsGA1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxFDASBgNVBAsMC0Jyb2tlciBU
+ZXN0MR4wHAYDVQQDDBVsb2NhbGhvc3Qtbm9uLXJldm9rZWQxIDAeBgkqhkiG9w0B
+CQEWEXRlc3QxQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQClAM49bIqhRQdDO+0Dtomi1EeH43ECWq+r9747kQ5RUJyA1m4mNHV6nQR+HMzc
+amFd7XVuP4o5AAO5Q0KeyXRNVfvrljk39XqbkzmXeaTxNiMzk01MsFYlDP7i8Xt/
+IvAQ6J39UN8lzY/SdqW55nAfMZmoQCIP3h+gvt4DHjAyRQIDAQABMA0GCSqGSIb3
+DQEBBQUAA4GBAEhL1QRF2mrpbErgPK4zX+2UXKMQQVon2XHvI9iETk7O/e7yYSWB
+N9V7oS4rMDEo6qebnnjtBRJ/bDtSkHm+I1y4Xg3nPUTPeYpvtyTzcLORr7rV336O
+05xmaOCeRBZXKohcCvdbV8KA8/EjC3BI04S4pkehjgARpY/DL026VbGp
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE REQUEST-----
+MIIB7jCCAVcCAQAwga0xCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2lu
+Z2RvbTEOMAwGA1UEBwwFRGVyYnkxHTAbBgNVBAoMFE1vc3F1aXR0byBUZXN0IFN1
+aXRlMRQwEgYDVQQLDAtCcm9rZXIgVGVzdDEeMBwGA1UEAwwVbG9jYWxob3N0LW5v
+bi1yZXZva2VkMSAwHgYJKoZIhvcNAQkBFhF0ZXN0MUBleGFtcGxlLmNvbTCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApQDOPWyKoUUHQzvtA7aJotRHh+NxAlqv
+q/e+O5EOUVCcgNZuJjR1ep0EfhzM3GphXe11bj+KOQADuUNCnsl0TVX765Y5N/V6
+m5M5l3mk8TYjM5NNTLBWJQz+4vF7fyLwEOid/VDfJc2P0nalueZwHzGZqEAiD94f
+oL7eAx4wMkUCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBADIUaGKO+LK8MDXTGQjZ
+Wr5wpJC67xgjXSdaMcx6wHicrd8wKoeKBk2ibbbNJIcIcYgdPmmNp96C0RacmtlM
+dL9sIIVVO/vzsV3SVozVHZZLLtvByYV3KZu/bXpEwSa8LOK35fBNkHS2/5DJhwpR
+E6pFDre+HBoEUM+h1ZKLb4Ba
+-----END CERTIFICATE REQUEST-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQClAM49bIqhRQdDO+0Dtomi1EeH43ECWq+r9747kQ5RUJyA1m4m
+NHV6nQR+HMzcamFd7XVuP4o5AAO5Q0KeyXRNVfvrljk39XqbkzmXeaTxNiMzk01M
+sFYlDP7i8Xt/IvAQ6J39UN8lzY/SdqW55nAfMZmoQCIP3h+gvt4DHjAyRQIDAQAB
+AoGAOgsUgcsC4UQ1D9OuxpWZySu/le/OOzPHhEj8JFwcm0IuK6xCxzHA+cbuY6ah
+8g+B/NahbozvhKmapbshXlYjkNgWI0OY6HcLGKeW8gTduIEC69TPTRdTiZY6INhi
+vsdvwkIUnzaSkm0e+NHBWY3HgcT4ziGqsb2kIQujVcRV5IECQQDY15UmhMg9ujmQ
+epIAuWquIWruTCoeD/Qsm74RCvdJq8CZQmZ3TQisQn9hM2LJ98lpcGj+5AktaQdc
+8/QLzucxAkEAwszA7h1wRgaFeG9GMM+pGiWTKz5dmF/mA3VBJIk8bQP6eZx4Asrj
+8i6LPdeqBqvopJOMSqv89YohMQGmscafVQJADslcpVzGntb1F/ITxLLgIHGs7gUJ
+ljU2TFhudJEP3dk1b/4EKLx7C+wY7ZLoeKdJ98OvlToNmiggVWlZSfjY8QJAOpyv
+BDZZXMiDilw4y2EAKXzazi0irkFUjP/PzPV59/c1ezfoKDbx5SG6Ba6JWx6IjlRS
+5vje0OZx3DP5+w2fTQJAAYd/wwaA9M9hIrL4LXmnznZcoHgfQ6ImNoi5TcMoZoNk
+z7PaMNx+26Q+gJ1+/ILn6mdDUHVl2j1Xg6piuVOZQw==
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN X509 CRL-----
+MIIBmDCCAQECAQEwDQYJKoZIhvcNAQEFBQAwgaAxCzAJBgNVBAYTAkdCMRcwFQYD
+VQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwGA1UEBwwFRGVyYnkxHTAbBgNVBAoMFE1v
+c3F1aXR0byBUZXN0IFN1aXRlMQ8wDQYDVQQLDAZCcm9rZXIxFzAVBgNVBAMMDmJy
+b2tlci10ZXN0LWNhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tFw0x
+MzAyMjIyMTE0MjhaFw0yMzAyMjAyMTE0MjhaMBwwGgIJAM2uDlZKKJGnFw0xMjA3
+MDMxNTU4NDZaoA4wDDAKBgNVHRQEAwIBBDANBgkqhkiG9w0BAQUFAAOBgQB1eSbU
+qouEY+28nNc7QkoDJqK/VgfVYSIgbkuaM8RP4TalndoA/eP4VJbOycZ6HbU10315
+sfYhoARx8xXtz/OIU470PSUKrbeNQMXoebp5YGZHl1Gd+FvClVgzAPoJCn68rd4N
+mzkDJrdpBZCTt4yxv+vkLwdBmAetcLKj/lhpbg==
+-----END X509 CRL-----
--- /dev/null
+R 391118144000Z 120703155846Z CDAE0E564A2891A7 unknown /C=GB/ST=United Kingdom/L=Derby/O=Mosquitto Test Suite/OU=Broker Test/CN=localhost-client-test
--- /dev/null
+unique_subject = no
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIEITCCAwmgAwIBAgIJAJu8ZUmvHGqDMA0GCSqGSIb3DQEBBQUAMIGmMQswCQYD
+VQQGEwJERTEQMA4GA1UECAwHR2VybWFueTEPMA0GA1UEBwwGQmVybGluMR8wHQYD
+VQQKDBZGYWtlIENlcnRpZmljYXRlcyBHbWJoMRcwFQYDVQQLDA5Eb2RneSBEZWFs
+aW5nczEZMBcGA1UEAwwQZmFrZS1jZXJ0aWZpY2F0ZTEfMB0GCSqGSIb3DQEJARYQ
+ZmFrZUBleGFtcGxlLmNvbTAeFw0xMzAxMjUwOTE0MTJaFw0yMzAxMjMwOTE0MTJa
+MIGmMQswCQYDVQQGEwJERTEQMA4GA1UECAwHR2VybWFueTEPMA0GA1UEBwwGQmVy
+bGluMR8wHQYDVQQKDBZGYWtlIENlcnRpZmljYXRlcyBHbWJoMRcwFQYDVQQLDA5E
+b2RneSBEZWFsaW5nczEZMBcGA1UEAwwQZmFrZS1jZXJ0aWZpY2F0ZTEfMB0GCSqG
+SIb3DQEJARYQZmFrZUBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKRMCDzm2gYvw4LViJLqbyYJLozmv12IIO2XYq1ySVftJlx3rfsP
+v67llJ56hncj/c53vCe9U1jcb/R+ycDMP+28XvnEaEGAsotO4W1ky60Sku8rUwKM
+wIIMj5/bhPGCuZqw1PTlUfwb5BCcP1Kte+Tx4lElZZ7KgigWFFrxwlORUaRrVrBm
+qervDlb4ze7iryShpPQRtWvzCxyXyEB2mD9QEwDdFRF3+mooqCfSxy/LlzMPn2dx
+5MJtJ9M1dnjY8vI1GI+Uiw78GigJjlBLiZAfRRTlRWja/Q8vt/j0IbNQHKumfzEe
+uTnl0UlpzzX5CpVtfZXJRXtF7KRnCBpMT5ECAwEAAaNQME4wHQYDVR0OBBYEFDA+
+RHNT+TYVdOAJ1YJWIfKlY/zIMB8GA1UdIwQYMBaAFDA+RHNT+TYVdOAJ1YJWIfKl
+Y/zIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAG2oSw+kBTD9/FGJ
++rYAmBchTQTMOS2RPgpudEwRapswLvnYhWh/Vos6I0oQaGu9wf7rYPdqmTsNPoke
+/0j/Jqwp7/QEwtu6X0heqtAh/+FJVrpUlLbRoGbAuCb8rS3t3k8zGjaAKzNLB5vL
+quy9f4TZI0ojyN7v4q6B5FTdjaaxQa5hFVRno98oipI6jghQsnL9oxqsBedKaRE5
+WowXviqK3umHh0zqfkzVptee5GsNt7MceKRAgWUE2qj70kp2ceSX8D9aoWEar5k8
+VmvWfTAgmFq9GPV2WhJAoDj7P8lGDdqqtoI5qJAg+KSjtC1MSzMDSy/xuW7eFdbJ
+qJVuqN8=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIqEGGNc7YpJYCAggA
+MBQGCCqGSIb3DQMHBAiBEYk/9zv90QSCBMj06vSmv9YwCHKKSwyD2u7E+rbUbQz1
+UfY/Shf7zOvBTaELaSsctO9UpoAeWzPjkNvhhTsZIBhHxKomHc+YFBvAHLkUx7mk
+9MT9zxC5hQd0k0lcYt4xc5HFpg8AVx34PGA2AnRCWKz+Aj+fgxV0U3tNIzAw8V0w
+bZJGLFxQqhLXlME3Bd4iSG+oh5UzjhDvOVMTJoRsOpipOUfx/DKCMB23Z6987k7j
+t45VucNF3BlbBlaUDOCUKecMtkYAjHn44D9JmvII53xF5PTUFMVjd3Xdqdo6jQBX
+dN/HNigt2iDl1g0Wrere/8CjMFBuh3ANBrIF91eyj1xLTQ8+ZhBVRpEhgq11oMUd
+5jqWUJgax7PTgY4wSK24dYak9iSaqi32+1+KxQW8gi2E+vtCBgTO8NbH1F7OJarK
+wyUmRSE/hmD1XTLg6QEoKKl7/8WnGL5GkuppMkePHQj1YI5Dpwy4ElVFOzo7VvF0
+gqeLIFP7afE1BoTO7pcu8R30/KjHmg//LFJ4YksYMInyg1zv2djnoE7s/gwLA2jY
+J95VhN7SA4r5kpVzHdkwscaQ1J1ss9BLGRSJ2NuMo5vrvFnAHnZ9pvhzUA2UveD9
+EKzSbR8KRZf/BFn6d06n1nVEyNtGvD0p427L4Fn1HmD1yOi5z/iQHpgHUektFrEl
+LVduaLONwPyPXX3Gzzuy8ETQVRdiHA6J0ZQh/EpYPtsJ1MvmWMocQwKqaBvyQKc6
+CLop+2/MbAyJszUvUTJIDuXoHO4mm2u7G8h/4KOifDmRyERJdugQ+ZMu3LSgjl+e
+/Bi5qSDcgzI3GJLBjTsnkY5yKSz9VpCvvIh0gXlZT/7aFTBkic5+cc2l3K4Sf6DF
+C0FY2xfiNiFTsK2LZov4Dc7msv3Psfc0oCABpkv/i+lrHEMnJw3jj/KnVfw30/Gp
+/0hgcGBpdQ7EJmGXKY/yAd7gA3frfQ6lT7Fr6MTD6v5LRY7ZfJAka3RCQM4DrIj4
+Tl+CnAskekL2Zgm0L5K1QuVOZkH0uJca7Dy6HEAlSB+EhYQ0F/HZA94Vp/ZjtOvp
+cw9PJEztWLYSvsHwC9691vgkG79+YsI3mZ2i+t/Ps0zHe7EL6p3sbHp3h9FZj+7+
+rIfMWF7SGc0mQBNdwq+eyRg46LgZp4Za1R6ap5QoYFtV7cnHzZGeNAk6+ucmtviu
+2y5p5d51J1Ll6KELKodDZ7PLUDez9l0JMSLurlysQI7uvM91za6gtjoWgGlhNqYb
+/V8Zdj8jn2ri2fL3CGRHAQcPRDy26lzjh5DuHCXkHC8sn8cCl+wJWcBhU1GCrvlk
+d0TJZWbm8MDKTxJ+vC/jn0PZkZ0v0fOzVFYgnIDmteBvOx8IRkJUYmIY/tcBcpUz
+fMNXYedw9Xdd3dRdGcSuEPcFMtKWNX8H8GW6rydcj1wUsrYL7w8ONLWGWF1u09dQ
+N9fV1PK4J3ZS7u6O5aR9Pp+aZIUTj1nUgZucTlmyeNe8wl2pNm1TDNZHuoG1ogFj
+SFITR86ftxaYmn6oi5fbmbQN9n1zcSl4a+oAyldm1kwl2t8tbWlxmbfLiiPEkWlP
+MZky8x63apLPx1aJ3cf8Ie+ZwmHSYHSvNP89KdLnHOhRULW1KejVyLjmmnlEIRTr
+RUY=
+-----END ENCRYPTED PRIVATE KEY-----
--- /dev/null
+This directory contains certificates and keys required for SSL testing.
+The CA key has password "password".
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICvjCCAicCCQDNrg5WSiiRqTANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMC
+R0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEdMBsG
+A1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxDzANBgNVBAsMBkJyb2tlcjEXMBUG
+A1UEAwwOYnJva2VyLXRlc3QtY2ExHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBs
+ZS5jb20wHhcNMTIwNzAzMTU1MDE1WhcNMTIwNzA0MTU1MDE1WjCBpTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEd
+MBsGA1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxFDASBgNVBAsMC0Jyb2tlciBU
+ZXN0MRcwFQYDVQQDDA5sb2NhbGhvc3QtdGVzdDEfMB0GCSqGSIb3DQEJARYQdGVz
+dEBleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAttnPoWjp
+KXROyGM2yqR7IGVmn4RID5hdCCWiJT1s0cSm3BwY3aMD1m/ZrCZfMu3K6tA+9rrh
+xMjEVCmG4ez4UdTv+xfxRalo2SkA2J6Yiti5/ec8Hjh6m3ch9F8Ju62XsS5KZl0Q
+oOE5D7UrMayq2eflBlO02qobn8114MIc0EkCAwEAATANBgkqhkiG9w0BAQUFAAOB
+gQC2KotrVoQCtsqW54VbcaCyHki9GpYw2QR1Ex+0sRCLcr2HhUK471D8BCooNo53
+Kft0yEclN1x5j8I7Rk6QmLmrXDeZBrRqSasDo0glYGCN8QwoVfx5L54r0ktEGDvr
+4PUWTieyuLKbFB+be0esM+/5IwpdsgVZuDI3D4jBR53SgQ==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICvjCCAicCCQDNrg5WSiiRpDANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMC
+R0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEdMBsG
+A1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxDzANBgNVBAsMBkJyb2tlcjEXMBUG
+A1UEAwwOYnJva2VyLXRlc3QtY2ExHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBs
+ZS5jb20wHhcNMTIwNzAzMTEzMjM0WhcNMzkxMTE4MTEzMjM0WjCBpTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEd
+MBsGA1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxFDASBgNVBAsMC0Jyb2tlciBU
+ZXN0MRcwFQYDVQQDDA5sb2NhbGhvc3QtdGVzdDEfMB0GCSqGSIb3DQEJARYQdGVz
+dEBleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAttnPoWjp
+KXROyGM2yqR7IGVmn4RID5hdCCWiJT1s0cSm3BwY3aMD1m/ZrCZfMu3K6tA+9rrh
+xMjEVCmG4ez4UdTv+xfxRalo2SkA2J6Yiti5/ec8Hjh6m3ch9F8Ju62XsS5KZl0Q
+oOE5D7UrMayq2eflBlO02qobn8114MIc0EkCAwEAATANBgkqhkiG9w0BAQUFAAOB
+gQCNcNqm8mb7K/ys+3LENUB7XccA1gzyb3ylpsqQj5TmGYqT+Z1g7pSw0Pbd94Uc
+x+ihqjRo5Eaz7GqCyS7mnNu5aGBHH3s1ir9hT18R7tm+XwMTQcGoRy986O1BJy+r
+q1Gg0lmgvu+jlYpR4xJHGzd3wK8agi+y9ZSAlfAZ6hJkrw==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE REQUEST-----
+MIIB5jCCAU8CAQAwgaUxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2lu
+Z2RvbTEOMAwGA1UEBwwFRGVyYnkxHTAbBgNVBAoMFE1vc3F1aXR0byBUZXN0IFN1
+aXRlMRQwEgYDVQQLDAtCcm9rZXIgVGVzdDEXMBUGA1UEAwwObG9jYWxob3N0LXRl
+c3QxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBALbZz6Fo6Sl0TshjNsqkeyBlZp+ESA+YXQgloiU9bNHE
+ptwcGN2jA9Zv2awmXzLtyurQPva64cTIxFQphuHs+FHU7/sX8UWpaNkpANiemIrY
+uf3nPB44ept3IfRfCbutl7EuSmZdEKDhOQ+1KzGsqtnn5QZTtNqqG5/NdeDCHNBJ
+AgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQBDqZNA2bsljTddqvAONJDLXv9R7mTy
+sGHIRlQoV/p8GMywBaOzh1T5H3RdUKBDKN8Kt9nNW8Xfqi9vJGPse4ZBq11FoC+b
+59aFTlh+IXQu0rH9r1E8htjcMdNdzDSFxcD/6cwp1uiFm/2YbYl0iojsKLxbVlaK
+jMIfJi3EpeDyHQ==
+-----END CERTIFICATE REQUEST-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC22c+haOkpdE7IYzbKpHsgZWafhEgPmF0IJaIlPWzRxKbcHBjd
+owPWb9msJl8y7crq0D72uuHEyMRUKYbh7PhR1O/7F/FFqWjZKQDYnpiK2Ln95zwe
+OHqbdyH0Xwm7rZexLkpmXRCg4TkPtSsxrKrZ5+UGU7TaqhufzXXgwhzQSQIDAQAB
+AoGAXUSq8SVHUXrfOL3K1ACkQXkXqKRb8YCBa8dudtpnKHTLvBik4mDlczsoZ/RG
+uP6sc6v3gfj/clYKNvfbsmAipRWfHVC157vBlEiBfAoBbNgicF/4dCOSGsDYStOy
+F88l1SvcDWjK6u33gj/SBHDMz6SOam2muXZNZa0brSSW2tUCQQDrQTuTHkf4PgUa
+5a4stlx4bplAEtJGJvXt4k2xXvtZ1UW/G7xMspphQb8n2UB3uQeXXV6cJAmHnmx2
+2ghxje0zAkEAxvmVMb2ZRmeIfiUOSFXPjtKNqJZG8hpQ8i2yDuyq69Hi7L3SQSGN
+V9uPceEdVW+IQEOJg2feXhmlfCNWIFP0kwJBAKy5HzlbsTGEr5DY8zFmzqupYCEX
+8ISLFGMMlUhV2StSl7vBbFXPh+NCN0vViSydkAJFDjKLjuegnDgCytI8htsCQA/N
+gLjrmwHJdUC3hrPeBNcOB+wsy0OtLWKemHaw+z4xdDljNhCwLn6c1H6x51eCvSqF
+cqV6GWIV3VvHnq6AnHsCQBqyU46p0dax4z+5vzbL+zWmi8gdBYvPSY5SpAjsQADQ
+A3PcKi2DFuPjcxdl0qr9aq1qg6VUHy3RLTcmgA8YKWo=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDHjCCAoegAwIBAgIJAK2kGB3tYrLVMA0GCSqGSIb3DQEBBQUAMIGnMQswCQYD
+VQQGEwJHQjEXMBUGA1UECAwOVW5pdGVkIEtpbmdkb20xDjAMBgNVBAcMBURlcmJ5
+MR0wGwYDVQQKDBRNb3NxdWl0dG8gVGVzdCBTdWl0ZTEXMBUGA1UECwwOQWx0ZXJu
+YXRpdmUgQ0ExFDASBgNVBAMMC3Rlc3QtY2EtYWx0MSEwHwYJKoZIhvcNAQkBFhJj
+YS1hbHRAZXhhbXBsZS5jb20wHhcNMTIwNzAzMTQ1MDI3WhcNMzkxMTE5MTQ1MDI3
+WjCBpzELMAkGA1UEBhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYD
+VQQHDAVEZXJieTEdMBsGA1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxFzAVBgNV
+BAsMDkFsdGVybmF0aXZlIENBMRQwEgYDVQQDDAt0ZXN0LWNhLWFsdDEhMB8GCSqG
+SIb3DQEJARYSY2EtYWx0QGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDxlFX2Ihc2Uk0ksPe0EJoULBKfr3b5LEuTEqocnypXZ52i61sx8DPd
+HM1EBjlxSrUGxPR0mVoL7d/i9kgEs+4seeNeXn27Vot4Wd+jPTyaHbziLUG1L/nZ
+112hWfAfqTU6MUFAkv5BNCoHZZKXLybP4tBXgHpwrVzXa9f3hUGfMwIDAQABo1Aw
+TjAdBgNVHQ4EFgQUZik8IjLHGc/taXUD60zLp4TA3gkwHwYDVR0jBBgwFoAUZik8
+IjLHGc/taXUD60zLp4TA3gkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOB
+gQAomJg4uKFY3Bi5+k+63O/Ze0SaV9gsgO9GWwb1Jjyi3ZyFTzCGWr3XsD9DsOyT
+gHwAzUgDFIyYVXc3kgBBg54wjEA8A7yQ++HIsutEIR3XykbBfU2oS0VbPKejsPrS
+4zuGt3nQdYrKI2iD207HG6XiO0VfUTro6BGuazvsfE9jGg==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQImtEogQ274ucCAggA
+MBQGCCqGSIb3DQMHBAgmCV4tCw6jVwSCAoA7cj6maNe06C2LjYbSgRMr4s2DIini
+2E1KqqRnrz1Ie3U9a4OzmToGuwDqnYR1uK6ImWHwzFZxXkIfsmJUra2BEnSj7W1N
+qGPiQwsBD6L93YyejwoTRFMkkFw6pgdg64eyIstNxYMiiJ9cB+6dc4Z+kY4Ik7Fg
+4xR1+PTHVbNpHeM2ApUHuaoOyaMijzdhwwMbUegVdnsrjb6kgM/5EoIsrxvpax4E
+JSIk+DpdQ8jspwC6n6RNfYhOJdk6ECSi6Wszz1vfLb76YkSFvSsucxYFK7iH6vW2
+llrW/GqfA1+KQwUbD7RULSne+TIWqkk+Z3u/gpDsDe6qf17b3DLcJFGqRdUZXZ9G
+lJfGiWYBro3m/z8gELAfKqVf5BbRYdfAqXdNRHqQkC/VvwsqGknRO0XIawzHxZLA
+OajqAZ4MX2lG/GGYGv51bpnB6B7gdT4LcXtJAUUfezBiu+aw0cFxx3Mox+gPQgKy
+YimuMLPTVaayFfe963odDwVUTcmh48dIvfvfHonvJA8n6pdF3dl+F4FcJ3yTUdBf
+LivlIuXtbobm2ANR4aBrISP47tug11XKs92nGBv5fgvmALr8qjbMLd4naKjA3HR6
+g36cRAu5XBSqN8UNpNyw1lQrQMsNHlFtHhvD5pdh5KuXf9KJrVt6PVUuXCzOb4fW
+EvcSSNR2xIJeRkPwdgAIasnnThyCWQPxm+SoYvogNuRMuy/T2k4Y+RtofJ12KfPr
+mI7M6x2/TPuSbu9Vnee7Xt67JyCLAv1RLXrzqIIJlHrS4hw7Oza11CBCDbYVT3UE
+8Wf1a+L4dF6TSp4NGY6KXaISjvjGGCyueVwH8/YrxnfMk80HvVLNlS3g
+-----END ENCRYPTED PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDEDCCAnmgAwIBAgIJALKg/M/sjw95MA0GCSqGSIb3DQEBBQUAMIGgMQswCQYD
+VQQGEwJHQjEXMBUGA1UECAwOVW5pdGVkIEtpbmdkb20xDjAMBgNVBAcMBURlcmJ5
+MR0wGwYDVQQKDBRNb3NxdWl0dG8gVGVzdCBTdWl0ZTEPMA0GA1UECwwGQnJva2Vy
+MRcwFQYDVQQDDA5icm9rZXItdGVzdC1jYTEfMB0GCSqGSIb3DQEJARYQdGVzdEBl
+eGFtcGxlLmNvbTAeFw0xMjA3MDMxMTMwMDdaFw0zOTExMTkxMTMwMDdaMIGgMQsw
+CQYDVQQGEwJHQjEXMBUGA1UECAwOVW5pdGVkIEtpbmdkb20xDjAMBgNVBAcMBURl
+cmJ5MR0wGwYDVQQKDBRNb3NxdWl0dG8gVGVzdCBTdWl0ZTEPMA0GA1UECwwGQnJv
+a2VyMRcwFQYDVQQDDA5icm9rZXItdGVzdC1jYTEfMB0GCSqGSIb3DQEJARYQdGVz
+dEBleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxdqM/gN/
+nRuHdvjKQ3nOIHzblSQRmnU17AvusuucKaEAaIBJO05pUryoUfAPhb/QtPbiqGQ+
+VZN5n2Di4MxgYTdQ9SAjJKv/v6TNm23IjlYgt5XnXbvZhGid/FrTjsldPVEKKKM/
+DTPx24o3coE2KxDOjnfGsR20LPnmEp7icBkCAwEAAaNQME4wHQYDVR0OBBYEFO2l
+nlq5L/AmkNaZ0s7OqJr1KoUtMB8GA1UdIwQYMBaAFO2lnlq5L/AmkNaZ0s7OqJr1
+KoUtMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAEaeNG4qmUrJtQqR6
+Kq8rIYBfYpkhlWTgo/uOxo8OgAjwhrYja7SBiSDOk7dk+OPdvMxxLIalzb+4IaoH
+IMoFBsCuJApFBEkaxE4W6AZA7wjtwq5t3HbRtRD44soBtoeHhPILFfKuWpYVM4Vg
+esxJKdf10bGX48nXFREBcSg6ce4=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI9H1Lfg1rh7wCAggA
+MBQGCCqGSIb3DQMHBAi1Lzw2lwTCCgSCAoAJV4++qH5sbnxx4yOO2OYOn8ceZePP
+xjfFHgG3D6AxrQtNnzIqk/WTeSOJKPn3jt5AZmpr///t2cEb45KZFPJff/CzXtfM
+jxhbklkTZLBv+4S7Z/Me0Z14eJdjk2uGDnV2dJdCOB7U+Ig06dfyliHktC/CIqiH
+5v9qsGog9Dv1oauN6HGcWjIz/fXI3THBRlynNo4tv1BOaoxDXTYenHgo2v4CXmsQ
+chOQX6u/RDDpht9Dm45IFQKcHfHbHIpB9s0iXXm+9kbDAdxIRfNV2EywK53Xm3D8
+u0iynbmAlUh30d0n+0sLEbITrs6BTS+/eCvRUnPqerYTVBOpGb+o4GiVLm3LsogH
+3nuh//JlRDD894rHLqQQ4uzXHvn1fVFNe+th8kjiIVEr6NevIIBPvTnPrxSFco4k
+CHmOMmm+hhp2B2sZa2IFfIKJ64DrmiT5mezG4aMRjkB2PK0MNDctoYdhQYmB01VR
+1P0r5svnt39tNep6jQhydiMOXhyhX2AVfbTBG9izoi0Sn2eQ0tYtlT+oQzw7yxK2
+7MOo7PTemlvIsCVgyL1+OkVSBn+n0nHEgRd+DNsu1gsmetUpENfYtqjqnKD6FDLN
+gOPJ3Eoj7XIo5qTrKbJd2EQNlzMFCMikfWHcijRLioEhS9tx8PHppdP7MSinXAYc
+IsWN1+4lIHj8jsnEWDp3UWy6FkLFsy5iOayWsC1PjJqo4yVdsYJ/Ef34g5IBBB1x
+AC7Orq6ZGWoV4jFXkzFj/FhOpf9G6wpQ30qiW/wnVUT09Nr45vsOqcLChLxUO7s7
+VQk9XcDNhUJQB9uVUgF+Z536CG6U9K9fZSqK72iN/1t+dhQn/eZbrwLL
+-----END ENCRYPTED PRIVATE KEY-----
--- /dev/null
+CDAE0E564A2891AA