From: Roger Light Date: Fri, 25 Jan 2013 10:31:19 +0000 (+0000) Subject: Initial contribution. X-Git-Tag: v0.9~86 X-Git-Url: https://git.michaelhowe.org/gitweb/?a=commitdiff_plain;h=e9f93670d282e97941cd0b85e625cf366a8a928e;p=packages%2Fp%2Fpaho-mqtt.git Initial contribution. --- e9f93670d282e97941cd0b85e625cf366a8a928e diff --git a/.hgeol b/.hgeol new file mode 100644 index 0000000..0ecd034 --- /dev/null +++ b/.hgeol @@ -0,0 +1,14 @@ +[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 + diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..50c84b9 --- /dev/null +++ b/.hgignore @@ -0,0 +1,39 @@ +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/ diff --git a/LICENSE-3rd-party.txt b/LICENSE-3rd-party.txt new file mode 100644 index 0000000..87cb45a --- /dev/null +++ b/LICENSE-3rd-party.txt @@ -0,0 +1,666 @@ +============================================================================ +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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..cc09f28 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,54 @@ +Copyright (c) 2009-2012 Roger Light +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. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..008dd73 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +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/ + diff --git a/lib/python/Makefile b/lib/python/Makefile new file mode 100644 index 0000000..a111355 --- /dev/null +++ b/lib/python/Makefile @@ -0,0 +1,17 @@ +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__ diff --git a/lib/python/mosquitto.py b/lib/python/mosquitto.py new file mode 100755 index 0000000..0a68446 --- /dev/null +++ b/lib/python/mosquitto.py @@ -0,0 +1,1767 @@ +# Copyright (c) 2012 Roger Light +# 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() + diff --git a/lib/python/setup.py b/lib/python/setup.py new file mode 100644 index 0000000..7b97bfa --- /dev/null +++ b/lib/python/setup.py @@ -0,0 +1,28 @@ +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', + ] + ) diff --git a/lib/python/sub.py b/lib/python/sub.py new file mode 100755 index 0000000..7af946d --- /dev/null +++ b/lib/python/sub.py @@ -0,0 +1,62 @@ +#!/usr/bin/python + +# Copyright (c) 2010,2011 Roger Light +# 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)) diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..8c9a44d --- /dev/null +++ b/test/Makefile @@ -0,0 +1,91 @@ +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 diff --git a/test/lib/01-con-discon-success.py b/test/lib/01-con-discon-success.py new file mode 100755 index 0000000..51cec8f --- /dev/null +++ b/test/lib/01-con-discon-success.py @@ -0,0 +1,65 @@ +#!/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) + diff --git a/test/lib/01-keepalive-pingreq.py b/test/lib/01-keepalive-pingreq.py new file mode 100755 index 0000000..612a802 --- /dev/null +++ b/test/lib/01-keepalive-pingreq.py @@ -0,0 +1,69 @@ +#!/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) + diff --git a/test/lib/01-no-clean-session.py b/test/lib/01-no-clean-session.py new file mode 100755 index 0000000..b495e85 --- /dev/null +++ b/test/lib/01-no-clean-session.py @@ -0,0 +1,56 @@ +#!/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) + diff --git a/test/lib/01-unpwd-set.py b/test/lib/01-unpwd-set.py new file mode 100755 index 0000000..f5ae278 --- /dev/null +++ b/test/lib/01-unpwd-set.py @@ -0,0 +1,56 @@ +#!/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) + diff --git a/test/lib/01-will-set.py b/test/lib/01-will-set.py new file mode 100755 index 0000000..586adf7 --- /dev/null +++ b/test/lib/01-will-set.py @@ -0,0 +1,58 @@ +#!/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) + diff --git a/test/lib/01-will-unpwd-set.py b/test/lib/01-will-unpwd-set.py new file mode 100755 index 0000000..77eb0aa --- /dev/null +++ b/test/lib/01-will-unpwd-set.py @@ -0,0 +1,60 @@ +#!/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) + diff --git a/test/lib/02-subscribe-qos0.py b/test/lib/02-subscribe-qos0.py new file mode 100755 index 0000000..8e24440 --- /dev/null +++ b/test/lib/02-subscribe-qos0.py @@ -0,0 +1,75 @@ +#!/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) diff --git a/test/lib/02-subscribe-qos1.py b/test/lib/02-subscribe-qos1.py new file mode 100755 index 0000000..eeac5df --- /dev/null +++ b/test/lib/02-subscribe-qos1.py @@ -0,0 +1,75 @@ +#!/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) diff --git a/test/lib/02-subscribe-qos2.py b/test/lib/02-subscribe-qos2.py new file mode 100755 index 0000000..580fece --- /dev/null +++ b/test/lib/02-subscribe-qos2.py @@ -0,0 +1,75 @@ +#!/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) diff --git a/test/lib/02-unsubscribe.py b/test/lib/02-unsubscribe.py new file mode 100755 index 0000000..1d107b4 --- /dev/null +++ b/test/lib/02-unsubscribe.py @@ -0,0 +1,65 @@ +#!/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) diff --git a/test/lib/03-publish-b2c-qos1.py b/test/lib/03-publish-b2c-qos1.py new file mode 100755 index 0000000..8f47f07 --- /dev/null +++ b/test/lib/03-publish-b2c-qos1.py @@ -0,0 +1,79 @@ +#!/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) diff --git a/test/lib/03-publish-b2c-qos2.py b/test/lib/03-publish-b2c-qos2.py new file mode 100755 index 0000000..51e64fe --- /dev/null +++ b/test/lib/03-publish-b2c-qos2.py @@ -0,0 +1,91 @@ +#!/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) diff --git a/test/lib/03-publish-c2b-qos1-disconnect.py b/test/lib/03-publish-c2b-qos1-disconnect.py new file mode 100755 index 0000000..33db504 --- /dev/null +++ b/test/lib/03-publish-c2b-qos1-disconnect.py @@ -0,0 +1,77 @@ +#!/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) diff --git a/test/lib/03-publish-c2b-qos1-timeout.py b/test/lib/03-publish-c2b-qos1-timeout.py new file mode 100755 index 0000000..46d37ed --- /dev/null +++ b/test/lib/03-publish-c2b-qos1-timeout.py @@ -0,0 +1,83 @@ +#!/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) diff --git a/test/lib/03-publish-c2b-qos2-disconnect.py b/test/lib/03-publish-c2b-qos2-disconnect.py new file mode 100755 index 0000000..d92b482 --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-disconnect.py @@ -0,0 +1,96 @@ +#!/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) diff --git a/test/lib/03-publish-c2b-qos2-timeout.py b/test/lib/03-publish-c2b-qos2-timeout.py new file mode 100755 index 0000000..8b0129c --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-timeout.py @@ -0,0 +1,94 @@ +#!/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) diff --git a/test/lib/03-publish-qos0-no-payload.py b/test/lib/03-publish-qos0-no-payload.py new file mode 100755 index 0000000..6b16663 --- /dev/null +++ b/test/lib/03-publish-qos0-no-payload.py @@ -0,0 +1,69 @@ +#!/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) diff --git a/test/lib/03-publish-qos0.py b/test/lib/03-publish-qos0.py new file mode 100755 index 0000000..db7e578 --- /dev/null +++ b/test/lib/03-publish-qos0.py @@ -0,0 +1,69 @@ +#!/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) diff --git a/test/lib/04-retain-qos0.py b/test/lib/04-retain-qos0.py new file mode 100755 index 0000000..b0c6e24 --- /dev/null +++ b/test/lib/04-retain-qos0.py @@ -0,0 +1,59 @@ +#!/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) diff --git a/test/lib/08-ssl-bad-cacert.py b/test/lib/08-ssl-bad-cacert.py new file mode 100755 index 0000000..13fed1c --- /dev/null +++ b/test/lib/08-ssl-bad-cacert.py @@ -0,0 +1,38 @@ +#!/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) diff --git a/test/lib/08-ssl-connect-cert-auth.py b/test/lib/08-ssl-connect-cert-auth.py new file mode 100755 index 0000000..8554713 --- /dev/null +++ b/test/lib/08-ssl-connect-cert-auth.py @@ -0,0 +1,73 @@ +#!/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) diff --git a/test/lib/08-ssl-connect-no-auth.py b/test/lib/08-ssl-connect-no-auth.py new file mode 100755 index 0000000..716c59d --- /dev/null +++ b/test/lib/08-ssl-connect-no-auth.py @@ -0,0 +1,70 @@ +#!/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) diff --git a/test/lib/09-util-topic-matching.py b/test/lib/09-util-topic-matching.py new file mode 100755 index 0000000..57c989e --- /dev/null +++ b/test/lib/09-util-topic-matching.py @@ -0,0 +1,27 @@ +#!/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) diff --git a/test/lib/Makefile b/test/lib/Makefile new file mode 100644 index 0000000..a78b68b --- /dev/null +++ b/test/lib/Makefile @@ -0,0 +1,45 @@ +.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 diff --git a/test/lib/python/01-con-discon-success.test b/test/lib/python/01-con-discon-success.test new file mode 100755 index 0000000..50a45f3 --- /dev/null +++ b/test/lib/python/01-con-discon-success.test @@ -0,0 +1,33 @@ +#!/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) diff --git a/test/lib/python/01-keepalive-pingreq.test b/test/lib/python/01-keepalive-pingreq.test new file mode 100755 index 0000000..9aa289b --- /dev/null +++ b/test/lib/python/01-keepalive-pingreq.test @@ -0,0 +1,25 @@ +#!/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) diff --git a/test/lib/python/01-no-clean-session.test b/test/lib/python/01-no-clean-session.test new file mode 100755 index 0000000..b006dc7 --- /dev/null +++ b/test/lib/python/01-no-clean-session.test @@ -0,0 +1,12 @@ +#!/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) diff --git a/test/lib/python/01-unpwd-set.test b/test/lib/python/01-unpwd-set.test new file mode 100755 index 0000000..b55dab8 --- /dev/null +++ b/test/lib/python/01-unpwd-set.test @@ -0,0 +1,13 @@ +#!/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) diff --git a/test/lib/python/01-will-set.test b/test/lib/python/01-will-set.test new file mode 100755 index 0000000..75803a3 --- /dev/null +++ b/test/lib/python/01-will-set.test @@ -0,0 +1,13 @@ +#!/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) diff --git a/test/lib/python/01-will-unpwd-set.test b/test/lib/python/01-will-unpwd-set.test new file mode 100755 index 0000000..0379b36 --- /dev/null +++ b/test/lib/python/01-will-unpwd-set.test @@ -0,0 +1,14 @@ +#!/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) diff --git a/test/lib/python/02-subscribe-qos0.test b/test/lib/python/02-subscribe-qos0.test new file mode 100755 index 0000000..2f65055 --- /dev/null +++ b/test/lib/python/02-subscribe-qos0.test @@ -0,0 +1,35 @@ +#!/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) diff --git a/test/lib/python/02-subscribe-qos1.test b/test/lib/python/02-subscribe-qos1.test new file mode 100755 index 0000000..d00cc13 --- /dev/null +++ b/test/lib/python/02-subscribe-qos1.test @@ -0,0 +1,35 @@ +#!/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) diff --git a/test/lib/python/02-subscribe-qos2.test b/test/lib/python/02-subscribe-qos2.test new file mode 100755 index 0000000..83a60b9 --- /dev/null +++ b/test/lib/python/02-subscribe-qos2.test @@ -0,0 +1,35 @@ +#!/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) diff --git a/test/lib/python/02-unsubscribe.test b/test/lib/python/02-unsubscribe.test new file mode 100755 index 0000000..e3b5ecc --- /dev/null +++ b/test/lib/python/02-unsubscribe.test @@ -0,0 +1,35 @@ +#!/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) diff --git a/test/lib/python/03-publish-b2c-qos1.test b/test/lib/python/03-publish-b2c-qos1.test new file mode 100755 index 0000000..3b93c3a --- /dev/null +++ b/test/lib/python/03-publish-b2c-qos1.test @@ -0,0 +1,45 @@ +#!/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) diff --git a/test/lib/python/03-publish-b2c-qos2.test b/test/lib/python/03-publish-b2c-qos2.test new file mode 100755 index 0000000..74e9df0 --- /dev/null +++ b/test/lib/python/03-publish-b2c-qos2.test @@ -0,0 +1,48 @@ +#!/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) diff --git a/test/lib/python/03-publish-c2b-qos1-disconnect.test b/test/lib/python/03-publish-c2b-qos1-disconnect.test new file mode 100755 index 0000000..5da5623 --- /dev/null +++ b/test/lib/python/03-publish-c2b-qos1-disconnect.test @@ -0,0 +1,48 @@ +#!/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) diff --git a/test/lib/python/03-publish-c2b-qos1-timeout.test b/test/lib/python/03-publish-c2b-qos1-timeout.test new file mode 100755 index 0000000..30f1b33 --- /dev/null +++ b/test/lib/python/03-publish-c2b-qos1-timeout.test @@ -0,0 +1,44 @@ +#!/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) diff --git a/test/lib/python/03-publish-c2b-qos2-disconnect.test b/test/lib/python/03-publish-c2b-qos2-disconnect.test new file mode 100755 index 0000000..f394c61 --- /dev/null +++ b/test/lib/python/03-publish-c2b-qos2-disconnect.test @@ -0,0 +1,44 @@ +#!/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) diff --git a/test/lib/python/03-publish-c2b-qos2-timeout.test b/test/lib/python/03-publish-c2b-qos2-timeout.test new file mode 100755 index 0000000..940c594 --- /dev/null +++ b/test/lib/python/03-publish-c2b-qos2-timeout.test @@ -0,0 +1,37 @@ +#!/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) diff --git a/test/lib/python/03-publish-qos0-no-payload.test b/test/lib/python/03-publish-qos0-no-payload.test new file mode 100755 index 0000000..7730e52 --- /dev/null +++ b/test/lib/python/03-publish-qos0-no-payload.test @@ -0,0 +1,36 @@ +#!/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) diff --git a/test/lib/python/03-publish-qos0.test b/test/lib/python/03-publish-qos0.test new file mode 100755 index 0000000..c446a08 --- /dev/null +++ b/test/lib/python/03-publish-qos0.test @@ -0,0 +1,36 @@ +#!/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) diff --git a/test/lib/python/04-retain-qos0.test b/test/lib/python/04-retain-qos0.test new file mode 100755 index 0000000..516538d --- /dev/null +++ b/test/lib/python/04-retain-qos0.test @@ -0,0 +1,27 @@ +#!/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) diff --git a/test/lib/python/08-ssl-bad-cacert.test b/test/lib/python/08-ssl-bad-cacert.test new file mode 100755 index 0000000..5b8bfb2 --- /dev/null +++ b/test/lib/python/08-ssl-bad-cacert.test @@ -0,0 +1,23 @@ +#!/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) diff --git a/test/lib/python/08-ssl-connect-cert-auth.test b/test/lib/python/08-ssl-connect-cert-auth.test new file mode 100755 index 0000000..d2c59c6 --- /dev/null +++ b/test/lib/python/08-ssl-connect-cert-auth.test @@ -0,0 +1,37 @@ +#!/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) diff --git a/test/lib/python/08-ssl-connect-no-auth.test b/test/lib/python/08-ssl-connect-no-auth.test new file mode 100755 index 0000000..6baea37 --- /dev/null +++ b/test/lib/python/08-ssl-connect-no-auth.test @@ -0,0 +1,37 @@ +#!/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) diff --git a/test/lib/python/08-ssl-fake-cacert.test b/test/lib/python/08-ssl-fake-cacert.test new file mode 100755 index 0000000..1c475ba --- /dev/null +++ b/test/lib/python/08-ssl-fake-cacert.test @@ -0,0 +1,32 @@ +#!/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) diff --git a/test/lib/python/09-util-topic-matching.test b/test/lib/python/09-util-topic-matching.test new file mode 100755 index 0000000..11fd81b --- /dev/null +++ b/test/lib/python/09-util-topic-matching.test @@ -0,0 +1,62 @@ +#!/usr/bin/python + +# Copyright (c) 2012 Roger Light +# 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) diff --git a/test/lib/python3/01-con-discon-success.test b/test/lib/python3/01-con-discon-success.test new file mode 100755 index 0000000..e517856 --- /dev/null +++ b/test/lib/python3/01-con-discon-success.test @@ -0,0 +1,33 @@ +#!/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) diff --git a/test/lib/python3/01-keepalive-pingreq.test b/test/lib/python3/01-keepalive-pingreq.test new file mode 100755 index 0000000..f7b0f30 --- /dev/null +++ b/test/lib/python3/01-keepalive-pingreq.test @@ -0,0 +1,25 @@ +#!/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) diff --git a/test/lib/python3/01-no-clean-session.test b/test/lib/python3/01-no-clean-session.test new file mode 100755 index 0000000..17d1aad --- /dev/null +++ b/test/lib/python3/01-no-clean-session.test @@ -0,0 +1,12 @@ +#!/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) diff --git a/test/lib/python3/01-unpwd-set.test b/test/lib/python3/01-unpwd-set.test new file mode 100755 index 0000000..593636f --- /dev/null +++ b/test/lib/python3/01-unpwd-set.test @@ -0,0 +1,13 @@ +#!/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) diff --git a/test/lib/python3/01-will-set.test b/test/lib/python3/01-will-set.test new file mode 100755 index 0000000..2aae62f --- /dev/null +++ b/test/lib/python3/01-will-set.test @@ -0,0 +1,13 @@ +#!/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) diff --git a/test/lib/python3/01-will-unpwd-set.test b/test/lib/python3/01-will-unpwd-set.test new file mode 100755 index 0000000..290fe77 --- /dev/null +++ b/test/lib/python3/01-will-unpwd-set.test @@ -0,0 +1,14 @@ +#!/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) diff --git a/test/lib/python3/02-subscribe-qos0.test b/test/lib/python3/02-subscribe-qos0.test new file mode 100755 index 0000000..2d5b721 --- /dev/null +++ b/test/lib/python3/02-subscribe-qos0.test @@ -0,0 +1,35 @@ +#!/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) diff --git a/test/lib/python3/02-subscribe-qos1.test b/test/lib/python3/02-subscribe-qos1.test new file mode 100755 index 0000000..aebda7d --- /dev/null +++ b/test/lib/python3/02-subscribe-qos1.test @@ -0,0 +1,35 @@ +#!/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) diff --git a/test/lib/python3/02-subscribe-qos2.test b/test/lib/python3/02-subscribe-qos2.test new file mode 100755 index 0000000..1fb5e2c --- /dev/null +++ b/test/lib/python3/02-subscribe-qos2.test @@ -0,0 +1,35 @@ +#!/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) diff --git a/test/lib/python3/02-unsubscribe.test b/test/lib/python3/02-unsubscribe.test new file mode 100755 index 0000000..12a85b6 --- /dev/null +++ b/test/lib/python3/02-unsubscribe.test @@ -0,0 +1,35 @@ +#!/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) diff --git a/test/lib/python3/03-publish-b2c-qos1.test b/test/lib/python3/03-publish-b2c-qos1.test new file mode 100755 index 0000000..56252ed --- /dev/null +++ b/test/lib/python3/03-publish-b2c-qos1.test @@ -0,0 +1,45 @@ +#!/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) diff --git a/test/lib/python3/03-publish-b2c-qos2.test b/test/lib/python3/03-publish-b2c-qos2.test new file mode 100755 index 0000000..be0ff08 --- /dev/null +++ b/test/lib/python3/03-publish-b2c-qos2.test @@ -0,0 +1,48 @@ +#!/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) diff --git a/test/lib/python3/03-publish-c2b-qos1-disconnect.test b/test/lib/python3/03-publish-c2b-qos1-disconnect.test new file mode 100755 index 0000000..5da5623 --- /dev/null +++ b/test/lib/python3/03-publish-c2b-qos1-disconnect.test @@ -0,0 +1,48 @@ +#!/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) diff --git a/test/lib/python3/03-publish-c2b-qos1-timeout.test b/test/lib/python3/03-publish-c2b-qos1-timeout.test new file mode 100755 index 0000000..3283c74 --- /dev/null +++ b/test/lib/python3/03-publish-c2b-qos1-timeout.test @@ -0,0 +1,44 @@ +#!/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) diff --git a/test/lib/python3/03-publish-c2b-qos2-disconnect.test b/test/lib/python3/03-publish-c2b-qos2-disconnect.test new file mode 100755 index 0000000..0d97ec5 --- /dev/null +++ b/test/lib/python3/03-publish-c2b-qos2-disconnect.test @@ -0,0 +1,44 @@ +#!/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) diff --git a/test/lib/python3/03-publish-c2b-qos2-timeout.test b/test/lib/python3/03-publish-c2b-qos2-timeout.test new file mode 100755 index 0000000..13c7baa --- /dev/null +++ b/test/lib/python3/03-publish-c2b-qos2-timeout.test @@ -0,0 +1,37 @@ +#!/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) diff --git a/test/lib/python3/03-publish-qos0-no-payload.test b/test/lib/python3/03-publish-qos0-no-payload.test new file mode 100755 index 0000000..874988c --- /dev/null +++ b/test/lib/python3/03-publish-qos0-no-payload.test @@ -0,0 +1,36 @@ +#!/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) diff --git a/test/lib/python3/03-publish-qos0.test b/test/lib/python3/03-publish-qos0.test new file mode 100755 index 0000000..f3474fd --- /dev/null +++ b/test/lib/python3/03-publish-qos0.test @@ -0,0 +1,36 @@ +#!/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) diff --git a/test/lib/python3/04-retain-qos0.test b/test/lib/python3/04-retain-qos0.test new file mode 100755 index 0000000..7bf6c85 --- /dev/null +++ b/test/lib/python3/04-retain-qos0.test @@ -0,0 +1,27 @@ +#!/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) diff --git a/test/lib/python3/08-ssl-bad-cacert.test b/test/lib/python3/08-ssl-bad-cacert.test new file mode 100755 index 0000000..5b8bfb2 --- /dev/null +++ b/test/lib/python3/08-ssl-bad-cacert.test @@ -0,0 +1,23 @@ +#!/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) diff --git a/test/lib/python3/08-ssl-connect-cert-auth.test b/test/lib/python3/08-ssl-connect-cert-auth.test new file mode 100755 index 0000000..f7dbadf --- /dev/null +++ b/test/lib/python3/08-ssl-connect-cert-auth.test @@ -0,0 +1,34 @@ +#!/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) diff --git a/test/lib/python3/08-ssl-connect-no-auth.test b/test/lib/python3/08-ssl-connect-no-auth.test new file mode 100755 index 0000000..1fd5aec --- /dev/null +++ b/test/lib/python3/08-ssl-connect-no-auth.test @@ -0,0 +1,34 @@ +#!/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) diff --git a/test/lib/python3/08-ssl-fake-cacert.test b/test/lib/python3/08-ssl-fake-cacert.test new file mode 100755 index 0000000..e740b60 --- /dev/null +++ b/test/lib/python3/08-ssl-fake-cacert.test @@ -0,0 +1,32 @@ +#!/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) diff --git a/test/lib/python3/09-util-topic-matching.test b/test/lib/python3/09-util-topic-matching.test new file mode 100755 index 0000000..11fd81b --- /dev/null +++ b/test/lib/python3/09-util-topic-matching.test @@ -0,0 +1,62 @@ +#!/usr/bin/python + +# Copyright (c) 2012 Roger Light +# 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) diff --git a/test/mosq_test.py b/test/mosq_test.py new file mode 100644 index 0000000..111e524 --- /dev/null +++ b/test/mosq_test.py @@ -0,0 +1,322 @@ +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 diff --git a/test/ssl/client-expired.crt b/test/ssl/client-expired.crt new file mode 100644 index 0000000..d6df974 --- /dev/null +++ b/test/ssl/client-expired.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICpDCCAg0CCQDNrg5WSiiRqDANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMC +R0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEdMBsG +A1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxDzANBgNVBAsMBkJyb2tlcjEXMBUG +A1UEAwwOYnJva2VyLXRlc3QtY2ExHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBs +ZS5jb20wHhcNMTIwNzAzMTQ0MDA3WhcNMTIwNzA0MTQ0MDA3WjCBizELMAkGA1UE +BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTEd +MBsGA1UECgwUTW9zcXVpdHRvIFRlc3QgU3VpdGUxFDASBgNVBAsMC0Jyb2tlciBU +ZXN0MR4wHAYDVQQDDBVsb2NhbGhvc3QtY2xpZW50LXRlc3QwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAM1kzx27D7TooydBNi6tTIMvstPZqFviwN5JGCKxs0wI +ZAdVP1HNeECioww4HiMO6J6IfcLppLDuWr1pteSG471MjGLPc0Z6UKNejKntM30p +7649fBZ9DIomVKTJHya/jtU8hIJfSpY29FKGOe3gVjg99nFZtB1dGnycQys5FRVZ +AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEACkRM53UdHTXhDNv+xZk6DJyNneEm0BWG +u9IExujL1YKbEIxlOncacDEzAYe3YbcnzN+1rgYel2l8Oq7Esb4OhlM5ftzfNLw5 +p2uM1QWzB1N1pwJ7BMtlkFuL+JI+VikaVdqvV9YhCDxIUUujoMsXLYzVOVMQbToJ +YedFIjsMj6s= +-----END CERTIFICATE----- diff --git a/test/ssl/client-revoked.crt b/test/ssl/client-revoked.crt new file mode 100644 index 0000000..ac70249 --- /dev/null +++ b/test/ssl/client-revoked.crt @@ -0,0 +1,17 @@ +-----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----- diff --git a/test/ssl/client-revoked.csr b/test/ssl/client-revoked.csr new file mode 100644 index 0000000..1314812 --- /dev/null +++ b/test/ssl/client-revoked.csr @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBzDCCATUCAQAwgYsxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2lu +Z2RvbTEOMAwGA1UEBwwFRGVyYnkxHTAbBgNVBAoMFE1vc3F1aXR0byBUZXN0IFN1 +aXRlMRQwEgYDVQQLDAtCcm9rZXIgVGVzdDEeMBwGA1UEAwwVbG9jYWxob3N0LWNs +aWVudC10ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNZM8duw+06KMn +QTYurUyDL7LT2ahb4sDeSRgisbNMCGQHVT9RzXhAoqMMOB4jDuieiH3C6aSw7lq9 +abXkhuO9TIxiz3NGelCjXoyp7TN9Ke+uPXwWfQyKJlSkyR8mv47VPISCX0qWNvRS +hjnt4FY4PfZxWbQdXRp8nEMrORUVWQIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEA +uzoEdsl3JkiNIviQzDtr67k6L/vogtVSgRnCFCel16Q0i1W+mVAwUqYArwf/7fwp +UhZPd8NLSVT1pn4Nj2a2Q7S3GMpMguiQlhCol7hZOrNpc0fDGg1JSmYjCEmm3TOl +Z49eyqmE8r4xdDEAyPk3u21HYrZ5RshyS/8vfPwqR9o= +-----END CERTIFICATE REQUEST----- diff --git a/test/ssl/client-revoked.key b/test/ssl/client-revoked.key new file mode 100644 index 0000000..479a528 --- /dev/null +++ b/test/ssl/client-revoked.key @@ -0,0 +1,15 @@ +-----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----- diff --git a/test/ssl/client.crt b/test/ssl/client.crt new file mode 100644 index 0000000..7b73d73 --- /dev/null +++ b/test/ssl/client.crt @@ -0,0 +1,17 @@ +-----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----- diff --git a/test/ssl/client.csr b/test/ssl/client.csr new file mode 100644 index 0000000..982d45c --- /dev/null +++ b/test/ssl/client.csr @@ -0,0 +1,13 @@ +-----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----- diff --git a/test/ssl/client.key b/test/ssl/client.key new file mode 100644 index 0000000..e98e2ce --- /dev/null +++ b/test/ssl/client.key @@ -0,0 +1,15 @@ +-----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----- diff --git a/test/ssl/crl.pem b/test/ssl/crl.pem new file mode 100644 index 0000000..650b350 --- /dev/null +++ b/test/ssl/crl.pem @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBmDCCAQECAQEwDQYJKoZIhvcNAQEFBQAwgaAxCzAJBgNVBAYTAkdCMRcwFQYD +VQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwGA1UEBwwFRGVyYnkxHTAbBgNVBAoMFE1v +c3F1aXR0byBUZXN0IFN1aXRlMQ8wDQYDVQQLDAZCcm9rZXIxFzAVBgNVBAMMDmJy +b2tlci10ZXN0LWNhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tFw0x +MzAyMjIyMTE0MjhaFw0yMzAyMjAyMTE0MjhaMBwwGgIJAM2uDlZKKJGnFw0xMjA3 +MDMxNTU4NDZaoA4wDDAKBgNVHRQEAwIBBDANBgkqhkiG9w0BAQUFAAOBgQB1eSbU +qouEY+28nNc7QkoDJqK/VgfVYSIgbkuaM8RP4TalndoA/eP4VJbOycZ6HbU10315 +sfYhoARx8xXtz/OIU470PSUKrbeNQMXoebp5YGZHl1Gd+FvClVgzAPoJCn68rd4N +mzkDJrdpBZCTt4yxv+vkLwdBmAetcLKj/lhpbg== +-----END X509 CRL----- diff --git a/test/ssl/demoCA/crlnumber b/test/ssl/demoCA/crlnumber new file mode 100644 index 0000000..eeee65e --- /dev/null +++ b/test/ssl/demoCA/crlnumber @@ -0,0 +1 @@ +05 diff --git a/test/ssl/demoCA/index.txt b/test/ssl/demoCA/index.txt new file mode 100644 index 0000000..ced7051 --- /dev/null +++ b/test/ssl/demoCA/index.txt @@ -0,0 +1 @@ +R 391118144000Z 120703155846Z CDAE0E564A2891A7 unknown /C=GB/ST=United Kingdom/L=Derby/O=Mosquitto Test Suite/OU=Broker Test/CN=localhost-client-test diff --git a/test/ssl/demoCA/index.txt.attr b/test/ssl/demoCA/index.txt.attr new file mode 100644 index 0000000..3a7e39e --- /dev/null +++ b/test/ssl/demoCA/index.txt.attr @@ -0,0 +1 @@ +unique_subject = no diff --git a/test/ssl/demoCA/serial b/test/ssl/demoCA/serial new file mode 100644 index 0000000..8a0f05e --- /dev/null +++ b/test/ssl/demoCA/serial @@ -0,0 +1 @@ +01 diff --git a/test/ssl/fake-ca.crt b/test/ssl/fake-ca.crt new file mode 100644 index 0000000..209af42 --- /dev/null +++ b/test/ssl/fake-ca.crt @@ -0,0 +1,25 @@ +-----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----- diff --git a/test/ssl/fake-ca.key b/test/ssl/fake-ca.key new file mode 100644 index 0000000..1edfb1b --- /dev/null +++ b/test/ssl/fake-ca.key @@ -0,0 +1,30 @@ +-----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----- diff --git a/test/ssl/readme.txt b/test/ssl/readme.txt new file mode 100644 index 0000000..bbb6ad3 --- /dev/null +++ b/test/ssl/readme.txt @@ -0,0 +1,2 @@ +This directory contains certificates and keys required for SSL testing. +The CA key has password "password". diff --git a/test/ssl/server-expired.crt b/test/ssl/server-expired.crt new file mode 100644 index 0000000..cf3824c --- /dev/null +++ b/test/ssl/server-expired.crt @@ -0,0 +1,17 @@ +-----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----- diff --git a/test/ssl/server.crt b/test/ssl/server.crt new file mode 100644 index 0000000..7a1b776 --- /dev/null +++ b/test/ssl/server.crt @@ -0,0 +1,17 @@ +-----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----- diff --git a/test/ssl/server.csr b/test/ssl/server.csr new file mode 100644 index 0000000..81f5dff --- /dev/null +++ b/test/ssl/server.csr @@ -0,0 +1,13 @@ +-----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----- diff --git a/test/ssl/server.key b/test/ssl/server.key new file mode 100644 index 0000000..dd76952 --- /dev/null +++ b/test/ssl/server.key @@ -0,0 +1,15 @@ +-----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----- diff --git a/test/ssl/test-ca-alt.crt b/test/ssl/test-ca-alt.crt new file mode 100644 index 0000000..37c540c --- /dev/null +++ b/test/ssl/test-ca-alt.crt @@ -0,0 +1,19 @@ +-----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----- diff --git a/test/ssl/test-ca-alt.key b/test/ssl/test-ca-alt.key new file mode 100644 index 0000000..2e14911 --- /dev/null +++ b/test/ssl/test-ca-alt.key @@ -0,0 +1,17 @@ +-----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----- diff --git a/test/ssl/test-ca.crt b/test/ssl/test-ca.crt new file mode 100644 index 0000000..64e29f9 --- /dev/null +++ b/test/ssl/test-ca.crt @@ -0,0 +1,19 @@ +-----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----- diff --git a/test/ssl/test-ca.key b/test/ssl/test-ca.key new file mode 100644 index 0000000..13f7a41 --- /dev/null +++ b/test/ssl/test-ca.key @@ -0,0 +1,17 @@ +-----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----- diff --git a/test/ssl/test-ca.srl b/test/ssl/test-ca.srl new file mode 100644 index 0000000..53b5530 --- /dev/null +++ b/test/ssl/test-ca.srl @@ -0,0 +1 @@ +CDAE0E564A2891AA