packages feed

toxcore-c (empty) → 0.2.11

raw patch · 15 files changed

+4069/−0 lines, 15 filesdep +QuickCheckdep +basedep +base16-bytestringsetup-changed

Dependencies added: QuickCheck, base, base16-bytestring, bytestring, bytestring-arbitrary, data-default-class, directory, hspec, saltine, toxcore-c

Files

+ LICENSE view
@@ -0,0 +1,675 @@+                    GNU GENERAL PUBLIC LICENSE+                       Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++                            Preamble++  The GNU General Public License is a free, copyleft license for+software and other kinds of works.++  The licenses for most software and other practical works are designed+to take away your freedom to share and change the works.  By contrast,+the GNU General Public License is intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users.  We, the Free Software Foundation, use the+GNU General Public License for most of our software; it applies also to+any other work released this way by its authors.  You can apply it to+your programs, too.++  When we speak of free software, we are referring to freedom, 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+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++  To protect your rights, we need to prevent others from denying you+these rights or asking you to surrender the rights.  Therefore, you have+certain responsibilities if you distribute copies of the software, or if+you modify it: responsibilities to respect the freedom of others.++  For example, if you distribute copies of such a program, whether+gratis or for a fee, you must pass on to the recipients the same+freedoms that you received.  You must make sure that they, too, receive+or can get the source code.  And you must show them these terms so they+know their rights.++  Developers that use the GNU GPL protect your rights with two steps:+(1) assert copyright on the software, and (2) offer you this License+giving you legal permission to copy, distribute and/or modify it.++  For the developers' and authors' protection, the GPL clearly explains+that there is no warranty for this free software.  For both users' and+authors' sake, the GPL requires that modified versions be marked as+changed, so that their problems will not be attributed erroneously to+authors of previous versions.++  Some devices are designed to deny users access to install or run+modified versions of the software inside them, although the manufacturer+can do so.  This is fundamentally incompatible with the aim of+protecting users' freedom to change the software.  The systematic+pattern of such abuse occurs in the area of products for individuals to+use, which is precisely where it is most unacceptable.  Therefore, we+have designed this version of the GPL to prohibit the practice for those+products.  If such problems arise substantially in other domains, we+stand ready to extend this provision to those domains in future versions+of the GPL, as needed to protect the freedom of users.++  Finally, every program is threatened constantly by software patents.+States should not allow patents to restrict development and use of+software on general-purpose computers, but in those that do, we wish to+avoid the special danger that patents applied to a free program could+make it effectively proprietary.  To prevent this, the GPL assures that+patents cannot be used to render the program non-free.++  The precise terms and conditions for copying, distribution and+modification follow.++                       TERMS AND CONDITIONS++  0. Definitions.++  "This License" refers to version 3 of the GNU General Public License.++  "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++  "The Program" refers to any copyrightable work licensed under this+License.  Each licensee is addressed as "you".  "Licensees" and+"recipients" may be individuals or organizations.++  To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy.  The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++  A "covered work" means either the unmodified Program or a work based+on the Program.++  To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy.  Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++  To "convey" a work means any kind of propagation that enables other+parties to make or receive copies.  Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++  An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License.  If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++  1. Source Code.++  The "source code" for a work means the preferred form of the work+for making modifications to it.  "Object code" means any non-source+form of a work.++  A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++  The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form.  A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++  The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities.  However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work.  For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++  The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++  The Corresponding Source for a work in source code form is that+same work.++  2. Basic Permissions.++  All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met.  This License explicitly affirms your unlimited+permission to run the unmodified Program.  The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work.  This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++  You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force.  You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright.  Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++  Conveying under any other circumstances is permitted solely under+the conditions stated below.  Sublicensing is not allowed; section 10+makes it unnecessary.++  3. Protecting Users' Legal Rights From Anti-Circumvention Law.++  No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++  When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++  4. Conveying Verbatim Copies.++  You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++  You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++  5. Conveying Modified Source Versions.++  You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++    a) The work must carry prominent notices stating that you modified+    it, and giving a relevant date.++    b) The work must carry prominent notices stating that it is+    released under this License and any conditions added under section+    7.  This requirement modifies the requirement in section 4 to+    "keep intact all notices".++    c) You must license the entire work, as a whole, under this+    License to anyone who comes into possession of a copy.  This+    License will therefore apply, along with any applicable section 7+    additional terms, to the whole of the work, and all its parts,+    regardless of how they are packaged.  This License gives no+    permission to license the work in any other way, but it does not+    invalidate such permission if you have separately received it.++    d) If the work has interactive user interfaces, each must display+    Appropriate Legal Notices; however, if the Program has interactive+    interfaces that do not display Appropriate Legal Notices, your+    work need not make them do so.++  A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit.  Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++  6. Conveying Non-Source Forms.++  You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++    a) Convey the object code in, or embodied in, a physical product+    (including a physical distribution medium), accompanied by the+    Corresponding Source fixed on a durable physical medium+    customarily used for software interchange.++    b) Convey the object code in, or embodied in, a physical product+    (including a physical distribution medium), accompanied by a+    written offer, valid for at least three years and valid for as+    long as you offer spare parts or customer support for that product+    model, to give anyone who possesses the object code either (1) a+    copy of the Corresponding Source for all the software in the+    product that is covered by this License, on a durable physical+    medium customarily used for software interchange, for a price no+    more than your reasonable cost of physically performing this+    conveying of source, or (2) access to copy the+    Corresponding Source from a network server at no charge.++    c) Convey individual copies of the object code with a copy of the+    written offer to provide the Corresponding Source.  This+    alternative is allowed only occasionally and noncommercially, and+    only if you received the object code with such an offer, in accord+    with subsection 6b.++    d) Convey the object code by offering access from a designated+    place (gratis or for a charge), and offer equivalent access to the+    Corresponding Source in the same way through the same place at no+    further charge.  You need not require recipients to copy the+    Corresponding Source along with the object code.  If the place to+    copy the object code is a network server, the Corresponding Source+    may be on a different server (operated by you or a third party)+    that supports equivalent copying facilities, provided you maintain+    clear directions next to the object code saying where to find the+    Corresponding Source.  Regardless of what server hosts the+    Corresponding Source, you remain obligated to ensure that it is+    available for as long as needed to satisfy these requirements.++    e) Convey the object code using peer-to-peer transmission, provided+    you inform other peers where the object code and Corresponding+    Source of the work are being offered to the general public at no+    charge under subsection 6d.++  A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++  A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling.  In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage.  For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product.  A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++  "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source.  The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++  If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information.  But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++  The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed.  Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++  Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++  7. Additional Terms.++  "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law.  If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++  When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it.  (Additional permissions may be written to require their own+removal in certain cases when you modify the work.)  You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++  Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++    a) Disclaiming warranty or limiting liability differently from the+    terms of sections 15 and 16 of this License; or++    b) Requiring preservation of specified reasonable legal notices or+    author attributions in that material or in the Appropriate Legal+    Notices displayed by works containing it; or++    c) Prohibiting misrepresentation of the origin of that material, or+    requiring that modified versions of such material be marked in+    reasonable ways as different from the original version; or++    d) Limiting the use for publicity purposes of names of licensors or+    authors of the material; or++    e) Declining to grant rights under trademark law for use of some+    trade names, trademarks, or service marks; or++    f) Requiring indemnification of licensors and authors of that+    material by anyone who conveys the material (or modified versions of+    it) with contractual assumptions of liability to the recipient, for+    any liability that these contractual assumptions directly impose on+    those licensors and authors.++  All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10.  If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term.  If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++  If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++  Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++  8. Termination.++  You may not propagate or modify a covered work except as expressly+provided under this License.  Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++  However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++  Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++  Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License.  If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++  9. Acceptance Not Required for Having Copies.++  You are not required to accept this License in order to receive or+run a copy of the Program.  Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance.  However,+nothing other than this License grants you permission to propagate or+modify any covered work.  These actions infringe copyright if you do+not accept this License.  Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++  10. Automatic Licensing of Downstream Recipients.++  Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License.  You are not responsible+for enforcing compliance by third parties with this License.++  An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations.  If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++  You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License.  For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++  11. Patents.++  A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based.  The+work thus licensed is called the contributor's "contributor version".++  A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version.  For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++  Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++  In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement).  To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++  If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients.  "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++  If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++  A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License.  You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++  Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++  12. No Surrender of Others' Freedom.++  If 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 convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all.  For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++  13. Use with the GNU Affero General Public License.++  Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU Affero General Public License into a single+combined work, and to convey the resulting work.  The terms of this+License will continue to apply to the part which is the covered work,+but the special requirements of the GNU Affero General Public License,+section 13, concerning interaction through a network will apply to the+combination as such.++  14. Revised Versions of this License.++  The Free Software Foundation may publish revised and/or new versions of+the GNU 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+Program specifies that a certain numbered version of the GNU General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation.  If the Program does not specify a version number of the+GNU General Public License, you may choose any version ever published+by the Free Software Foundation.++  If the Program specifies that a proxy can decide which future+versions of the GNU General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++  Later license versions may give you additional or different+permissions.  However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++  15. Disclaimer of Warranty.++  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++  16. Limitation of Liability.++  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++  17. Interpretation of Sections 15 and 16.++  If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++                     END OF TERMS AND CONDITIONS++            How to Apply These Terms to Your New Programs++  If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++  To do so, attach the following notices to the program.  It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++    <one line to give the program's name and a brief idea of what it does.>+    Copyright (C) <year>  <name of author>++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program 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 General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++  If the program does terminal interaction, make it output a short+notice like this when it starts in an interactive mode:++    <program>  Copyright (C) <year>  <name of author>+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+    This is free software, and you are welcome to redistribute it+    under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License.  Of course, your program's commands+might be different; for a GUI interface, you would use an "about box".++  You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU GPL, see+<http://www.gnu.org/licenses/>.++  The GNU General Public License does not permit incorporating your program+into proprietary programs.  If your program is a subroutine library, you+may consider it more useful to permit linking proprietary applications with+the library.  If this is what you want to do, use the GNU Lesser General+Public License instead of this License.  But first, please read+<http://www.gnu.org/philosophy/why-not-lgpl.html>.+
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ src/Network/Tox/C.hs view
@@ -0,0 +1,10 @@+module Network.Tox.C+  ( module M+  ) where++import           Network.Tox.C.Callbacks as M+import           Network.Tox.C.Constants as M+import           Network.Tox.C.Options   as M+import           Network.Tox.C.Tox       as M+import           Network.Tox.C.Type      as M+import           Network.Tox.C.Version   as M
+ src/Network/Tox/C/CEnum.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE Trustworthy                #-}+module Network.Tox.C.CEnum where++import           Control.Applicative   ((<$>))+import           Foreign.C.Types       (CInt)+import           Foreign.Marshal.Alloc (alloca)+import           Foreign.Ptr           (Ptr)+import           Foreign.Storable      (Storable (..))+++newtype CEnum a = CEnum { unCEnum :: CInt }+  deriving (Storable)++instance (Enum a, Show a) => Show (CEnum a) where+  show cen = show (toEnum $ fromIntegral $ unCEnum cen :: a)+++toCEnum :: Enum a => a -> CEnum a+toCEnum = CEnum . fromIntegral . fromEnum+++fromCEnum :: Enum a => CEnum a -> a+fromCEnum = toEnum . fromIntegral . unCEnum+++type CErr err = Ptr (CEnum err)++callErrFun :: (Eq err, Enum err, Bounded err)+           => (CErr err -> IO r) -> IO (Either err r)+callErrFun f = alloca $ \errPtr -> do+  res <- f errPtr+  err <- toEnum . fromIntegral . unCEnum <$> peek errPtr+  return $ if err /= minBound+    then Left  err+    else Right res
+ src/Network/Tox/C/Callbacks.hs view
@@ -0,0 +1,89 @@+module Network.Tox.C.Callbacks where++import           Control.Exception  (bracket)+import           Foreign.Ptr        (freeHaskellFunPtr, nullFunPtr)++import qualified Network.Tox.C.Tox  as Tox+import           Network.Tox.C.Type (Tox)+++-- | Low level event handler. The functions in this class are directly+-- registered with the corresponding C callback. We use 'StablePtr' to pass+-- opaque Haskell values around in C.+class CHandler a where+  cSelfConnectionStatus     :: Tox.SelfConnectionStatusCb     a+  cSelfConnectionStatus _ _ = return+  cFriendName               :: Tox.FriendNameCb               a+  cFriendName _ _ _ = return+  cFriendStatusMessage      :: Tox.FriendStatusMessageCb      a+  cFriendStatusMessage _ _ _ = return+  cFriendStatus             :: Tox.FriendStatusCb             a+  cFriendStatus _ _ _ = return+  cFriendConnectionStatus   :: Tox.FriendConnectionStatusCb   a+  cFriendConnectionStatus _ _ _ = return+  cFriendTyping             :: Tox.FriendTypingCb             a+  cFriendTyping _ _ _ = return+  cFriendReadReceipt        :: Tox.FriendReadReceiptCb        a+  cFriendReadReceipt _ _ _ = return+  cFriendRequest            :: Tox.FriendRequestCb            a+  cFriendRequest _ _ _ = return+  cFriendMessage            :: Tox.FriendMessageCb            a+  cFriendMessage _ _ _ _ = return+  cFileRecvControl          :: Tox.FileRecvControlCb          a+  cFileRecvControl _ _ _ _ = return+  cFileChunkRequest         :: Tox.FileChunkRequestCb         a+  cFileChunkRequest _ _ _ _ _ = return+  cFileRecv                 :: Tox.FileRecvCb                 a+  cFileRecv _ _ _ _ _ _ = return+  cFileRecvChunk            :: Tox.FileRecvChunkCb            a+  cFileRecvChunk _ _ _ _ _ = return+  cConferenceInvite         :: Tox.ConferenceInviteCb         a+  cConferenceInvite _ _ _ _ = return+  cConferenceMessage        :: Tox.ConferenceMessageCb        a+  cConferenceMessage _ _ _ _ _ = return+  cConferenceTitle          :: Tox.ConferenceTitleCb          a+  cConferenceTitle _ _ _ _ = return+  cConferencePeerName :: Tox.ConferencePeerNameCb a+  cConferencePeerName _ _ _ _ = return+  cConferencePeerListChanged :: Tox.ConferencePeerListChangedCb a+  cConferencePeerListChanged _ _ = return+  cFriendLossyPacket        :: Tox.FriendLossyPacketCb        a+  cFriendLossyPacket _ _ _ = return+  cFriendLosslessPacket     :: Tox.FriendLosslessPacketCb     a+  cFriendLosslessPacket _ _ _ = return+++-- | Installs an event handler into the passed 'Tox' instance. After performing+-- the IO action, all event handlers are reset to null. This function does not+-- save the original event handlers.+withCHandler :: CHandler a => Tox a -> IO r -> IO r+withCHandler tox =+  install Tox.tox_callback_self_connection_status       (Tox.selfConnectionStatusCb      cSelfConnectionStatus     ) .+  install Tox.tox_callback_friend_name                  (Tox.friendNameCb                cFriendName               ) .+  install Tox.tox_callback_friend_status_message        (Tox.friendStatusMessageCb       cFriendStatusMessage      ) .+  install Tox.tox_callback_friend_status                (Tox.friendStatusCb              cFriendStatus             ) .+  install Tox.tox_callback_friend_connection_status     (Tox.friendConnectionStatusCb    cFriendConnectionStatus   ) .+  install Tox.tox_callback_friend_typing                (Tox.friendTypingCb              cFriendTyping             ) .+  install Tox.tox_callback_friend_read_receipt          (Tox.friendReadReceiptCb         cFriendReadReceipt        ) .+  install Tox.tox_callback_friend_request               (Tox.friendRequestCb             cFriendRequest            ) .+  install Tox.tox_callback_friend_message               (Tox.friendMessageCb             cFriendMessage            ) .+  install Tox.tox_callback_file_recv_control            (Tox.fileRecvControlCb           cFileRecvControl          ) .+  install Tox.tox_callback_file_chunk_request           (Tox.fileChunkRequestCb          cFileChunkRequest         ) .+  install Tox.tox_callback_file_recv                    (Tox.fileRecvCb                  cFileRecv                 ) .+  install Tox.tox_callback_file_recv_chunk              (Tox.fileRecvChunkCb             cFileRecvChunk            ) .+  install Tox.tox_callback_conference_invite            (Tox.conferenceInviteCb          cConferenceInvite         ) .+  install Tox.tox_callback_conference_message           (Tox.conferenceMessageCb         cConferenceMessage        ) .+  install Tox.tox_callback_conference_title             (Tox.conferenceTitleCb           cConferenceTitle          ) .+  install Tox.tox_callback_conference_peer_name         (Tox.conferencePeerNameCb        cConferencePeerName       ) .+  install Tox.tox_callback_conference_peer_list_changed (Tox.conferencePeerListChangedCb cConferencePeerListChanged ) .+  install Tox.tox_callback_friend_lossy_packet          (Tox.friendLossyPacketCb         cFriendLossyPacket        ) .+  install Tox.tox_callback_friend_lossless_packet       (Tox.friendLosslessPacketCb      cFriendLosslessPacket     )+  where+    install cInstall wrapper action =+      bracket wrapper (uninstall cInstall) $ \cb -> do+        () <- cInstall tox cb+        action++    uninstall cInstall cb = do+      freeHaskellFunPtr cb+      cInstall tox nullFunPtr
+ src/Network/Tox/C/Constants.hs view
@@ -0,0 +1,50 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE Trustworthy #-}+module Network.Tox.C.Constants where++import           Data.Word (Word32)+++--------------------------------------------------------------------------------+--+-- :: Numeric constants+--+--------------------------------------------------------------------------------++-- | The size of a Tox Public Key in bytes.+foreign import ccall tox_public_key_size :: Word32++-- | The size of a Tox Secret Key in bytes.+foreign import ccall tox_secret_key_size :: Word32++-- | The size of a Tox address in bytes. Tox addresses are in the format+-- [Public Key ('tox_public_key_size' bytes)][nospam (4 bytes)][checksum (2 bytes)].+--+-- The checksum is computed over the Public Key and the nospam value. The first+-- byte is an XOR of all the even bytes (0, 2, 4, ...), the second byte is an+-- XOR of all the odd bytes (1, 3, 5, ...) of the Public Key and nospam.+foreign import ccall tox_address_size :: Word32++-- | Maximum length of a nickname in bytes.+foreign import ccall tox_max_name_length :: Word32++-- | Maximum length of a status message in bytes.+foreign import ccall tox_max_status_message_length :: Word32++-- | Maximum length of a friend request message in bytes.+foreign import ccall tox_max_friend_request_length :: Word32++-- | Maximum length of a single message after which it should be split.+foreign import ccall tox_max_message_length :: Word32++-- | Maximum size of custom packets. TODO: should be LENGTH?+foreign import ccall tox_max_custom_packet_size :: Word32++-- | The number of bytes in a hash generated by tox_hash.+foreign import ccall tox_hash_length :: Word32++-- | The number of bytes in a file id.+foreign import ccall tox_file_id_length :: Word32++-- | Maximum file name length for file transfers.+foreign import ccall tox_max_filename_length :: Word32
+ src/Network/Tox/C/Options.hs view
@@ -0,0 +1,317 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase    #-}+{-# LANGUAGE Safe          #-}+module Network.Tox.C.Options where++import           Control.Applicative ((<$>))+import           Control.Exception   (bracket)+import           Data.ByteString     (ByteString)+import qualified Data.ByteString     as BS+import           Data.Default.Class  (Default (..))+import           Data.Word           (Word16)+import           Foreign.C.String    (CString, peekCString, withCString)+import           Foreign.C.Types     (CInt (..), CSize (..))+import           Foreign.Ptr         (Ptr, nullPtr)+import           GHC.Generics        (Generic)++import           Network.Tox.C.CEnum++--------------------------------------------------------------------------------+--+-- :: Startup options+--+--------------------------------------------------------------------------------+++-- | Type of proxy used to connect to TCP relays.+data ProxyType+  = ProxyTypeNone+    -- Don't use a proxy.+  | ProxyTypeHttp+    -- HTTP proxy using CONNECT.+  | ProxyTypeSocks5+    -- SOCKS proxy for simple socket pipes.+  deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)+++-- Type of savedata to create the Tox instance from.+data SavedataType+  = SavedataTypeNone+    -- No savedata.+  | SavedataTypeToxSave+    -- Savedata is one that was obtained from tox_get_savedata+  | SavedataTypeSecretKey+    -- Savedata is a secret key of length 'tox_secret_key_size'+  deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)+++-- This struct contains all the startup options for Tox. You can either allocate+-- this object yourself, and pass it to tox_options_default, or call+-- tox_options_new to get a new default options object.+data Options = Options+  { ipv6Enabled  :: Bool+    -- The type of socket to create.+    --+    -- If this is set to false, an IPv4 socket is created, which subsequently+    -- only allows IPv4 communication.+    -- If it is set to true, an IPv6 socket is created, allowing both IPv4 and+    -- IPv6 communication.++  , udpEnabled   :: Bool+    -- Enable the use of UDP communication when available.+    --+    -- Setting this to false will force Tox to use TCP only. Communications will+    -- need to be relayed through a TCP relay node, potentially slowing them+    -- down. Disabling UDP support is necessary when using anonymous proxies or+    -- Tor.++  , proxyType    :: ProxyType+    -- Pass communications through a proxy.++  , proxyHost    :: String+    -- The IP address or DNS name of the proxy to be used.+    --+    -- If used, this must be non-'nullPtr' and be a valid DNS name. The name+    -- must not exceed 255 ('tox_max_filename_length') characters, and be in a+    -- NUL-terminated C string format (255 chars + 1 NUL byte).+    --+    -- This member is ignored (it can be 'nullPtr') if proxy_type is+    -- ProxyTypeNone.++  , proxyPort    :: Word16+    -- The port to use to connect to the proxy server.+    --+    -- Ports must be in the range (1, 65535). The value is ignored if proxy_type+    -- is ProxyTypeNone.++  , startPort    :: Word16+    -- The start port of the inclusive port range to attempt to use.+    --+    -- If both 'startPort' and 'endPort' are 0, the default port range will be+    -- used: [33445, 33545].+    --+    -- If either 'startPort' or 'endPort' is 0 while the other is non-zero, the+    -- non-zero port will be the only port in the range.+    --+    -- Having 'startPort' > 'endport' will yield the same behavior as if+    -- 'startPort' and 'endPort' were swapped.++  , endPort      :: Word16+    -- The end port of the inclusive port range to attempt to use.++  , tcpPort      :: Word16+    -- The port to use for the TCP server (relay). If 0, the TCP server is+    -- disabled.+    --+    -- Enabling it is not required for Tox to function properly.+    --+    -- When enabled, your Tox instance can act as a TCP relay for other Tox+    -- instance. This leads to increased traffic, thus when writing a client it+    -- is recommended to enable TCP server only if the user has an option to+    -- disable it.++  , savedataType :: SavedataType+    -- The type of savedata to load from.++  , savedataData :: ByteString+    -- The savedata bytes.+  }+  deriving (Eq, Read, Show, Generic)+++instance Default Options where+  def = Options+    { ipv6Enabled  = True+    , udpEnabled   = True+    , proxyType    = ProxyTypeNone+    , proxyHost    = ""+    , proxyPort    = 0+    , startPort    = 0+    , endPort      = 0+    , tcpPort      = 0+    , savedataType = SavedataTypeNone+    , savedataData = BS.empty+    }+++data OptionsStruct+type OptionsPtr = Ptr OptionsStruct+++foreign import ccall tox_options_get_ipv6_enabled     :: OptionsPtr -> IO Bool+foreign import ccall tox_options_get_udp_enabled      :: OptionsPtr -> IO Bool+foreign import ccall tox_options_get_proxy_type       :: OptionsPtr -> IO (CEnum ProxyType)+foreign import ccall tox_options_get_proxy_host       :: OptionsPtr -> IO CString+foreign import ccall tox_options_get_proxy_port       :: OptionsPtr -> IO Word16+foreign import ccall tox_options_get_start_port       :: OptionsPtr -> IO Word16+foreign import ccall tox_options_get_end_port         :: OptionsPtr -> IO Word16+foreign import ccall tox_options_get_tcp_port         :: OptionsPtr -> IO Word16+foreign import ccall tox_options_get_savedata_type    :: OptionsPtr -> IO (CEnum SavedataType)+foreign import ccall tox_options_get_savedata_data    :: OptionsPtr -> IO CString+foreign import ccall tox_options_get_savedata_length  :: OptionsPtr -> IO CSize++foreign import ccall tox_options_set_ipv6_enabled     :: OptionsPtr -> Bool -> IO ()+foreign import ccall tox_options_set_udp_enabled      :: OptionsPtr -> Bool -> IO ()+foreign import ccall tox_options_set_proxy_type       :: OptionsPtr -> CEnum ProxyType -> IO ()+foreign import ccall tox_options_set_proxy_host       :: OptionsPtr -> CString -> IO ()+foreign import ccall tox_options_set_proxy_port       :: OptionsPtr -> Word16 -> IO ()+foreign import ccall tox_options_set_start_port       :: OptionsPtr -> Word16 -> IO ()+foreign import ccall tox_options_set_end_port         :: OptionsPtr -> Word16 -> IO ()+foreign import ccall tox_options_set_tcp_port         :: OptionsPtr -> Word16 -> IO ()+foreign import ccall tox_options_set_savedata_type    :: OptionsPtr -> CEnum SavedataType -> IO ()+foreign import ccall tox_options_set_savedata_data    :: OptionsPtr -> CString -> CSize -> IO ()+foreign import ccall tox_options_set_savedata_length  :: OptionsPtr -> CSize -> IO ()+++data ErrOptionsNew+  = ErrOptionsNewOk+    -- The function returned successfully.++  | ErrOptionsNewMalloc+    -- The function was unable to allocate enough memory to store the internal+    -- structures for the Tox options object.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Allocates a new Tox_Options object and initialises it with the default+-- options. This function can be used to preserve long term ABI compatibility by+-- giving the responsibility of allocation and deallocation to the Tox library.+--+-- Objects returned from this function must be freed using the tox_options_free+-- function.+--+-- @return A new 'OptionsPtr' with default options or 'nullPtr' on failure.+foreign import ccall tox_options_new :: CErr ErrOptionsNew -> IO OptionsPtr++toxOptionsNew :: IO (Either ErrOptionsNew OptionsPtr)+toxOptionsNew = callErrFun tox_options_new+++-- | Releases all resources associated with an options objects.+--+-- Passing a pointer that was not returned by tox_options_new results in+-- undefined behaviour.+foreign import ccall tox_options_free :: OptionsPtr -> IO ()+++withToxOptions :: (OptionsPtr -> IO r) -> IO (Either ErrOptionsNew r)+withToxOptions f =+  bracket toxOptionsNew (either (const $ return ()) tox_options_free) $ \case+    Left err -> return $ Left err+    Right ok -> Right <$> f ok+++-- | Read 'Options' from an 'OptionsPtr'.+--+-- If the passed pointer is 'nullPtr', the behaviour is undefined.+peekToxOptions :: OptionsPtr -> IO Options+peekToxOptions ptr = do+  cIpv6Enabled    <- tox_options_get_ipv6_enabled    ptr+  cUdpEnabled     <- tox_options_get_udp_enabled     ptr+  cProxyType      <- tox_options_get_proxy_type      ptr+  cProxyHost      <- tox_options_get_proxy_host      ptr >>= peekNullableString+  cProxyPort      <- tox_options_get_proxy_port      ptr+  cStartPort      <- tox_options_get_start_port      ptr+  cEndPort        <- tox_options_get_end_port        ptr+  cTcpPort        <- tox_options_get_tcp_port        ptr+  cSavedataType   <- tox_options_get_savedata_type   ptr+  cSavedataData   <- tox_options_get_savedata_data   ptr+  cSavedataLength <- tox_options_get_savedata_length ptr+  cSavedata       <- BS.packCStringLen+                           ( cSavedataData+                           , fromIntegral cSavedataLength)+  return Options+    { ipv6Enabled    = cIpv6Enabled+    , udpEnabled     = cUdpEnabled+    , proxyType      = fromCEnum cProxyType+    , proxyHost      = cProxyHost+    , proxyPort      = cProxyPort+    , startPort      = cStartPort+    , endPort        = cEndPort+    , tcpPort        = cTcpPort+    , savedataType   = fromCEnum cSavedataType+    , savedataData   = cSavedata+    }++  where+    -- 'peekCString' doesn't handle NULL strings as empty, unlike+    -- 'packCStringLen', which ignores the pointer to zero-length 'CString's.+    peekNullableString p =+      if p == nullPtr+        then return ""+        else peekCString p+++-- | Save the options from the passed 'OptionsPtr', perform an IO action, and+-- restore the original values.+--+-- If the passed pointer is 'nullPtr', the behaviour is undefined.+saveToxOptions :: OptionsPtr -> IO r -> IO r+saveToxOptions ptr =+  bracket saveOptions restoreOptions . const+  where+    saveOptions = do+      v0 <- tox_options_get_ipv6_enabled    ptr+      v1 <- tox_options_get_udp_enabled     ptr+      v2 <- tox_options_get_proxy_type      ptr+      v3 <- tox_options_get_proxy_host      ptr+      v4 <- tox_options_get_proxy_port      ptr+      v5 <- tox_options_get_start_port      ptr+      v6 <- tox_options_get_end_port        ptr+      v7 <- tox_options_get_tcp_port        ptr+      v8 <- tox_options_get_savedata_type   ptr+      sd <- tox_options_get_savedata_data   ptr+      sl <- tox_options_get_savedata_length ptr+      return (v0, v1, v2, v3, v4, v5, v6, v7, v8, sd, sl)++    restoreOptions (v0, v1, v2, v3, v4, v5, v6, v7, v8, sd, sl) = do+      tox_options_set_ipv6_enabled    ptr v0+      tox_options_set_udp_enabled     ptr v1+      tox_options_set_proxy_type      ptr v2+      tox_options_set_proxy_host      ptr v3+      tox_options_set_proxy_port      ptr v4+      tox_options_set_start_port      ptr v5+      tox_options_set_end_port        ptr v6+      tox_options_set_tcp_port        ptr v7+      tox_options_set_savedata_type   ptr v8+      tox_options_set_savedata_data   ptr sd sl+      tox_options_set_savedata_length ptr sl+++-- | Fill in the 'Options' values into the 'OptionsPtr' and perform the IO+-- action afterwards.+--+-- This function restores the original values from the 'OptionsPtr' after+-- performing the action.+--+-- If the passed pointer is 'nullPtr', the behaviour is undefined.+pokeToxOptions :: Options -> OptionsPtr -> IO r -> IO r+pokeToxOptions options ptr action =+  saveToxOptions ptr $+    withCString (proxyHost options) $ \host ->+      BS.useAsCStringLen (savedataData options) $ \(saveData, saveLenInt) -> do+        let saveLen = fromIntegral saveLenInt+        tox_options_set_ipv6_enabled    ptr $ ipv6Enabled options+        tox_options_set_udp_enabled     ptr $ udpEnabled options+        tox_options_set_proxy_type      ptr $ toCEnum $ proxyType options+        tox_options_set_proxy_host      ptr host+        tox_options_set_proxy_port      ptr $ proxyPort options+        tox_options_set_start_port      ptr $ startPort options+        tox_options_set_end_port        ptr $ endPort options+        tox_options_set_tcp_port        ptr $ tcpPort options+        tox_options_set_savedata_type   ptr $ toCEnum $ savedataType options+        tox_options_set_savedata_data   ptr saveData saveLen+        tox_options_set_savedata_length ptr saveLen+        action+++-- | Allocate a new C options pointer, fills in the values from 'Options',+-- calls the processor function, and deallocates the options pointer.+--+-- The 'OptionsPtr' passed to the processor function is never 'nullPtr'. If+-- allocation fails, the IO action evaluates to 'Left' with an appropriate+-- error code.+withOptions :: Options -> (OptionsPtr -> IO r) -> IO (Either ErrOptionsNew r)+withOptions options f =+  withToxOptions $ \ptr ->+    pokeToxOptions options ptr (f ptr)
+ src/Network/Tox/C/Tox.hs view
@@ -0,0 +1,2405 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE Safe       #-}+-- | Public core API for Tox clients.+--+-- Every function that can fail takes a function-specific error code pointer+-- that can be used to diagnose problems with the Tox state or the function+-- arguments. The error code pointer can be 'nullPtr', which does not influence+-- the function's behaviour, but can be done if the reason for failure is+-- irrelevant to the client.+--+-- The exception to this rule are simple allocation functions whose only failure+-- mode is allocation failure. They return 'nullPtr' in that case, and do not+-- set an error code.+--+-- Every error code type has an OK value to which functions will set their error+-- code value on success. Clients can keep their error code uninitialised before+-- passing it to a function. The library guarantees that after returning, the+-- value pointed to by the error code pointer has been initialised.+--+-- Functions with pointer parameters often have a 'nullPtr' error code, meaning+-- they could not perform any operation, because one of the required parameters+-- was 'nullPtr'. Some functions operate correctly or are defined as effectless+-- on 'nullPtr'.+--+-- Some functions additionally return a value outside their return type domain,+-- or a bool containing true on success and false on failure.+--+-- All functions that take a Tox instance pointer will cause undefined behaviour+-- when passed a 'nullPtr' Tox pointer.+--+-- All integer values are expected in host byte order.+--+-- Functions with parameters with enum types cause unspecified behaviour if the+-- enumeration value is outside the valid range of the type. If possible, the+-- function will try to use a sane default, but there will be no error code, and+-- one possible action for the function to take is to have no effect.+--+-- \subsection events Events and callbacks+--+-- Events are handled by callbacks. One callback can be registered per event.+-- All events have a callback function type named `tox_{event}_cb` and a+-- function to register it named `tox_callback_{event}`. Passing a 'nullPtr'+-- callback will result in no callback being registered for that event. Only one+-- callback per event can be registered, so if a client needs multiple event+-- listeners, it needs to implement the dispatch functionality itself.+--+-- \subsection threading Threading implications+--+-- It is possible to run multiple concurrent threads with a Tox instance for+-- each thread. It is also possible to run all Tox instances in the same thread.+-- A common way to run Tox (multiple or single instance) is to have one thread+-- running a simple tox_iterate loop, sleeping for tox_iteration_interval+-- milliseconds on each iteration.+--+-- If you want to access a single Tox instance from multiple threads, access to+-- the instance must be synchronised. While multiple threads can concurrently+-- access multiple different Tox instances, no more than one API function can+-- operate on a single instance at any given time.+--+-- Functions that write to variable length byte arrays will always have a size+-- function associated with them. The result of this size function is only valid+-- until another mutating function (one that takes a pointer to non-const Tox)+-- is called. Thus, clients must ensure that no other thread calls a mutating+-- function between the call to the size function and the call to the retrieval+-- function.+--+-- E.g. to get the current nickname, one would write+--+-- \code+-- CSize length = tox_self_get_name_size(tox);+-- CString name = malloc(length);+-- if (!name) abort();+-- tox_self_get_name(tox, name);+-- \endcode+--+-- If any other thread calls tox_self_set_name while this thread is allocating+-- memory, the length may have become invalid, and the call to tox_self_get_name+-- may cause undefined behaviour.+--+module Network.Tox.C.Tox where++import           Control.Applicative     ((<$>))+import           Control.Concurrent.MVar (MVar, modifyMVar_)+import           Control.Exception       (bracket)+import           Control.Monad           ((>=>))+import qualified Data.ByteString         as BS+import           Data.Word               (Word16, Word32, Word64)+import           Foreign.C.String        (CString, peekCStringLen, withCString,+                                          withCStringLen)+import           Foreign.C.Types         (CChar (..), CInt (..), CSize (..),+                                          CTime (..))+import           Foreign.Marshal.Alloc   (alloca)+import           Foreign.Marshal.Array   (allocaArray, peekArray)+import           Foreign.Ptr             (FunPtr, Ptr, nullPtr)+import           Foreign.StablePtr       (deRefStablePtr, freeStablePtr,+                                          newStablePtr)+import           Foreign.Storable        (peek)+import           System.Posix.Types      (EpochTime)++import           Network.Tox.C.CEnum+import           Network.Tox.C.Constants+import           Network.Tox.C.Options+import           Network.Tox.C.Type+++--------------------------------------------------------------------------------+--+-- :: Global types+--+--------------------------------------------------------------------------------++-- Should we introduce such types?+-- newtype FriendNum = FriendNum { friendNum :: Word32 } deriving (Eq, Ord, Read, Show)+-- newtype ConferenceNum = ConferenceNum { conferenceNum :: Word32 } deriving (Eq, Ord, Read, Show)+-- newtype PeerNum = PeerNum { peerNum :: Word32 } deriving (Eq, Ord, Read, Show)+-- newtype FileNum = FileNum { fileNum :: Word32 } deriving (Eq, Ord, Read, Show)++-- | Represents the possible statuses a client can have.+data UserStatus+  = UserStatusNone+    -- ^ User is online and available.+  | UserStatusAway+    -- ^ User is away. Clients can set this e.g. after a user defined inactivity+    -- time.+  | UserStatusBusy+    -- ^ User is busy. Signals to other clients that this client does not+    -- currently wish to communicate.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Represents message types for tox_friend_send_message and group chat+-- messages.+data MessageType+  = MessageTypeNormal+    -- ^ Normal text message. Similar to PRIVMSG on IRC.+  | MessageTypeAction+    -- ^ A message describing an user action. This is similar to /me (CTCP+    -- ACTION) on IRC.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++--------------------------------------------------------------------------------+--+-- :: Creation and destruction+--+--------------------------------------------------------------------------------+++data ErrNew+  = ErrNewOk+    -- The function returned successfully.++  | ErrNewNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrNewMalloc+    -- The function was unable to allocate enough memory to store the internal+    -- structures for the Tox object.++  | ErrNewPortAlloc+    -- The function was unable to bind to a port. This may mean that all ports+    -- have already been bound, e.g. by other Tox instances, or it may mean a+    -- permission error. You may be able to gather more information from errno.++  | ErrNewProxyBadType+    -- proxy_type was invalid.++  | ErrNewProxyBadHost+    -- proxy_type was valid but the proxy_host passed had an invalid format or+    -- was 'nullPtr'.++  | ErrNewProxyBadPort+    -- proxy_type was valid, but the proxy_port was invalid.++  | ErrNewProxyNotFound+    -- The proxy address passed could not be resolved.++  | ErrNewLoadEncrypted+    -- The byte array to be loaded contained an encrypted save.++  | ErrNewLoadBadFormat+    -- The data format was invalid. This can happen when loading data that was+    -- saved by an older version of Tox, or when the data has been corrupted.+    -- When loading from badly formatted data, some data may have been loaded,+    -- and the rest is discarded. Passing an invalid length parameter also+    -- causes this error.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- @brief Creates and initialises a new Tox instance with the options passed.+--+-- This function will bring the instance into a valid state. Running the event+-- loop with a new instance will operate correctly.+--+-- If loading failed or succeeded only partially, the new or partially loaded+-- instance is returned and an error code is set.+--+-- @param options An options object as described above. If this parameter is+--   'nullPtr', the default options are used.+--+-- @see tox_iterate for the event loop.+--+-- @return A new Tox instance pointer on success or 'nullPtr' on failure.+foreign import ccall tox_new :: OptionsPtr -> CErr ErrNew -> IO (Tox a)++toxNew :: OptionsPtr -> IO (Either ErrNew (Tox a))+toxNew = callErrFun . tox_new++-- | Releases all resources associated with the Tox instance and disconnects+-- from the network.+--+-- After calling this function, the Tox pointer becomes invalid. No other+-- functions can be called, and the pointer value can no longer be read.+foreign import ccall tox_kill :: Tox a -> IO ()++toxKill :: Tox a -> IO ()+toxKill = tox_kill++withTox :: OptionsPtr -> (Tox a -> IO r) -> IO (Either ErrNew r)+withTox options f =+  bracket (toxNew options) (either (const $ return ()) toxKill) $ \case+    Left err -> return $ Left err+    Right ok -> Right <$> f ok+++withDefaultTox :: (Tox a -> IO r) -> IO (Either ErrNew r)+withDefaultTox = withTox nullPtr+++-- | Calculates the number of bytes required to store the tox instance with+-- tox_get_savedata. This function cannot fail. The result is always greater+-- than 0.+--+-- @see threading for concurrency implications.+foreign import ccall tox_get_savedata_size :: Tox a -> IO CSize++-- | Store all information associated with the tox instance to a byte array.+--+-- @param data A memory region large enough to store the tox instance data.+--   Call tox_get_savedata_size to find the number of bytes required. If this+--   parameter is 'nullPtr', this function has no effect.+foreign import ccall tox_get_savedata :: Tox a -> CString -> IO ()++toxGetSavedata :: Tox a -> IO BS.ByteString+toxGetSavedata tox = do+  savedataLen <- tox_get_savedata_size tox+  allocaArray (fromIntegral savedataLen) $ \savedataPtr -> do+    tox_get_savedata tox savedataPtr+    BS.packCStringLen (savedataPtr, fromIntegral savedataLen)+++--------------------------------------------------------------------------------+--+-- :: Connection lifecycle and event loop+--+--------------------------------------------------------------------------------+++data ErrBootstrap+  = ErrBootstrapOk+    -- The function returned successfully.++  | ErrBootstrapNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrBootstrapBadHost+    -- The address could not be resolved to an IP address, or the IP address+    -- passed was invalid.++  | ErrBootstrapBadPort+    -- The port passed was invalid. The valid port range is (1, 65535).+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Sends a "get nodes" request to the given bootstrap node with IP, port, and+-- public key to setup connections.+--+-- This function will attempt to connect to the node using UDP. You must use+-- this function even if Options.udp_enabled was set to false.+--+-- @param address The hostname or IP address (IPv4 or IPv6) of the node.+-- @param port The port on the host on which the bootstrap Tox instance is+--   listening.+-- @param public_key The long term public key of the bootstrap node+--   ('tox_public_key_size' bytes).+-- @return true on success.+foreign import ccall tox_bootstrap :: Tox a -> CString -> Word16 -> CString -> CErr ErrBootstrap -> IO ()++callBootstrapFun+  :: (Tox a -> CString -> Word16 -> CString -> CErr ErrBootstrap -> IO ())+  -> Tox a -> String -> Word16 -> BS.ByteString -> IO (Either ErrBootstrap ())+callBootstrapFun f tox address port pubKey =+  withCString address $ \address' ->+    BS.useAsCString pubKey $ \pubKey' ->+      callErrFun $ f tox address' (fromIntegral port) pubKey'++toxBootstrap :: Tox a -> String -> Word16 -> BS.ByteString -> IO (Either ErrBootstrap ())+toxBootstrap = callBootstrapFun tox_bootstrap+++-- | Adds additional host:port pair as TCP relay.+--+-- This function can be used to initiate TCP connections to different ports on+-- the same bootstrap node, or to add TCP relays without using them as+-- bootstrap nodes.+--+-- @param address The hostname or IP address (IPv4 or IPv6) of the TCP relay.+-- @param port The port on the host on which the TCP relay is listening.+-- @param public_key The long term public key of the TCP relay+--   ('tox_public_key_size' bytes).+-- @return true on success.+foreign import ccall tox_add_tcp_relay :: Tox a -> CString -> Word16 -> CString -> CErr ErrBootstrap -> IO ()++toxAddTcpRelay :: Tox a -> String -> Word16 -> BS.ByteString -> IO (Either ErrBootstrap ())+toxAddTcpRelay = callBootstrapFun tox_add_tcp_relay+++-- | Protocols that can be used to connect to the network or friends.+data Connection+  = ConnectionNone+    -- There is no connection. This instance, or the friend the state change is+    -- about, is now offline.++  | ConnectionTcp+    -- A TCP connection has been established. For the own instance, this means+    -- it is connected through a TCP relay, only. For a friend, this means that+    -- the connection to that particular friend goes through a TCP relay.++  | ConnectionUdp+    -- A UDP connection has been established. For the own instance, this means+    -- it is able to send UDP packets to DHT nodes, but may still be connected+    -- to a TCP relay. For a friend, this means that the connection to that+    -- particular friend was built using direct UDP packets.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | @param connection_status Whether we are connected to the DHT.+type SelfConnectionStatusCb a = Tox a -> Connection -> a -> IO a+type CSelfConnectionStatusCb a = Tox a -> CEnum Connection -> UserData a -> IO ()+foreign import ccall "wrapper" wrapSelfConnectionStatusCb :: CSelfConnectionStatusCb a -> IO (FunPtr (CSelfConnectionStatusCb a))++callSelfConnectionStatusCb :: SelfConnectionStatusCb a -> CSelfConnectionStatusCb a+callSelfConnectionStatusCb f tox conn = deRefStablePtr >=> (`modifyMVar_` f tox (fromCEnum conn))++selfConnectionStatusCb :: SelfConnectionStatusCb a -> IO (FunPtr (CSelfConnectionStatusCb a))+selfConnectionStatusCb = wrapSelfConnectionStatusCb . callSelfConnectionStatusCb+++-- | Set the callback for the `self_connection_status` event. Pass 'nullPtr' to+-- unset.+--+-- This event is triggered whenever there is a change in the DHT connection+-- state. When disconnected, a client may choose to call tox_bootstrap again, to+-- reconnect to the DHT. Note that this state may frequently change for short+-- amounts of time. Clients should therefore not immediately bootstrap on+-- receiving a disconnect.+--+-- TODO: how long should a client wait before bootstrapping again?+foreign import ccall tox_callback_self_connection_status :: Tox a -> FunPtr (CSelfConnectionStatusCb a) -> IO ()++-- | Return the time in milliseconds before tox_iterate() should be called again+-- for optimal performance.+foreign import ccall tox_iteration_interval :: Tox a -> IO Word32+toxIterationInterval :: Tox a -> IO Word32+toxIterationInterval = tox_iteration_interval++-- | The main loop that needs to be run in intervals of tox_iteration_interval()+-- milliseconds.+foreign import ccall tox_iterate :: Tox a -> UserData a -> IO ()+toxIterate :: Tox a -> MVar a -> IO ()+toxIterate tox ud = bracket (newStablePtr ud) freeStablePtr (tox_iterate tox)+++--------------------------------------------------------------------------------+--+-- :: Internal client information (Tox address/id)+--+--------------------------------------------------------------------------------+++-- | Writes the Tox friend address of the client to a byte array. The address is+-- not in human-readable format. If a client wants to display the address,+-- formatting is required.+--+-- @param address A memory region of at least 'tox_address_size' bytes. If this+--   parameter is 'nullPtr', this function has no effect.+-- @see 'tox_address_size' for the address format.+foreign import ccall tox_self_get_address :: Tox a -> CString -> IO ()++toxSelfGetAddress :: Tox a -> IO BS.ByteString+toxSelfGetAddress tox =+  let addrLen = fromIntegral tox_address_size in+  allocaArray addrLen $ \addrPtr -> do+    tox_self_get_address tox addrPtr+    BS.packCStringLen (addrPtr, addrLen)++-- | Set the 4-byte nospam part of the address.+--+-- @param nospam Any 32 bit unsigned integer.+foreign import ccall tox_self_set_nospam :: Tox a -> Word32 -> IO ()+toxSelfSetNospam :: Tox a -> Word32 -> IO ()+toxSelfSetNospam = tox_self_set_nospam++-- | Get the 4-byte nospam part of the address.+foreign import ccall tox_self_get_nospam :: Tox a -> IO Word32+toxSelfGetNospam :: Tox a -> IO Word32+toxSelfGetNospam = tox_self_get_nospam++-- | Copy the Tox Public Key (long term) from the Tox object.+--+-- @param public_key A memory region of at least 'tox_public_key_size' bytes. If+--   this parameter is 'nullPtr', this function has no effect.+foreign import ccall tox_self_get_public_key :: Tox a -> CString -> IO ()++toxSelfGetPublicKey :: Tox a -> IO BS.ByteString+toxSelfGetPublicKey tox =+  let pkLen = fromIntegral tox_public_key_size in+  allocaArray pkLen $ \pkPtr -> do+    tox_self_get_public_key tox pkPtr+    BS.packCStringLen (pkPtr, pkLen)++-- | Copy the Tox Secret Key from the Tox object.+--+-- @param secret_key A memory region of at least 'tox_secret_key_size' bytes. If+--   this parameter is 'nullPtr', this function has no effect.+foreign import ccall tox_self_get_secret_key :: Tox a -> CString -> IO ()++toxSelfGetSecretKey :: Tox a -> IO BS.ByteString+toxSelfGetSecretKey tox =+  let skLen = fromIntegral tox_secret_key_size in+  allocaArray skLen $ \skPtr -> do+    tox_self_get_secret_key tox skPtr+    BS.packCStringLen (skPtr, skLen)+++--------------------------------------------------------------------------------+--+-- :: User-visible client information (nickname/status)+--+--------------------------------------------------------------------------------+++-- | Common error codes for all functions that set a piece of user-visible+-- client information.+data ErrSetInfo+  = ErrSetInfoOk+    -- The function returned successfully.++  | ErrSetInfoNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrSetInfoTooLong+    -- Information length exceeded maximum permissible size.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Set the nickname for the Tox client.+--+-- Nickname length cannot exceed 'tox_max_name_length'. If length is 0, the name+-- parameter is ignored (it can be 'nullPtr'), and the nickname is set back to+-- empty.+--+-- @param name A byte array containing the new nickname.+-- @param length The size of the name byte array.+--+-- @return true on success.+foreign import ccall tox_self_set_name :: Tox a -> CString -> CSize -> CErr ErrSetInfo -> IO ()+callSelfSetNameFun :: (Tox a -> CString -> CSize -> CErr ErrSetInfo -> IO ()) ->+                      Tox a -> String -> IO (Either ErrSetInfo ())+callSelfSetNameFun f tox name =+  withCStringLen name $ \(nameStr, nameLen) ->+    callErrFun $ f tox nameStr (fromIntegral nameLen)++toxSelfSetName :: Tox a -> String -> IO (Either ErrSetInfo ())+toxSelfSetName = callSelfSetNameFun tox_self_set_name+++-- | Return the length of the current nickname as passed to tox_self_set_name.+--+-- If no nickname was set before calling this function, the name is empty,+-- and this function returns 0.+--+-- @see threading for concurrency implications.+foreign import ccall tox_self_get_name_size :: Tox a -> IO CSize++-- | Write the nickname set by tox_self_set_name to a byte array.+--+-- If no nickname was set before calling this function, the name is empty,+-- and this function has no effect.+--+-- Call tox_self_get_name_size to find out how much memory to allocate for+-- the result.+--+-- @param name A valid memory location large enough to hold the nickname.+--   If this parameter is NULL, the function has no effect.+foreign import ccall tox_self_get_name :: Tox a -> CString -> IO ()++toxSelfGetName :: Tox a -> IO String+toxSelfGetName tox = do+  nameLen <- tox_self_get_name_size tox+  allocaArray (fromIntegral nameLen) $ \namePtr -> do+    tox_self_get_name tox namePtr+    peekCStringLen (namePtr, fromIntegral nameLen)+++-- | Set the client's status message.+--+-- Status message length cannot exceed 'tox_max_status_message_length'. If+-- length is 0, the status parameter is ignored (it can be 'nullPtr'), and the+-- user status is set back to empty.+foreign import ccall tox_self_set_status_message :: Tox a -> CString -> CSize -> CErr ErrSetInfo -> IO ()+callSelfSetStatusMessageFun :: (Tox a -> CString -> CSize -> CErr ErrSetInfo -> IO ()) ->+                               Tox a -> String -> IO (Either ErrSetInfo ())+callSelfSetStatusMessageFun f tox statusMsg =+  withCStringLen statusMsg $ \(statusMsgStr, statusMsgLen) ->+    callErrFun $ f tox statusMsgStr (fromIntegral statusMsgLen)++toxSelfSetStatusMessage :: Tox a -> String -> IO (Either ErrSetInfo ())+toxSelfSetStatusMessage = callSelfSetStatusMessageFun tox_self_set_status_message+++-- | Return the length of the current status message as passed to tox_self_set_status_message.+--+-- If no status message was set before calling this function, the status+-- is empty, and this function returns 0.+--+-- @see threading for concurrency implications.+foreign import ccall tox_self_get_status_message_size :: Tox a -> IO CSize+++-- | Write the status message set by tox_self_set_status_message to a byte array.+--+-- If no status message was set before calling this function, the status is+-- empty, and this function has no effect.+--+-- Call tox_self_get_status_message_size to find out how much memory to allocate for+-- the result.+--+-- @param status_message A valid memory location large enough to hold the+--   status message. If this parameter is NULL, the function has no effect.+foreign import ccall tox_self_get_status_message :: Tox a -> CString -> IO ()++toxSelfGetStatusMessage :: Tox a -> IO String+toxSelfGetStatusMessage tox = do+  statusMessageLen <- tox_self_get_status_message_size tox+  allocaArray (fromIntegral statusMessageLen) $ \statusMessagePtr -> do+    tox_self_get_status_message tox statusMessagePtr+    peekCStringLen (statusMessagePtr, fromIntegral statusMessageLen)+++-- | Set the client's user status.+--+-- @param user_status One of the user statuses listed in the enumeration above.+foreign import ccall tox_self_set_status :: Tox a -> CEnum UserStatus -> IO ()+toxSelfSetStatus :: Tox a -> UserStatus -> IO ()+toxSelfSetStatus tox userStatus = tox_self_set_status tox $ toCEnum userStatus+++--------------------------------------------------------------------------------+--+-- :: Friend list management+--+--------------------------------------------------------------------------------+++data ErrFriendAdd+  = ErrFriendAddOk+    -- The function returned successfully.++  | ErrFriendAddNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrFriendAddTooLong+    -- The length of the friend request message exceeded+    -- 'tox_max_friend_request_length'.++  | ErrFriendAddNoMessage+    -- The friend request message was empty. This, and the TooLong code will+    -- never be returned from tox_friend_add_norequest.++  | ErrFriendAddOwnKey+    -- The friend address belongs to the sending client.++  | ErrFriendAddAlreadySent+    -- A friend request has already been sent, or the address belongs to a+    -- friend that is already on the friend list.++  | ErrFriendAddBadChecksum+    -- The friend address checksum failed.++  | ErrFriendAddSetNewNospam+    -- The friend was already there, but the nospam value was different.++  | ErrFriendAddMalloc+    -- A memory allocation failed when trying to increase the friend list size.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Add a friend to the friend list and send a friend request.+--+-- A friend request message must be at least 1 byte long and at most+-- 'tox_max_friend_request_length'.+--+-- Friend numbers are unique identifiers used in all functions that operate on+-- friends. Once added, a friend number is stable for the lifetime of the Tox+-- object. After saving the state and reloading it, the friend numbers may not+-- be the same as before. Deleting a friend creates a gap in the friend number+-- set, which is filled by the next adding of a friend. Any pattern in friend+-- numbers should not be relied on.+--+-- If more than INT32_MAX friends are added, this function causes undefined+-- behaviour.+--+-- @param address The address of the friend (returned by tox_self_get_address of+--   the friend you wish to add) it must be 'tox_address_size' bytes.+-- @param message The message that will be sent along with the friend request.+-- @param length The length of the data byte array.+--+-- @return the friend number on success, UINT32_MAX on failure.+foreign import ccall tox_friend_add :: Tox a -> CString -> CString -> CSize -> CErr ErrFriendAdd -> IO Word32+callFriendAddFun :: (Tox a -> CString -> CString -> CSize -> CErr ErrFriendAdd -> IO Word32) ->+                    Tox a -> BS.ByteString -> String -> IO (Either ErrFriendAdd Word32)+callFriendAddFun f tox address message =+  withCStringLen message $ \(msgStr, msgLen) ->+    BS.useAsCString address $ \addr' ->+      callErrFun $ f tox addr' msgStr (fromIntegral msgLen)++toxFriendAdd :: Tox a -> BS.ByteString -> String -> IO (Either ErrFriendAdd Word32)+toxFriendAdd = callFriendAddFun tox_friend_add++-- | Add a friend without sending a friend request.+--+-- This function is used to add a friend in response to a friend request. If the+-- client receives a friend request, it can be reasonably sure that the other+-- client added this client as a friend, eliminating the need for a friend+-- request.+--+-- This function is also useful in a situation where both instances are+-- controlled by the same entity, so that this entity can perform the mutual+-- friend adding. In this case, there is no need for a friend request, either.+--+-- @param public_key A byte array of length 'tox_public_key_size' containing the+--   Public Key (not the Address) of the friend to add.+--+-- @return the friend number on success, UINT32_MAX on failure.+-- @see tox_friend_add for a more detailed description of friend numbers.+foreign import ccall tox_friend_add_norequest :: Tox a -> CString -> CErr ErrFriendAdd -> IO Word32+callFriendAddNorequestFun :: (Tox a -> CString -> CErr ErrFriendAdd -> IO Word32) ->+                    Tox a -> BS.ByteString -> IO (Either ErrFriendAdd Word32)+callFriendAddNorequestFun f tox address =+  BS.useAsCString address $ \addr' ->+    callErrFun $ f tox addr'++toxFriendAddNorequest :: Tox a -> BS.ByteString -> IO (Either ErrFriendAdd Word32)+toxFriendAddNorequest = callFriendAddNorequestFun tox_friend_add_norequest+++data ErrFriendDelete+  = ErrFriendDeleteOk+    -- The function returned successfully.++  | ErrFriendDeleteFriendNotFound+    -- There was no friend with the given friend number. No friends were+    -- deleted.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Remove a friend from the friend list.+--+-- This does not notify the friend of their deletion. After calling this+-- function, this client will appear offline to the friend and no communication+-- can occur between the two.+--+-- @param friend_number Friend number for the friend to be deleted.+--+-- @return true on success.+foreign import ccall tox_friend_delete :: Tox a -> Word32 -> CErr ErrFriendDelete -> IO ()++toxFriendDelete :: Tox a -> Word32 -> IO (Either ErrFriendDelete ())+toxFriendDelete tox fn = callErrFun $ tox_friend_delete tox fn+++--------------------------------------------------------------------------------+--+-- :: Friend list queries+--+--------------------------------------------------------------------------------+++data ErrFriendByPublicKey+  = ErrFriendByPublicKeyOk+    -- The function returned successfully.++  | ErrFriendByPublicKeyNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrFriendByPublicKeyNotFound+    -- No friend with the given Public Key exists on the friend list.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Return the friend number associated with that Public Key.+--+-- @return the friend number on success, UINT32_MAX on failure.+-- @param public_key A byte array containing the Public Key.+foreign import ccall tox_friend_by_public_key :: Tox a -> CString -> CErr ErrFriendByPublicKey -> IO Word32+callFriendByPublicKey :: (Tox a -> CString -> CErr ErrFriendByPublicKey -> IO Word32) ->+                         Tox a -> BS.ByteString -> IO (Either ErrFriendByPublicKey Word32)+callFriendByPublicKey f tox address =+  BS.useAsCString address $ \addr' ->+    callErrFun $ f tox addr'++toxFriendByPublicKey :: Tox a -> BS.ByteString -> IO (Either ErrFriendByPublicKey Word32)+toxFriendByPublicKey = callFriendByPublicKey tox_friend_by_public_key++-- | Checks if a friend with the given friend number exists and returns true if+-- it does.+foreign import ccall tox_friend_exists :: Tox a -> Word32 -> IO Bool+toxFriendExists :: Tox a -> Word32 -> IO Bool+toxFriendExists = tox_friend_exists++-- | Return the number of friends on the friend list.+--+-- This function can be used to determine how much memory to allocate for+-- tox_self_get_friend_list.+foreign import ccall tox_self_get_friend_list_size :: Tox a -> IO CSize++-- | Copy a list of valid friend numbers into an array.+--+-- Call tox_self_get_friend_list_size to determine the number of elements to+-- allocate.+--+-- @param list A memory region with enough space to hold the friend list. If+--   this parameter is 'nullPtr', this function has no effect.+foreign import ccall tox_self_get_friend_list :: Tox a -> Ptr Word32 -> IO ()++toxSelfGetFriendList :: Tox a -> IO [Word32]+toxSelfGetFriendList tox = do+  friendListSize <- tox_self_get_friend_list_size tox+  allocaArray (fromIntegral friendListSize) $ \friendListPtr -> do+    tox_self_get_friend_list tox friendListPtr+    peekArray (fromIntegral friendListSize) friendListPtr++data ErrFriendGetPublicKey+  = ErrFriendGetPublicKeyOk+    -- The function returned successfully.++  | ErrFriendGetPublicKeyFriendNotFound+    -- No friend with the given number exists on the friend list.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Copies the Public Key associated with a given friend number to a byte+-- array.+--+-- @param friend_number The friend number you want the Public Key of.+-- @param public_key A memory region of at least 'tox_public_key_size' bytes. If+--   this parameter is 'nullPtr', this function has no effect.+--+-- @return true on success.+foreign import ccall tox_friend_get_public_key :: Tox a -> Word32 -> CString -> CErr ErrFriendGetPublicKey -> IO Bool+callFriendGetPublicKey :: (Tox a -> Word32 -> CString -> CErr ErrFriendGetPublicKey -> IO Bool) ->+                          Tox a -> Word32 -> IO (Either ErrFriendGetPublicKey BS.ByteString)+callFriendGetPublicKey f tox fn =+  let pkLen = fromIntegral tox_public_key_size in+  alloca $ \errPtr ->+    allocaArray pkLen $ \pkPtr -> do+      _ <- f tox fn pkPtr errPtr+      callGetPublicKey errPtr pkPtr pkLen++callGetPublicKey+  :: (Bounded err, Enum err, Eq err)+  => Ptr (CEnum err)+  -> Ptr CChar+  -> Int+  -> IO (Either err BS.ByteString)+callGetPublicKey errPtr pkPtr pkLen = do+  err <- toEnum . fromIntegral . unCEnum <$> peek errPtr+  str <- BS.packCStringLen (pkPtr, pkLen)+  return $ if err /= minBound+           then Left  err+           else Right str++toxFriendGetPublicKey :: Tox a -> Word32 -> IO (Either ErrFriendGetPublicKey BS.ByteString)+toxFriendGetPublicKey = callFriendGetPublicKey tox_friend_get_public_key+++data ErrFriendGetLastOnline+  = ErrFriendGetLastOnlineOk+    -- The function returned successfully.++  | ErrFriendGetLastOnlineFriendNotFound+    -- No friend with the given number exists on the friend list.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Return a unix-time timestamp of the last time the friend associated with a given+-- friend number was seen online. This function will return UINT64_MAX on error.+--+-- @param friend_number The friend number you want to query.+foreign import ccall tox_friend_get_last_online :: Tox a -> Word32 -> CErr ErrFriendGetLastOnline -> IO Word64+callFriendGetLastOnline :: (Tox a -> Word32 -> CErr ErrFriendGetLastOnline -> IO Word64) ->+                           Tox a -> Word32 -> IO (Either ErrFriendGetLastOnline EpochTime)+callFriendGetLastOnline f tox fn = callErrFun (f tox fn >=> (return . CTime . fromIntegral))++toxFriendGetLastOnline :: Tox a -> Word32 -> IO (Either ErrFriendGetLastOnline EpochTime)+toxFriendGetLastOnline = callFriendGetLastOnline tox_friend_get_last_online+++--------------------------------------------------------------------------------+--+-- :: Friend-specific state queries (can also be received through callbacks)+--+--------------------------------------------------------------------------------+++-- | Common error codes for friend state query functions.+data ErrFriendQuery+  = ErrFriendQueryOk+    -- The function returned successfully.++  | ErrFriendQueryNull+    -- The pointer parameter for storing the query result (name, message) was+    -- NULL. Unlike the `_self_` variants of these functions, which have no effect+    -- when a parameter is NULL, these functions return an error in that case.++  | ErrFriendQueryFriendNotFound+    -- The friend_number did not designate a valid friend.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Return the length of the friend's name. If the friend number is invalid, the+-- return value is unspecified.+--+-- The return value is equal to the `length` argument received by the last+-- `friend_name` callback.+foreign import ccall tox_friend_get_name_size :: Tox a -> Word32 -> CErr ErrFriendQuery -> IO CSize++-- | Write the name of the friend designated by the given friend number to a byte+-- array.+--+-- Call tox_friend_get_name_size to determine the allocation size for the `name`+-- parameter.+--+-- The data written to `name` is equal to the data received by the last+-- `friend_name` callback.+--+-- @param name A valid memory region large enough to store the friend's name.+--+-- @return true on success.+foreign import ccall tox_friend_get_name :: Tox a -> Word32 -> CString -> CErr ErrFriendQuery -> IO Bool++toxFriendGetName :: Tox a -> Word32 -> IO (Either ErrFriendQuery String)+toxFriendGetName tox fn = do+  nameLenRes <- callErrFun $ tox_friend_get_name_size tox fn+  case nameLenRes of+    Left err -> return $ Left err+    Right nameLen -> allocaArray (fromIntegral nameLen) $ \namePtr -> do+      nameRes <- callErrFun $ tox_friend_get_name tox fn namePtr+      case nameRes of+        Left err -> return $ Left err+        Right _ ->+          Right <$> peekCStringLen (namePtr, fromIntegral nameLen)+++-- | @param friend_number The friend number of the friend whose name changed.+-- @param name A byte array containing the same data as+--   tox_friend_get_name would write to its `name` parameter.+-- @param length A value equal to the return value of+--   tox_friend_get_name_size.+type FriendNameCb a = Tox a -> Word32 -> String -> a -> IO a+type CFriendNameCb a = Tox a -> Word32 -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendNameCb :: CFriendNameCb a -> IO (FunPtr (CFriendNameCb a))++callFriendNameCb :: FriendNameCb a -> CFriendNameCb a+callFriendNameCb f tox fn nameStr nameLen udPtr = do+  ud <- deRefStablePtr udPtr+  name <- peekCStringLen (nameStr, fromIntegral nameLen)+  modifyMVar_ ud $ f tox fn name++friendNameCb :: FriendNameCb a -> IO (FunPtr (CFriendNameCb a))+friendNameCb = wrapFriendNameCb . callFriendNameCb+++-- | Set the callback for the `friend_name` event. Pass 'nullPtr' to unset.+--+-- This event is triggered when a friend changes their name.+foreign import ccall tox_callback_friend_name :: Tox a -> FunPtr (CFriendNameCb a) -> IO ()+++-- | @param friend_number The friend number of the friend whose status message+--   changed.+-- @param message A byte array containing the same data as+--   tox_friend_get_status_message would write to its `status_message`+--   parameter.+-- @param length A value equal to the return value of+--   tox_friend_get_status_message_size.+type FriendStatusMessageCb a = Tox a -> Word32 -> String -> a -> IO a+type CFriendStatusMessageCb a = Tox a -> Word32 -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendStatusMessageCb :: CFriendStatusMessageCb a -> IO (FunPtr (CFriendStatusMessageCb a))++callFriendStatusMessageCb :: FriendStatusMessageCb a -> CFriendStatusMessageCb a+callFriendStatusMessageCb f tox fn statusMessageStr statusMessageLen udPtr = do+  ud <- deRefStablePtr udPtr+  statusMessage <- peekCStringLen (statusMessageStr, fromIntegral statusMessageLen)+  modifyMVar_ ud $ f tox fn statusMessage++friendStatusMessageCb :: FriendStatusMessageCb a -> IO (FunPtr (CFriendStatusMessageCb a))+friendStatusMessageCb = wrapFriendStatusMessageCb . callFriendStatusMessageCb+++-- | Return the length of the friend's status message. If the friend number is+-- invalid, the return value is SIZE_MAX.+foreign import ccall tox_friend_get_status_message_size :: Tox a -> Word32 -> CErr ErrFriendQuery -> IO CSize++-- | Write the status message of the friend designated by the given friend number to a byte+-- array.+--+-- Call tox_friend_get_status_message_size to determine the allocation size for the `status_name`+-- parameter.+--+-- The data written to `status_message` is equal to the data received by the last+-- `friend_status_message` callback.+--+-- @param status_message A valid memory region large enough to store the friend's status message.+foreign import ccall tox_friend_get_status_message :: Tox a -> Word32 -> CString -> CErr ErrFriendQuery -> IO Bool++toxFriendGetStatusMessage :: Tox a -> Word32 -> IO (Either ErrFriendQuery String)+toxFriendGetStatusMessage tox fn = do+  statusMessageLenRes <- callErrFun $ tox_friend_get_status_message_size tox fn+  case statusMessageLenRes of+    Left err -> return $ Left err+    Right statusMessageLen -> allocaArray (fromIntegral statusMessageLen) $ \statusMessagePtr -> do+      statusMessageRes <- callErrFun $ tox_friend_get_status_message tox fn statusMessagePtr+      case statusMessageRes of+        Left err -> return $ Left err+        Right _ ->+          Right <$> peekCStringLen (statusMessagePtr, fromIntegral statusMessageLen)+++-- | Set the callback for the `friend_status_message` event. Pass 'nullPtr' to+-- unset.+--+-- This event is triggered when a friend changes their status message.+foreign import ccall tox_callback_friend_status_message :: Tox a -> FunPtr (CFriendStatusMessageCb a) -> IO ()+++-- | @param friend_number The friend number of the friend whose user status+--   changed.+-- @param status The new user status.+type FriendStatusCb a = Tox a -> Word32 -> UserStatus -> a -> IO a+type CFriendStatusCb a = Tox a -> Word32 -> CEnum UserStatus -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendStatusCb :: CFriendStatusCb a -> IO (FunPtr (CFriendStatusCb a))++callFriendStatusCb :: FriendStatusCb a -> CFriendStatusCb a+callFriendStatusCb f tox fn status = deRefStablePtr >=> (`modifyMVar_` f tox fn (fromCEnum status))++friendStatusCb :: FriendStatusCb a -> IO (FunPtr (CFriendStatusCb a))+friendStatusCb = wrapFriendStatusCb . callFriendStatusCb+++-- | Set the callback for the `friend_status` event. Pass 'nullPtr' to unset.+--+-- This event is triggered when a friend changes their user status.+foreign import ccall tox_callback_friend_status :: Tox a -> FunPtr (CFriendStatusCb a) -> IO ()+++-- | Check whether a friend is currently connected to this client.+--+-- The result of this function is equal to the last value received by the+-- `friend_connection_status` callback.+--+-- @param friend_number The friend number for which to query the connection+--   status.+--+-- @return the friend's connection status as it was received through the+--   `friend_connection_status` event.+foreign import ccall tox_friend_get_connection_status :: Tox a -> Word32 -> CErr ErrFriendQuery -> IO (CEnum Connection)++callFriendGetConnectionStatus :: (Tox a -> Word32 -> CErr ErrFriendQuery -> IO (CEnum Connection)) ->+                                 Tox a -> Word32 -> IO (Either ErrFriendQuery Connection)+callFriendGetConnectionStatus f tox fn = callErrFun (f tox fn >=> (return . fromCEnum))++toxFriendGetConnectionStatus :: Tox a -> Word32 -> IO (Either ErrFriendQuery Connection)+toxFriendGetConnectionStatus = callFriendGetConnectionStatus tox_friend_get_connection_status+++-- | @param friend_number The friend number of the friend whose connection+--   status changed.+-- @param connection_status The result of calling+--   tox_friend_get_connection_status on the passed friend_number.+type FriendConnectionStatusCb a = Tox a -> Word32 -> Connection -> a -> IO a+type CFriendConnectionStatusCb a = Tox a -> Word32 -> CEnum Connection -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendConnectionStatusCb :: CFriendConnectionStatusCb a -> IO (FunPtr (CFriendConnectionStatusCb a))++callFriendConnectionStatusCb :: FriendConnectionStatusCb a -> CFriendConnectionStatusCb a+callFriendConnectionStatusCb f tox fn connectionStatus = deRefStablePtr >=> (`modifyMVar_` f tox fn (fromCEnum connectionStatus))++friendConnectionStatusCb :: FriendConnectionStatusCb a -> IO (FunPtr (CFriendConnectionStatusCb a))+friendConnectionStatusCb = wrapFriendConnectionStatusCb . callFriendConnectionStatusCb+++-- | Set the callback for the `friend_connection_status` event. Pass 'nullPtr'+-- to unset.+--+-- This event is triggered when a friend goes offline after having been online,+-- or when a friend goes online.+--+-- This callback is not called when adding friends. It is assumed that when+-- adding friends, their connection status is initially offline.+foreign import ccall tox_callback_friend_connection_status :: Tox a -> FunPtr (CFriendConnectionStatusCb a) -> IO ()+++-- | Check whether a friend is currently typing a message.+--+-- @param friend_number The friend number for which to query the typing status.+--+-- @return true if the friend is typing.+-- @return false if the friend is not typing, or the friend number was+--   invalid. Inspect the error code to determine which case it is.+foreign import ccall tox_friend_get_typing :: Tox a -> Word32 -> CErr ErrFriendQuery -> IO Bool++callFriendGetTyping :: (Tox a -> Word32 -> CErr ErrFriendQuery -> IO Bool) ->+                       Tox a -> Word32 -> IO (Either ErrFriendQuery Bool)+callFriendGetTyping f tox fn = callErrFun $ f tox fn++toxFriendGetTyping :: Tox a -> Word32 -> IO (Either ErrFriendQuery Bool)+toxFriendGetTyping = callFriendGetTyping tox_friend_get_typing+++-- | @param friend_number The friend number of the friend who started or stopped+--   typing.+-- @param is_typing The result of calling tox_friend_get_typing on the passed+--   friend_number.+type FriendTypingCb a = Tox a -> Word32 -> Bool -> a -> IO a+type CFriendTypingCb a = Tox a -> Word32 -> Bool -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendTypingCb :: CFriendTypingCb a -> IO (FunPtr (CFriendTypingCb a))++callFriendTypingCb :: FriendTypingCb a -> CFriendTypingCb a+callFriendTypingCb f tox fn typing = deRefStablePtr >=> (`modifyMVar_` f tox fn typing)++friendTypingCb :: FriendTypingCb a -> IO (FunPtr (CFriendTypingCb a))+friendTypingCb = wrapFriendTypingCb . callFriendTypingCb++-- | Set the callback for the `friend_typing` event. Pass 'nullPtr' to unset.+--+-- This event is triggered when a friend starts or stops typing.+foreign import ccall tox_callback_friend_typing :: Tox a -> FunPtr (CFriendTypingCb a) -> IO ()+++--------------------------------------------------------------------------------+--+-- :: Sending private messages+--+--------------------------------------------------------------------------------+++data ErrSetTyping+  = ErrSetTypingOk+    -- The function returned successfully.++  | ErrSetTypingFriendNotFound+    -- The friend number did not designate a valid friend.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Set the client's typing status for a friend.+--+-- The client is responsible for turning it on or off.+--+-- @param friend_number The friend to which the client is typing a message.+-- @param typing The typing status. True means the client is typing.+--+-- @return true on success.+foreign import ccall tox_self_set_typing :: Tox a -> Word32 -> Bool -> CErr ErrSetTyping -> IO Bool+callSelfSetTyping :: (Tox a -> Word32 -> Bool -> CErr ErrSetTyping -> IO Bool) ->+                     Tox a -> Word32 -> Bool -> IO (Either ErrSetTyping Bool)+callSelfSetTyping f tox fn typing = callErrFun $ f tox fn typing++toxSelfSetTyping :: Tox a -> Word32 -> Bool -> IO (Either ErrSetTyping Bool)+toxSelfSetTyping = callSelfSetTyping tox_self_set_typing++data ErrFriendSendMessage+  = ErrFriendSendMessageOk+    -- The function returned successfully.++  | ErrFriendSendMessageNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrFriendSendMessageFriendNotFound+    -- The friend number did not designate a valid friend.++  | ErrFriendSendMessageFriendNotConnected+    -- This client is currently not connected to the friend.++  | ErrFriendSendMessageSendq+    -- An allocation error occurred while increasing the send queue size.++  | ErrFriendSendMessageTooLong+    -- Message length exceeded 'tox_max_message_length'.++  | ErrFriendSendMessageEmpty+    -- Attempted to send a zero-length message.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Send a text chat message to an online friend.+--+-- This function creates a chat message packet and pushes it into the send+-- queue.+--+-- The message length may not exceed 'tox_max_message_length'. Larger messages+-- must be split by the client and sent as separate messages. Other clients can+-- then reassemble the fragments. Messages may not be empty.+--+-- The return value of this function is the message ID. If a read receipt is+-- received, the triggered `friend_read_receipt` event will be passed this+-- message ID.+--+-- Message IDs are unique per friend. The first message ID is 0. Message IDs are+-- incremented by 1 each time a message is sent. If UINT32_MAX messages were+-- sent, the next message ID is 0.+--+-- @param type Message type (normal, action, ...).+-- @param friend_number The friend number of the friend to send the message to.+-- @param message A non-'nullPtr' pointer to the first element of a byte array+--   containing the message text.+-- @param length Length of the message to be sent.+foreign import ccall tox_friend_send_message :: Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> CErr ErrFriendSendMessage -> IO Word32+callFriendSendMessage :: (Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> CErr ErrFriendSendMessage -> IO Word32) ->+                         Tox a -> Word32 -> MessageType -> String -> IO (Either ErrFriendSendMessage Word32)+callFriendSendMessage f tox fn messageType message =+  withCStringLen message $ \(msgStr, msgLen) ->+    callErrFun $ f tox fn (toCEnum messageType) msgStr (fromIntegral msgLen)++toxFriendSendMessage :: Tox a -> Word32 -> MessageType -> String -> IO (Either ErrFriendSendMessage Word32)+toxFriendSendMessage = callFriendSendMessage tox_friend_send_message+++-- | @param friend_number The friend number of the friend who received the+--   message.+-- @param message_id The message ID as returned from tox_friend_send_message+--   corresponding to the message sent.+type FriendReadReceiptCb a = Tox a -> Word32 -> Word32 -> a -> IO a+type CFriendReadReceiptCb a = Tox a -> Word32 -> Word32 -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendReadReceiptCb :: CFriendReadReceiptCb a -> IO (FunPtr (CFriendReadReceiptCb a))++callFriendReadReceiptCb :: FriendReadReceiptCb a -> CFriendReadReceiptCb a+callFriendReadReceiptCb f tox fn msgId = deRefStablePtr >=> (`modifyMVar_` f tox fn msgId)++friendReadReceiptCb :: FriendReadReceiptCb a -> IO (FunPtr (CFriendReadReceiptCb a))+friendReadReceiptCb = wrapFriendReadReceiptCb . callFriendReadReceiptCb+++-- | Set the callback for the `friend_read_receipt` event. Pass 'nullPtr' to+-- unset.+--+-- This event is triggered when the friend receives the message sent with+-- tox_friend_send_message with the corresponding message ID.+foreign import ccall tox_callback_friend_read_receipt :: Tox a -> FunPtr (CFriendReadReceiptCb a) -> IO ()+++--------------------------------------------------------------------------------+--+-- :: Receiving private messages and friend requests+--+--------------------------------------------------------------------------------+++-- | @param public_key The Public Key of the user who sent the friend request.+-- @param time_delta A delta in seconds between when the message was composed+--   and when it is being transmitted. For messages that are sent immediately,+--   it will be 0. If a message was written and couldn't be sent immediately+--   (due to a connection failure, for example), the time_delta is an+--   approximation of when it was composed.+-- @param message The message they sent along with the request.+-- @param length The size of the message byte array.+type FriendRequestCb a = Tox a -> BS.ByteString -> String -> a -> IO a+type CFriendRequestCb a = Tox a -> CString -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendRequestCb :: CFriendRequestCb a -> IO (FunPtr (CFriendRequestCb a))++callFriendRequestCb :: FriendRequestCb a -> CFriendRequestCb a+callFriendRequestCb f tox addrPtr nameStr nameLen udPtr = do+  let addrLen = fromIntegral tox_address_size+  ud <- deRefStablePtr udPtr+  addr <- BS.packCStringLen (addrPtr, addrLen)+  name <- peekCStringLen (nameStr, fromIntegral nameLen)+  modifyMVar_ ud $ f tox addr name++friendRequestCb :: FriendRequestCb a -> IO (FunPtr (CFriendRequestCb a))+friendRequestCb = wrapFriendRequestCb . callFriendRequestCb+++-- | Set the callback for the `friend_request` event. Pass 'nullPtr' to unset.+--+-- This event is triggered when a friend request is received.+foreign import ccall tox_callback_friend_request :: Tox a -> FunPtr (CFriendRequestCb a) -> IO ()++-- | @param friend_number The friend number of the friend who sent the message.+-- @param time_delta Time between composition and sending.+-- @param message The message data they sent.+-- @param length The size of the message byte array.+--+-- @see friend_request for more information on time_delta.+type FriendMessageCb a = Tox a -> Word32 -> MessageType -> String -> a -> IO a+type CFriendMessageCb a = Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendMessageCb :: CFriendMessageCb a -> IO (FunPtr (CFriendMessageCb a))++callFriendMessageCb :: FriendMessageCb a -> CFriendMessageCb a+callFriendMessageCb f tox fn msgType msgStr msgLen udPtr = do+  ud <- deRefStablePtr udPtr+  msg <- peekCStringLen (msgStr, fromIntegral msgLen)+  modifyMVar_ ud $ f tox fn (fromCEnum msgType) msg++friendMessageCb :: FriendMessageCb a -> IO (FunPtr (CFriendMessageCb a))+friendMessageCb = wrapFriendMessageCb . callFriendMessageCb+++-- | Set the callback for the `friend_message` event. Pass 'nullPtr' to unset.+--+-- This event is triggered when a message from a friend is received.+foreign import ccall tox_callback_friend_message :: Tox a -> FunPtr (CFriendMessageCb a) -> IO ()+++--------------------------------------------------------------------------------+--+-- :: File transmission: common between sending and receiving+--+--------------------------------------------------------------------------------+++-- | Generates a cryptographic hash of the given data.+--+-- This function may be used by clients for any purpose, but is provided+-- primarily for validating cached avatars. This use is highly recommended to+-- avoid unnecessary avatar updates.+--+-- If hash is 'nullPtr' or data is 'nullPtr' while length is not 0 the function+-- returns false, otherwise it returns true.+--+-- This function is a wrapper to internal message-digest functions.+--+-- @param hash A valid memory location the hash data. It must be at least+--   'tox_hash_length' bytes in size.+-- @param data Data to be hashed or 'nullPtr'.+-- @param length Size of the data array or 0.+--+-- @return true if hash was not 'nullPtr'.+foreign import ccall tox_hash :: CString -> CString -> CSize -> IO Bool++toxHash :: BS.ByteString -> IO BS.ByteString+toxHash d =+  let hashLen = fromIntegral tox_hash_length in+  allocaArray hashLen $ \hashPtr ->+    BS.useAsCStringLen d $ \(dataPtr, dataLen) -> do+      _ <- tox_hash hashPtr dataPtr (fromIntegral dataLen)+      BS.packCStringLen (dataPtr, fromIntegral dataLen)++data FileKind+  = FileKindData+    -- Arbitrary file data. Clients can choose to handle it based on the file+    -- name or magic or any other way they choose.++  | FileKindAvatar+    -- Avatar file_id. This consists of tox_hash(image).  Avatar data. This+    -- consists of the image data.+    --+    -- Avatars can be sent at any time the client wishes. Generally, a client+    -- will send the avatar to a friend when that friend comes online, and to+    -- all friends when the avatar changed. A client can save some traffic by+    -- remembering which friend received the updated avatar already and only+    -- send it if the friend has an out of date avatar.+    --+    -- Clients who receive avatar send requests can reject it (by sending+    -- FileControlCancel before any other controls), or accept it (by sending+    -- FileControlResume). The file_id of length 'tox_hash_length' bytes (same+    -- length as 'tox_file_id_length') will contain the hash. A client can+    -- compare this hash with a saved hash and send FileControlCancel to+    -- terminate the avatar transfer if it matches.+    --+    -- When file_size is set to 0 in the transfer request it means that the+    -- client has no avatar.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++data FileControl+  = FileControlResume+    -- Sent by the receiving side to accept a file send request. Also sent after+    -- a FileControlPause command to continue sending or receiving.++  | FileControlPause+    -- Sent by clients to pause the file transfer. The initial state of a file+    -- transfer is always paused on the receiving side and running on the+    -- sending side. If both the sending and receiving side pause the transfer,+    -- then both need to send FileControlResume for the transfer to resume.++  | FileControlCancel+    -- Sent by the receiving side to reject a file send request before any other+    -- commands are sent. Also sent by either side to terminate a file transfer.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++data ErrFileControl+  = ErrFileControlOk+    -- The function returned successfully.++  | ErrFileControlFriendNotFound+    -- The friend_number passed did not designate a valid friend.++  | ErrFileControlFriendNotConnected+    -- This client is currently not connected to the friend.++  | ErrFileControlNotFound+    -- No file transfer with the given file number was found for the given friend.++  | ErrFileControlNotPaused+    -- A Resume control was sent, but the file transfer is running normally.++  | ErrFileControlDenied+    -- A Resume control was sent, but the file transfer was paused by the other+    -- party. Only the party that paused the transfer can resume it.++  | ErrFileControlAlreadyPaused+    -- A Pause control was sent, but the file transfer was already paused.++  | ErrFileControlSendq+    -- Packet queue is full.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Sends a file control command to a friend for a given file transfer.+--+-- @param friend_number The friend number of the friend the file is being+--   transferred to or received from.+-- @param file_number The friend-specific identifier for the file transfer.+-- @param control The control command to send.+--+-- @return true on success.+foreign import ccall tox_file_control :: Tox a -> Word32 -> Word32 -> CEnum FileControl -> CErr ErrFileControl -> IO Bool++callFileControl :: (Tox a -> Word32 -> Word32 -> CEnum FileControl -> CErr ErrFileControl -> IO Bool) ->+                   Tox a -> Word32 -> Word32 -> FileControl -> IO (Either ErrFileControl Bool)+callFileControl f tox fn fileNum control = callErrFun $ f tox fn fileNum (toCEnum control)++toxFileControl :: Tox a -> Word32 -> Word32 -> FileControl -> IO (Either ErrFileControl Bool)+toxFileControl = callFileControl tox_file_control++-- | When receiving FileControlCancel, the client should release the+-- resources associated with the file number and consider the transfer failed.+--+-- @param friend_number The friend number of the friend who is sending the file.+-- @param file_number The friend-specific file number the data received is+--   associated with.+-- @param control The file control command received.+type FileRecvControlCb a = Tox a -> Word32 -> Word32 -> FileControl -> a -> IO a+type CFileRecvControlCb a = Tox a -> Word32 -> Word32 -> CEnum FileControl -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFileRecvControlCb :: CFileRecvControlCb a -> IO (FunPtr (CFileRecvControlCb a))++callFileRecvControlCb :: FileRecvControlCb a -> CFileRecvControlCb a+callFileRecvControlCb f tox fn fileNum ctrlCmd = deRefStablePtr >=> (`modifyMVar_` f tox fn fileNum (fromCEnum ctrlCmd))++fileRecvControlCb :: FileRecvControlCb a -> IO (FunPtr (CFileRecvControlCb a))+fileRecvControlCb = wrapFileRecvControlCb . callFileRecvControlCb+++-- | Set the callback for the `file_recv_control` event. Pass 'nullPtr' to+-- unset.+--+-- This event is triggered when a file control command is received from a+-- friend.+foreign import ccall tox_callback_file_recv_control :: Tox a -> FunPtr (CFileRecvControlCb a) -> IO ()++data ErrFileSeek+  = ErrFileSeekOk+    -- The function returned successfully.++  | ErrFileSeekFriendNotFound+    -- The friend_number passed did not designate a valid friend.++  | ErrFileSeekFriendNotConnected+    -- This client is currently not connected to the friend.++  | ErrFileSeekNotFound+    -- No file transfer with the given file number was found for the given friend.++  | ErrFileSeekDenied+    -- File was not in a state where it could be seeked.++  | ErrFileSeekInvalidPosition+    -- Seek position was invalid++  | ErrFileSeekSendq+    -- Packet queue is full.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Sends a file seek control command to a friend for a given file transfer.+--+-- This function can only be called to resume a file transfer right before+-- FileControlResume is sent.+--+-- @param friend_number The friend number of the friend the file is being+--   received from.+-- @param file_number The friend-specific identifier for the file transfer.+-- @param position The position that the file should be seeked to.+foreign import ccall tox_file_seek :: Tox a -> Word32 -> Word32 -> Word64 -> CErr ErrFileSeek -> IO Bool++callFileSeek :: (Tox a -> Word32 -> Word32 -> Word64 -> CErr ErrFileSeek -> IO Bool) ->+                   Tox a -> Word32 -> Word32 -> Word64 -> IO (Either ErrFileSeek Bool)+callFileSeek f tox fn fileNum pos = callErrFun $ f tox fn fileNum pos++toxFileSeek :: Tox a -> Word32 -> Word32 -> Word64 -> IO (Either ErrFileSeek Bool)+toxFileSeek = callFileSeek tox_file_seek+++data ErrFileGet+  = ErrFileGetOk+    -- The function returned successfully.++  | ErrFileGetNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrFileGetFriendNotFound+    -- The friend_number passed did not designate a valid friend.++  | ErrFileGetNotFound+    -- No file transfer with the given file number was found for the given friend.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Copy the file id associated to the file transfer to a byte array.+--+-- @param friend_number The friend number of the friend the file is being+--   transferred to or received from.+-- @param file_number The friend-specific identifier for the file transfer.+-- @param file_id A memory region of at least 'tox_file_id_length' bytes. If+--   this parameter is 'nullPtr', this function has no effect.+--+-- @return true on success.+foreign import ccall tox_file_get_file_id :: Tox a -> Word32 -> Word32 -> CString -> CErr ErrFileGet -> IO Bool++callFileGetFileId :: (Tox a -> Word32 -> Word32 -> CString -> CErr ErrFileGet -> IO Bool) ->+                     Tox a -> Word32 -> Word32 -> IO (Either ErrFileGet BS.ByteString)+callFileGetFileId f tox fn fileNum =+  let fileIdLen = fromIntegral tox_file_id_length in+  alloca $ \errPtr ->+    allocaArray fileIdLen $ \fileIdPtr -> do+      _ <- f tox fn fileNum fileIdPtr errPtr+      err <- toEnum . fromIntegral . unCEnum <$> peek errPtr+      fileId <- BS.packCStringLen (fileIdPtr, fileIdLen)+      return $ if err /= minBound+               then Left  err+               else Right fileId++toxFileGetFileId :: Tox a -> Word32 -> Word32 -> IO (Either ErrFileGet BS.ByteString)+toxFileGetFileId = callFileGetFileId tox_file_get_file_id+++--------------------------------------------------------------------------------+--+-- :: File transmission: sending+--+--------------------------------------------------------------------------------+++data ErrFileSend+  = ErrFileSendOk+    -- The function returned successfully.++  | ErrFileSendNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrFileSendFriendNotFound+    -- The friend_number passed did not designate a valid friend.++  | ErrFileSendFriendNotConnected+    -- This client is currently not connected to the friend.++  | ErrFileSendNameTooLong+    -- Filename length exceeded 'tox_max_filename_length' bytes.++  | ErrFileSendTooMany+    -- Too many ongoing transfers. The maximum number of concurrent file transfers+    -- is 256 per friend per direction (sending and receiving).+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Send a file transmission request.+--+-- Maximum filename length is 'tox_max_filename_length' bytes. The filename+-- should generally just be a file name, not a path with directory names.+--+-- If a non-UINT64_MAX file size is provided, it can be used by both sides to+-- determine the sending progress. File size can be set to UINT64_MAX for+-- streaming data of unknown size.+--+-- File transmission occurs in chunks, which are requested through the+-- `file_chunk_request` event.+--+-- When a friend goes offline, all file transfers associated with the friend are+-- purged from core.+--+-- If the file contents change during a transfer, the behaviour is unspecified+-- in general. What will actually happen depends on the mode in which the file+-- was modified and how the client determines the file size.+--+-- - If the file size was increased+--   - and sending mode was streaming (file_size = UINT64_MAX), the behaviour+--     will be as expected.+--   - and sending mode was file (file_size != UINT64_MAX), the+--     file_chunk_request callback will receive length = 0 when Core thinks+--     the file transfer has finished. If the client remembers the file size as+--     it was when sending the request, it will terminate the transfer normally.+--     If the client re-reads the size, it will think the friend cancelled the+--     transfer.+-- - If the file size was decreased+--   - and sending mode was streaming, the behaviour is as expected.+--   - and sending mode was file, the callback will return 0 at the new+--     (earlier) end-of-file, signalling to the friend that the transfer was+--     cancelled.+-- - If the file contents were modified+--   - at a position before the current read, the two files (local and remote)+--     will differ after the transfer terminates.+--   - at a position after the current read, the file transfer will succeed as+--     expected.+--   - In either case, both sides will regard the transfer as complete and+--     successful.+--+-- @param friend_number The friend number of the friend the file send request+--   should be sent to.+-- @param kind The meaning of the file to be sent.+-- @param file_size Size in bytes of the file the client wants to send,+--   UINT64_MAX if unknown or streaming.+-- @param file_id A file identifier of length 'tox_file_id_length' that can be+--   used to uniquely identify file transfers across core restarts. If+--   'nullPtr', a random one will be generated by core. It can then be obtained+--   by using tox_file_get_file_id().+-- @param filename Name of the file. Does not need to be the actual name. This+--   name will be sent along with the file send request.+-- @param filename_length Size in bytes of the filename.+--+-- @return A file number used as an identifier in subsequent callbacks. This+--   number is per friend. File numbers are reused after a transfer terminates.+--   On failure, this function returns UINT32_MAX. Any pattern in file numbers+--   should not be relied on.+foreign import ccall tox_file_send :: Tox a -> Word32 -> CEnum FileKind -> Word64 -> CString -> CString -> CSize -> CErr ErrFileSend -> IO Word32+callFileSend :: (Tox a -> Word32 -> CEnum FileKind -> Word64 -> CString -> CString -> CSize -> CErr ErrFileSend -> IO Word32) ->+                Tox a -> Word32 -> FileKind -> Word64 -> String -> IO (Either ErrFileSend Word32)+callFileSend f tox fn fileKind fileSize fileName =+  withCStringLen fileName $ \(fileNamePtr, fileNameLen) ->+    callErrFun $ f tox fn (toCEnum fileKind) fileSize nullPtr fileNamePtr (fromIntegral fileNameLen)++toxFileSend :: Tox a -> Word32 -> FileKind -> Word64 -> String -> IO (Either ErrFileSend Word32)+toxFileSend = callFileSend tox_file_send++data ErrFileSendChunk+  = ErrFileSendChunkOk+    -- The function returned successfully.++  | ErrFileSendChunkNull+    -- The length parameter was non-zero, but data was 'nullPtr'.++  | ErrFileSendChunkFriendNotFound+    -- The friend_number passed did not designate a valid friend.++  | ErrFileSendChunkFriendNotConnected+    -- This client is currently not connected to the friend.++  | ErrFileSendChunkNotFound+    -- No file transfer with the given file number was found for the given friend.++  | ErrFileSendChunkNotTransferring+    -- File transfer was found but isn't in a transferring state: (paused, done,+    -- broken, etc...) (happens only when not called from the request chunk+    -- callback).++  | ErrFileSendChunkInvalidLength+    -- Attempted to send more or less data than requested. The requested data+    -- size is adjusted according to maximum transmission unit and the expected+    -- end of the file. Trying to send less or more than requested will return+    -- this error.++  | ErrFileSendChunkSendq+    -- Packet queue is full.++  | ErrFileSendChunkWrongPosition+    -- Position parameter was wrong.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Send a chunk of file data to a friend.+--+-- This function is called in response to the `file_chunk_request` callback.+-- The length parameter should be equal to the one received though the+-- callback.  If it is zero, the transfer is assumed complete. For files with+-- known size, Core will know that the transfer is complete after the last byte+-- has been received, so it is not necessary (though not harmful) to send a+-- zero-length chunk to terminate. For streams, core will know that the+-- transfer is finished if a chunk with length less than the length requested+-- in the callback is sent.+--+-- @param friend_number The friend number of the receiving friend for this file.+-- @param file_number The file transfer identifier returned by tox_file_send.+-- @param position The file or stream position from which to continue reading.+-- @return true on success.+foreign import ccall tox_file_send_chunk :: Tox a -> Word32 -> Word32 -> Word64 -> CString -> CSize -> CErr ErrFileSendChunk -> IO Bool+callFileSendChunk :: (Tox a -> Word32 -> Word32 -> Word64 -> CString -> CSize -> CErr ErrFileSendChunk -> IO Bool) ->+                Tox a -> Word32 -> Word32 -> Word64 -> BS.ByteString ->  IO (Either ErrFileSendChunk Bool)+callFileSendChunk f tox fn fileNum pos d =+  BS.useAsCStringLen d $ \(dataPtr, dataLen) ->+    callErrFun $ f tox fn fileNum pos dataPtr (fromIntegral dataLen)++toxFileSendChunk :: Tox a -> Word32 -> Word32 -> Word64 -> BS.ByteString -> IO (Either ErrFileSendChunk Bool)+toxFileSendChunk = callFileSendChunk tox_file_send_chunk++-- | If the length parameter is 0, the file transfer is finished, and the+-- client's resources associated with the file number should be released. After+-- a call with zero length, the file number can be reused for future file+-- transfers.+--+-- If the requested position is not equal to the client's idea of the current+-- file or stream position, it will need to seek. In case of read-once streams,+-- the client should keep the last read chunk so that a seek back can be+-- supported. A seek-back only ever needs to read from the last requested+-- chunk.  This happens when a chunk was requested, but the send failed. A+-- seek-back request can occur an arbitrary number of times for any given+-- chunk.+--+-- In response to receiving this callback, the client should call the function+-- `tox_file_send_chunk` with the requested chunk. If the number of bytes sent+-- through that function is zero, the file transfer is assumed complete. A+-- client must send the full length of data requested with this callback.+--+-- @param friend_number The friend number of the receiving friend for this file.+-- @param file_number The file transfer identifier returned by tox_file_send.+-- @param position The file or stream position from which to continue reading.+-- @param length The number of bytes requested for the current chunk.+type FileChunkRequestCb a = Tox a -> Word32 -> Word32 -> Word64 -> CSize -> a -> IO a+type CFileChunkRequestCb a = Tox a -> Word32 -> Word32 -> Word64 -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFileChunkRequestCb :: CFileChunkRequestCb a -> IO (FunPtr (CFileChunkRequestCb a))++callFileChunkRequestCb :: FileChunkRequestCb a -> CFileChunkRequestCb a+callFileChunkRequestCb f tox fn fileNum pos len = deRefStablePtr >=> (`modifyMVar_` f tox fn fileNum pos len)++fileChunkRequestCb :: FileChunkRequestCb a -> IO (FunPtr (CFileChunkRequestCb a))+fileChunkRequestCb = wrapFileChunkRequestCb . callFileChunkRequestCb++-- | Set the callback for the `file_chunk_request` event. Pass 'nullPtr' to+-- unset.+--+-- This event is triggered when Core is ready to send more file data.+foreign import ccall tox_callback_file_chunk_request :: Tox a -> FunPtr (CFileChunkRequestCb a) -> IO ()+++--------------------------------------------------------------------------------+--+-- :: File transmission: receiving+--+--------------------------------------------------------------------------------+++-- | The client should acquire resources to be associated with the file+-- transfer.  Incoming file transfers start in the Paused state. After this+-- callback returns, a transfer can be rejected by sending a FileControlCancel+-- control command before any other control commands. It can be accepted by+-- sending FileControlResume.+--+-- @param friend_number The friend number of the friend who is sending the file+--   transfer request.+-- @param file_number The friend-specific file number the data received is+--   associated with.+-- @param kind The meaning of the file to be sent.+-- @param file_size Size in bytes of the file the client wants to send,+--   UINT64_MAX if unknown or streaming.+-- @param filename Name of the file. Does not need to be the actual name. This+--   name will be sent along with the file send request.+-- @param filename_length Size in bytes of the filename.+type FileRecvCb a = Tox a -> Word32 -> Word32 -> FileKind -> Word64 -> String -> a -> IO a+type CFileRecvCb a = Tox a -> Word32 -> Word32 -> CEnum FileKind -> Word64 -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFileRecvCb :: CFileRecvCb a -> IO (FunPtr (CFileRecvCb a))++callFileRecvCb :: FileRecvCb a -> CFileRecvCb a+callFileRecvCb f tox fn fileNum fileKind fileSize fileNameStr fileNameLen udPtr = do+  ud <- deRefStablePtr udPtr+  fileName <- peekCStringLen (fileNameStr, fromIntegral fileNameLen)+  modifyMVar_ ud $ f tox fn fileNum (fromCEnum fileKind) fileSize fileName++fileRecvCb :: FileRecvCb a -> IO (FunPtr (CFileRecvCb a))+fileRecvCb = wrapFileRecvCb . callFileRecvCb+++-- | Set the callback for the `file_recv` event. Pass 'nullPtr' to unset.+--+-- This event is triggered when a file transfer request is received.+foreign import ccall tox_callback_file_recv :: Tox a -> FunPtr (CFileRecvCb a) -> IO ()++-- | When length is 0, the transfer is finished and the client should release+-- the resources it acquired for the transfer. After a call with length = 0,+-- the file number can be reused for new file transfers.+--+-- If position is equal to file_size (received in the file_receive callback)+-- when the transfer finishes, the file was received completely. Otherwise, if+-- file_size was UINT64_MAX, streaming ended successfully when length is 0.+--+-- @param friend_number The friend number of the friend who is sending the file.+-- @param file_number The friend-specific file number the data received is+--   associated with.+-- @param position The file position of the first byte in data.+-- @param data A byte array containing the received chunk.+-- @param length The length of the received chunk.+type FileRecvChunkCb a = Tox a -> Word32 -> Word32 -> Word64 -> BS.ByteString -> a -> IO a+type CFileRecvChunkCb a = Tox a -> Word32 -> Word32 -> Word64 -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFileRecvChunkCb :: CFileRecvChunkCb a -> IO (FunPtr (CFileRecvChunkCb a))++callFileRecvChunkCb :: FileRecvChunkCb a -> CFileRecvChunkCb a+callFileRecvChunkCb f tox fn fileNum pos dataPtr dataLen udPtr = do+  ud <- deRefStablePtr udPtr+  d <- BS.packCStringLen (dataPtr, fromIntegral dataLen)+  modifyMVar_ ud $ f tox fn fileNum pos d++fileRecvChunkCb :: FileRecvChunkCb a -> IO (FunPtr (CFileRecvChunkCb a))+fileRecvChunkCb = wrapFileRecvChunkCb . callFileRecvChunkCb+++-- | Set the callback for the `file_recv_chunk` event. Pass 'nullPtr' to unset.+--+-- This event is first triggered when a file transfer request is received, and+-- subsequently when a chunk of file data for an accepted request was received.+foreign import ccall tox_callback_file_recv_chunk :: Tox a -> FunPtr (CFileRecvChunkCb a) -> IO ()+++--------------------------------------------------------------------------------+--+-- :: Conference management+--+--------------------------------------------------------------------------------+++-- | Conference types for the conference_invite event.+data ConferenceType+  = ConferenceTypeText+    -- Text-only conferences that must be accepted with the tox_conference_join function.++  | ConferenceTypeAv+    -- Video conference. The function to accept these is in toxav.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | The invitation will remain valid until the inviting friend goes offline+-- or exits the conference.+--+-- @param friend_number The friend who invited us.+-- @param type The conference type (text only or audio/video).+-- @param cookie A piece of data of variable length required to join the+--   conference.+-- @param length The length of the cookie.+type ConferenceInviteCb a = Tox a -> Word32 -> ConferenceType -> BS.ByteString -> a -> IO a+type CConferenceInviteCb a = Tox a -> Word32 -> CEnum ConferenceType -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapConferenceInviteCb :: CConferenceInviteCb a -> IO (FunPtr (CConferenceInviteCb a))++callConferenceInviteCb :: ConferenceInviteCb a -> CConferenceInviteCb a+callConferenceInviteCb f tox fn confType cookiePtr cookieLen udPtr = do+  ud <- deRefStablePtr udPtr+  cookie <- BS.packCStringLen (cookiePtr, fromIntegral cookieLen)+  modifyMVar_ ud $ f tox fn (fromCEnum confType) cookie++conferenceInviteCb :: ConferenceInviteCb a -> IO (FunPtr (CConferenceInviteCb a))+conferenceInviteCb = wrapConferenceInviteCb . callConferenceInviteCb+++-- | Set the callback for the `conference_invite` event. Pass NULL to unset.+--+-- This event is triggered when the client is invited to join a conference.+foreign import ccall tox_callback_conference_invite :: Tox a -> FunPtr (CConferenceInviteCb a) -> IO ()+++-- | @param conference_number The conference number of the conference the message is intended for.+-- @param peer_number The ID of the peer who sent the message.+-- @param type The type of message (normal, action, ...).+-- @param message The message data.+-- @param length The length of the message.+type ConferenceMessageCb a = Tox a -> Word32 -> Word32 -> MessageType -> String -> a -> IO a+type CConferenceMessageCb a = Tox a -> Word32 -> Word32 -> CEnum MessageType -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapConferenceMessageCb :: CConferenceMessageCb a -> IO (FunPtr (CConferenceMessageCb a))++callConferenceMessageCb :: ConferenceMessageCb a -> CConferenceMessageCb a+callConferenceMessageCb f tox gn pn msgType msgStr msgLen udPtr = do+  ud <- deRefStablePtr udPtr+  msg <- peekCStringLen (msgStr, fromIntegral msgLen)+  modifyMVar_ ud $ f tox gn pn (fromCEnum msgType) msg++conferenceMessageCb :: ConferenceMessageCb a -> IO (FunPtr (CConferenceMessageCb a))+conferenceMessageCb = wrapConferenceMessageCb . callConferenceMessageCb+++-- | Set the callback for the `conference_message` event. Pass NULL to unset.+--+-- This event is triggered when the client receives a conference message.+foreign import ccall tox_callback_conference_message :: Tox a -> FunPtr (CConferenceMessageCb a) -> IO ()+++-- | @param conference_number The conference number of the conference the title change is intended for.+-- @param peer_number The ID of the peer who changed the title.+-- @param title The title data.+-- @param length The title length.+type ConferenceTitleCb a = Tox a -> Word32 -> Word32 -> String -> a -> IO a+type CConferenceTitleCb a = Tox a -> Word32 -> Word32 -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapConferenceTitleCb :: CConferenceTitleCb a -> IO (FunPtr (CConferenceTitleCb a))++callConferenceTitleCb :: ConferenceTitleCb a -> CConferenceTitleCb a+callConferenceTitleCb f tox gn pn titleStr titleLen udPtr = do+  ud <- deRefStablePtr udPtr+  title <- peekCStringLen (titleStr, fromIntegral titleLen)+  modifyMVar_ ud $ f tox gn pn title++conferenceTitleCb :: ConferenceTitleCb a -> IO (FunPtr (CConferenceTitleCb a))+conferenceTitleCb = wrapConferenceTitleCb . callConferenceTitleCb+++-- | Set the callback for the `conference_title` event. Pass NULL to unset.+--+-- This event is triggered when a peer changes the conference title.+--+-- If peer_number == UINT32_MAX, then author is unknown (e.g. initial joining the conference).+foreign import ccall tox_callback_conference_title :: Tox a -> FunPtr (CConferenceTitleCb a) -> IO ()+++-- | @param conference_number The conference number of the conference the peer is in.+-- @param peer_number The ID of the peer who changed their name.+-- @param name The new name of the peer.+type ConferencePeerNameCb a = Tox a -> Word32 -> Word32 -> String -> a -> IO a+type CConferencePeerNameCb a = Tox a -> Word32 -> Word32 -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapConferencePeerNameCb :: CConferencePeerNameCb a -> IO (FunPtr (CConferencePeerNameCb a))++callConferencePeerNameCb :: ConferencePeerNameCb a -> CConferencePeerNameCb a+callConferencePeerNameCb f tox gn pn nameStr nameLen udPtr = do+  ud <- deRefStablePtr udPtr+  name <- peekCStringLen (nameStr, fromIntegral nameLen)+  modifyMVar_ ud $ f tox gn pn name++conferencePeerNameCb :: ConferencePeerNameCb a -> IO (FunPtr (CConferencePeerNameCb a))+conferencePeerNameCb = wrapConferencePeerNameCb . callConferencePeerNameCb+++-- | Set the callback for the `conference_peer_name` event. Pass NULL to unset.+--+-- This event is triggered when the peer changes their nickname.+foreign import ccall tox_callback_conference_peer_name :: Tox a -> FunPtr (CConferencePeerNameCb a) -> IO ()+++-- | @param conference_number The conference number of the conference for which the peer list changed.+type ConferencePeerListChangedCb a = Tox a -> Word32 -> a -> IO a+type CConferencePeerListChangedCb a = Tox a -> Word32 -> UserData a -> IO ()+foreign import ccall "wrapper" wrapConferencePeerListChangedCb :: CConferencePeerListChangedCb a -> IO (FunPtr (CConferencePeerListChangedCb a))++callConferencePeerListChangedCb :: ConferencePeerListChangedCb a -> CConferencePeerListChangedCb a+callConferencePeerListChangedCb f tox gn = deRefStablePtr >=> (`modifyMVar_` f tox gn)++conferencePeerListChangedCb :: ConferencePeerListChangedCb a -> IO (FunPtr (CConferencePeerListChangedCb a))+conferencePeerListChangedCb = wrapConferencePeerListChangedCb . callConferencePeerListChangedCb+++-- | Set the callback for the `conference_peer_list_changed` event. Pass NULL to unset.+--+-- This event is triggered when the peer list changes (peer join, peer exit).+foreign import ccall tox_callback_conference_peer_list_changed :: Tox a -> FunPtr (CConferencePeerListChangedCb a) -> IO ()+++data ErrConferenceNew+  = ErrConferenceNewOk+    -- The function returned successfully.++  | ErrConferenceNewInit+    -- The conference instance failed to initialize.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Creates a new conference.+--+-- This function creates a new text conference.+--+-- @return conference number on success, or UINT32_MAX on failure.+foreign import ccall tox_conference_new :: Tox a -> CErr ErrConferenceNew -> IO Word32+callConferenceNew :: (Tox a -> CErr ErrConferenceNew -> IO Word32) ->+                     Tox a -> IO (Either ErrConferenceNew Word32)+callConferenceNew f tox = callErrFun $ f tox++toxConferenceNew :: Tox a -> IO (Either ErrConferenceNew Word32)+toxConferenceNew = callConferenceNew tox_conference_new+++data ErrConferenceDelete+  = ErrConferenceDeleteOk+    -- The function returned successfully.++  | ErrConferenceDeleteConferenceNotFound+    -- The conference number passed did not designate a valid conference.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | This function deletes a conference.+--+-- @param conference_number The conference number of the conference to be deleted.+--+-- @return true on success.+foreign import ccall tox_conference_delete :: Tox a -> Word32 -> CErr ErrConferenceDelete -> IO Bool+callConferenceDelete :: (Tox a -> Word32 -> CErr ErrConferenceDelete -> IO Bool) ->+                        Tox a -> Word32 -> IO (Either ErrConferenceDelete Bool)+callConferenceDelete f tox gn = callErrFun $ f tox gn++toxConferenceDelete :: Tox a -> Word32 -> IO (Either ErrConferenceDelete Bool)+toxConferenceDelete = callConferenceDelete tox_conference_delete+++-- | Error codes for peer info queries.+data ErrConferencePeerQuery+  = ErrConferencePeerQueryOk+    -- The function returned successfully.++  | ErrConferencePeerQueryConferenceNotFound+    -- The conference number passed did not designate a valid conference.++  | ErrConferencePeerQueryPeerNotFound+    -- The peer number passed did not designate a valid peer.++  | ErrConferencePeerQueryNoConnection+    -- The client is not connected to the conference.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)++++-- | Return the number of peers in the conference. Return value is unspecified on failure.+foreign import ccall tox_conference_peer_count :: Tox a -> Word32 -> CErr ErrConferencePeerQuery -> IO Word32+callConferencePeerCount :: (Tox a -> Word32 -> CErr ErrConferencePeerQuery -> IO Word32) ->+                           Tox a -> Word32 -> IO (Either ErrConferencePeerQuery Word32)+callConferencePeerCount f tox gn = callErrFun $ f tox gn++toxConferencePeerCount :: Tox a -> Word32 -> IO (Either ErrConferencePeerQuery Word32)+toxConferencePeerCount = callConferencePeerCount tox_conference_peer_count+++-- | Return the length of the peer's name. Return value is unspecified on failure.+foreign import ccall tox_conference_peer_get_name_size :: Tox a -> Word32 -> Word32 -> CErr ErrConferencePeerQuery -> IO CSize+++-- | Copy the name of peer_number who is in conference_number to name.+-- name must be at least TOX_MAX_NAME_LENGTH long.+--+-- @return true on success.+foreign import ccall tox_conference_peer_get_name :: Tox a -> Word32 -> Word32 -> CString -> CErr ErrConferencePeerQuery -> IO Bool++toxConferencePeerGetName :: Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery String)+toxConferencePeerGetName tox gn pn = do+  nameLenRes <- callErrFun $ tox_conference_peer_get_name_size tox gn pn+  case nameLenRes of+    Left err -> return $ Left err+    Right nameLen -> allocaArray (fromIntegral nameLen) $ \namePtr -> do+      nameRes <- callErrFun $ tox_conference_peer_get_name tox gn pn namePtr+      case nameRes of+        Left err -> return $ Left err+        Right _ ->+          Right <$> peekCStringLen (namePtr, fromIntegral nameLen)+++-- | Copy the public key of peer_number who is in conference_number to public_key.+-- public_key must be TOX_PUBLIC_KEY_SIZE long.+--+-- @return true on success.+foreign import ccall tox_conference_peer_get_public_key :: Tox a -> Word32 -> Word32 -> CString -> CErr ErrConferencePeerQuery -> IO Bool+callConferencePeerGetPublicKey :: (Tox a -> Word32 -> Word32 -> CString -> CErr ErrConferencePeerQuery -> IO Bool) ->+                          Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery BS.ByteString)+callConferencePeerGetPublicKey f tox gn pn =+  let pkLen = fromIntegral tox_public_key_size in+  alloca $ \errPtr ->+    allocaArray pkLen $ \pkPtr -> do+      _ <- f tox gn pn pkPtr errPtr+      callGetPublicKey errPtr pkPtr pkLen++toxConferencePeerGetPublicKey :: Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery BS.ByteString)+toxConferencePeerGetPublicKey = callConferencePeerGetPublicKey tox_conference_peer_get_public_key+++-- | Return true if passed peer_number corresponds to our own.+foreign import ccall tox_conference_peer_number_is_ours :: Tox a -> Word32 -> Word32 -> CErr ErrConferencePeerQuery -> IO Bool+callConferencePeerNumberIsOurs :: (Tox a -> Word32 -> Word32 -> CErr ErrConferencePeerQuery -> IO Bool) ->+                                  Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery Bool)+callConferencePeerNumberIsOurs f tox gn pn = callErrFun $ f tox gn pn++toxConferencePeerNumberIsOurs :: Tox a -> Word32 -> Word32 -> IO (Either ErrConferencePeerQuery Bool)+toxConferencePeerNumberIsOurs = callConferencePeerNumberIsOurs tox_conference_peer_number_is_ours+++data ErrConferenceInvite+  = ErrConferenceInviteOk+    -- The function returned successfully.++  | ErrConferenceInviteConferenceNotFound+    -- The conference number passed did not designate a valid conference.++  | ErrConferenceInviteFailSend+    -- The invite packet failed to send.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)++++-- | Invites a friend to a conference.+--+-- @param friend_number The friend number of the friend we want to invite.+-- @param conference_number The conference number of the conference we want to invite the friend to.+--+-- @return true on success.+foreign import ccall tox_conference_invite :: Tox a -> Word32 -> Word32 -> CErr ErrConferenceInvite -> IO Bool+callConferenceInvite :: (Tox a -> Word32 -> Word32 -> CErr ErrConferenceInvite -> IO Bool) ->+                        Tox a -> Word32 -> Word32 -> IO (Either ErrConferenceInvite Bool)+callConferenceInvite f tox fn gn = callErrFun $ f tox fn gn++toxConferenceInvite :: Tox a -> Word32 -> Word32 -> IO (Either ErrConferenceInvite Bool)+toxConferenceInvite = callConferenceInvite tox_conference_invite+++data ErrConferenceJoin+  = ErrConferenceJoinOk+    -- The function returned successfully.++  | ErrConferenceJoinInvalidLength+    -- The cookie passed has an invalid length.++  | ErrConferenceJoinWrongType+    -- The conference is not the expected type. This indicates an invalid cookie.++  | ErrConferenceJoinFriendNotFound+    -- The friend number passed does not designate a valid friend.++  | ErrConferenceJoinDuplicate+    -- Client is already in this conference.++  | ErrConferenceJoinInitFail+    -- Conference instance failed to initialize.++  | ErrConferenceJoinFailSend+    -- The join packet failed to send.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)++++-- | Joins a conference that the client has been invited to.+--+-- @param friend_number The friend number of the friend who sent the invite.+-- @param cookie Received via the `conference_invite` event.+-- @param length The size of cookie.+--+-- @return conference number on success, UINT32_MAX on failure.+foreign import ccall tox_conference_join :: Tox a -> Word32 -> CString -> CSize -> CErr ErrConferenceJoin -> IO Word32+callConferenceJoin :: (Tox a -> Word32 -> CString -> CSize -> CErr ErrConferenceJoin -> IO Word32) ->+                        Tox a -> Word32 -> BS.ByteString -> IO (Either ErrConferenceJoin Word32)+callConferenceJoin f tox fn cookie =+  BS.useAsCStringLen cookie $ \(cookiePtr, cookieLen) ->+    callErrFun $ f tox fn cookiePtr (fromIntegral cookieLen)++toxConferenceJoin :: Tox a -> Word32 -> BS.ByteString -> IO (Either ErrConferenceJoin Word32)+toxConferenceJoin = callConferenceJoin tox_conference_join+++data ErrConferenceSendMessage+  = ErrConferenceSendMessageOk+    -- The function returned successfully.++  | ErrConferenceSendMessageConferenceNotFound+    -- The conference number passed did not designate a valid conference.++  | ErrConferenceSendMessageTooLong+    -- The message is too long.++  | ErrConferenceSendMessageNoConnection+    -- The client is not connected to the conference.++  | ErrConferenceSendMessageFailSend+    -- The message packet failed to send.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)++++-- | Send a text chat message to the conference.+--+-- This function creates a conference message packet and pushes it into the send+-- queue.+--+-- The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages+-- must be split by the client and sent as separate messages. Other clients can+-- then reassemble the fragments.+--+-- @param conference_number The conference number of the conference the message is intended for.+-- @param type Message type (normal, action, ...).+-- @param message A non-NULL pointer to the first element of a byte array+--   containing the message text.+-- @param length Length of the message to be sent.+--+-- @return true on success.+foreign import ccall tox_conference_send_message :: Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> CErr ErrConferenceSendMessage -> IO Bool+callConferenceSendMessage :: (Tox a -> Word32 -> CEnum MessageType -> CString -> CSize -> CErr ErrConferenceSendMessage -> IO Bool) ->+                        Tox a -> Word32 -> MessageType -> String -> IO (Either ErrConferenceSendMessage Bool)+callConferenceSendMessage f tox gn messageType message =+  withCStringLen message $ \(msgPtr, msgLen) ->+    callErrFun $ f tox gn (toCEnum messageType) msgPtr (fromIntegral msgLen)++toxConferenceSendMessage :: Tox a -> Word32 -> MessageType -> String -> IO (Either ErrConferenceSendMessage Bool)+toxConferenceSendMessage = callConferenceSendMessage tox_conference_send_message+++data ErrConferenceTitle+  = ErrConferenceTitleOk+    -- The function returned successfully.++  | ErrConferenceTitleConferenceNotFound+    -- The conference number passed did not designate a valid conference.++  | ErrConferenceTitleInvalidLength+    -- The title is too long or empty.++  | ErrConferenceTitleFailSend+    -- The title packet failed to send.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)++++-- | Return the length of the conference title. Return value is unspecified on failure.+--+-- The return value is equal to the `length` argument received by the last+-- `conference_title` callback.+foreign import ccall tox_conference_get_title_size :: Tox a -> Word32 -> CErr ErrConferenceTitle -> IO CSize+++-- | Write the title designated by the given conference number to a byte array.+--+-- Call tox_conference_get_title_size to determine the allocation size for the `title` parameter.+--+-- The data written to `title` is equal to the data received by the last+-- `conference_title` callback.+--+-- @param title A valid memory region large enough to store the title.+--   If this parameter is NULL, this function has no effect.+--+-- @return true on success.+foreign import ccall tox_conference_get_title :: Tox a -> Word32 -> CString -> CErr ErrConferenceTitle -> IO Bool++toxConferenceGetTitle :: Tox a -> Word32 -> IO (Either ErrConferenceTitle String)+toxConferenceGetTitle tox gn = do+  titleLenRes <- callErrFun $ tox_conference_get_title_size tox gn+  case titleLenRes of+    Left err -> return $ Left err+    Right titleLen -> allocaArray (fromIntegral titleLen) $ \titlePtr -> do+      titleRes <- callErrFun $ tox_conference_get_title tox gn titlePtr+      case titleRes of+        Left err -> return $ Left err+        Right _ ->+          Right <$> peekCStringLen (titlePtr, fromIntegral titleLen)++-- | Set the conference title and broadcast it to the rest of the conference.+--+-- Title length cannot be longer than TOX_MAX_NAME_LENGTH.+--+-- @return true on success.+foreign import ccall tox_conference_set_title :: Tox a -> Word32 -> CString -> CSize -> CErr ErrConferenceTitle -> IO Bool+callConferenceSetTitle :: (Tox a -> Word32 -> CString -> CSize -> CErr ErrConferenceTitle -> IO Bool) ->+                        Tox a -> Word32 -> String -> IO (Either ErrConferenceTitle Bool)+callConferenceSetTitle f tox gn title =+  withCStringLen title $ \(titlePtr, titleLen) ->+    callErrFun $ f tox gn titlePtr (fromIntegral titleLen)++toxConferenceSetTitle :: Tox a -> Word32 -> String -> IO (Either ErrConferenceTitle Bool)+toxConferenceSetTitle = callConferenceSetTitle tox_conference_set_title+++-- | Return the number of conferences in the Tox instance.+-- This should be used to determine how much memory to allocate for `tox_conference_get_chatlist`.+foreign import ccall tox_conference_get_chatlist_size :: Tox a -> IO CSize+++-- | Copy a list of valid conference IDs into the array chatlist. Determine how much space+-- to allocate for the array with the `tox_conference_get_chatlist_size` function.+foreign import ccall tox_conference_get_chatlist :: Tox a -> Ptr Word32 -> IO ()++toxConferenceGetChatlist :: Tox a -> IO [Word32]+toxConferenceGetChatlist tox = do+  chatListSize <- tox_conference_get_chatlist_size tox+  allocaArray (fromIntegral chatListSize) $ \chatListPtr -> do+    tox_conference_get_chatlist tox chatListPtr+    peekArray (fromIntegral chatListSize) chatListPtr++-- | Returns the type of conference (TOX_CONFERENCE_TYPE) that conference_number is. Return value is+-- unspecified on failure.+data ErrConferenceGetType+  = ErrConferenceGetTypeOk+    -- The function returned successfully.++  | ErrConferenceGetTypeConferenceNotFound+    -- The conference number passed did not designate a valid conference.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)++foreign import ccall tox_conference_get_type :: Tox a -> Word32 -> CErr ErrConferenceGetType -> IO (CEnum ConferenceType)+callConferenceGetType :: (Tox a -> Word32 -> CErr ErrConferenceGetType -> IO (CEnum ConferenceType)) ->+                         Tox a -> Word32 -> IO (Either ErrConferenceGetType ConferenceType)+callConferenceGetType f tox gn = callErrFun (f tox gn >=> (return . fromCEnum))++toxConferenceGetType :: Tox a -> Word32 -> IO (Either ErrConferenceGetType ConferenceType)+toxConferenceGetType = callConferenceGetType tox_conference_get_type+++--------------------------------------------------------------------------------+--+-- :: Low-level custom packet sending and receiving+--+--------------------------------------------------------------------------------+++data ErrFriendCustomPacket+  = ErrFriendCustomPacketOk+    -- The function returned successfully.++  | ErrFriendCustomPacketNull+    -- One of the arguments to the function was 'nullPtr' when it was not+    -- expected.++  | ErrFriendCustomPacketFriendNotFound+    -- The friend number did not designate a valid friend.++  | ErrFriendCustomPacketFriendNotConnected+    -- This client is currently not connected to the friend.++  | ErrFriendCustomPacketInvalid+    -- The first byte of data was not in the specified range for the packet+    -- type. This range is 200-254 for lossy, and 160-191 for lossless packets.++  | ErrFriendCustomPacketEmpty+    -- Attempted to send an empty packet.++  | ErrFriendCustomPacketTooLong+    -- Packet data length exceeded 'tox_max_custom_packet_size'.++  | ErrFriendCustomPacketSendq+    -- Packet queue is full.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Send a custom lossy packet to a friend.+--+-- The first byte of data must be in the range 200-254. Maximum length of a+-- custom packet is 'tox_max_custom_packet_size'.+--+-- Lossy packets behave like UDP packets, meaning they might never reach the+-- other side or might arrive more than once (if someone is messing with the+-- connection) or might arrive in the wrong order.+--+-- Unless latency is an issue, it is recommended that you use lossless custom+-- packets instead.+--+-- @param friend_number The friend number of the friend this lossy packet+--   should be sent to.+-- @param data A byte array containing the packet data.+-- @param length The length of the packet data byte array.+--+-- @return true on success.+foreign import ccall tox_friend_send_lossy_packet :: Tox a -> Word32 -> CString -> CSize -> CErr ErrFriendCustomPacket -> IO Bool+callFriendLossyPacket :: (Tox a -> Word32 -> CString -> CSize -> CErr ErrFriendCustomPacket -> IO Bool) ->+                         Tox a -> Word32 -> BS.ByteString -> IO (Either ErrFriendCustomPacket Bool)+callFriendLossyPacket f tox fn d =+  BS.useAsCStringLen d $ \(dataPtr, dataLen) ->+    callErrFun $ f tox fn dataPtr (fromIntegral dataLen)++toxFriendLossyPacket :: Tox a -> Word32 -> BS.ByteString -> IO (Either ErrFriendCustomPacket Bool)+toxFriendLossyPacket = callFriendLossyPacket tox_friend_send_lossy_packet++-- | Send a custom lossless packet to a friend.+--+-- The first byte of data must be in the range 160-191. Maximum length of a+-- custom packet is 'tox_max_custom_packet_size'.+--+-- Lossless packet behaviour is comparable to TCP (reliability, arrive in order)+-- but with packets instead of a stream.+--+-- @param friend_number The friend number of the friend this lossless packet+--   should be sent to.+-- @param data A byte array containing the packet data.+-- @param length The length of the packet data byte array.+--+-- @return true on success.+foreign import ccall tox_friend_send_lossless_packet :: Tox a -> Word32 -> CString -> CSize -> CErr ErrFriendCustomPacket -> IO Bool+callFriendLosslessPacket :: (Tox a -> Word32 -> CString -> CSize -> CErr ErrFriendCustomPacket -> IO Bool) ->+                         Tox a -> Word32 -> BS.ByteString -> IO (Either ErrFriendCustomPacket Bool)+callFriendLosslessPacket f tox fn d =+  BS.useAsCStringLen d $ \(dataPtr, dataLen) ->+    callErrFun $ f tox fn dataPtr (fromIntegral dataLen)++toxFriendLosslessPacket :: Tox a -> Word32 -> BS.ByteString -> IO (Either ErrFriendCustomPacket Bool)+toxFriendLosslessPacket = callFriendLosslessPacket tox_friend_send_lossless_packet++callFriendCustomPacketCb :: FriendLosslessPacketCb a -> CFriendLosslessPacketCb a+callFriendCustomPacketCb f tox fn dataPtr dataLen udPtr = do+  ud <- deRefStablePtr udPtr+  d <- BS.packCStringLen (dataPtr, fromIntegral dataLen)+  modifyMVar_ ud $ f tox fn d++-- | @param friend_number The friend number of the friend who sent a lossy+-- packet.+-- @param data A byte array containing the received packet data.+-- @param length The length of the packet data byte array.+type FriendLossyPacketCb a = Tox a -> Word32 -> BS.ByteString -> a -> IO a+type CFriendLossyPacketCb a = Tox a -> Word32 -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendLossyPacketCb :: CFriendLossyPacketCb a -> IO (FunPtr (CFriendLossyPacketCb a))++callFriendLossyPacketCb :: FriendLossyPacketCb a -> CFriendLossyPacketCb a+callFriendLossyPacketCb = callFriendCustomPacketCb++friendLossyPacketCb :: FriendLossyPacketCb a -> IO (FunPtr (CFriendLossyPacketCb a))+friendLossyPacketCb = wrapFriendLossyPacketCb . callFriendLossyPacketCb+++-- | Set the callback for the `friend_lossy_packet` event. Pass 'nullPtr' to+-- unset.+--+foreign import ccall tox_callback_friend_lossy_packet :: Tox a -> FunPtr (CFriendLossyPacketCb a) -> IO ()++-- | @param friend_number The friend number of the friend who sent the packet.+-- @param data A byte array containing the received packet data.+-- @param length The length of the packet data byte array.+type FriendLosslessPacketCb a = Tox a -> Word32 -> BS.ByteString -> a -> IO a+type CFriendLosslessPacketCb a = Tox a -> Word32 -> CString -> CSize -> UserData a -> IO ()+foreign import ccall "wrapper" wrapFriendLosslessPacketCb :: CFriendLosslessPacketCb a -> IO (FunPtr (CFriendLosslessPacketCb a))++callFriendLosslessPacketCb :: FriendLosslessPacketCb a -> CFriendLosslessPacketCb a+callFriendLosslessPacketCb = callFriendCustomPacketCb++friendLosslessPacketCb :: FriendLosslessPacketCb a -> IO (FunPtr (CFriendLosslessPacketCb a))+friendLosslessPacketCb = wrapFriendLosslessPacketCb . callFriendLosslessPacketCb+++-- | Set the callback for the `friend_lossless_packet` event. Pass 'nullPtr' to+-- unset.+--+foreign import ccall tox_callback_friend_lossless_packet :: Tox a -> FunPtr (CFriendLosslessPacketCb a) -> IO ()+++--------------------------------------------------------------------------------+--+-- :: Low-level network information+--+--------------------------------------------------------------------------------+++-- | Writes the temporary DHT public key of this instance to a byte array.+--+-- This can be used in combination with an externally accessible IP address and+-- the bound port (from tox_self_get_udp_port) to run a temporary bootstrap+-- node.+--+-- Be aware that every time a new instance is created, the DHT public key+-- changes, meaning this cannot be used to run a permanent bootstrap node.+--+-- @param dht_id A memory region of at least 'tox_public_key_size' bytes. If+--   this parameter is 'nullPtr', this function has no effect.+foreign import ccall tox_self_get_dht_id :: Tox a -> CString -> IO ()++toxSelfGetDhtId :: Tox a -> IO BS.ByteString+toxSelfGetDhtId tox =+  let idLen = fromIntegral tox_public_key_size in+  allocaArray idLen $ \idPtr -> do+    tox_self_get_dht_id tox idPtr+    BS.packCStringLen (idPtr, idLen)++data ErrGetPort+  = ErrGetPortOk+    -- The function returned successfully.++  | ErrGetPortNotBound+    -- The instance was not bound to any port.+  deriving (Eq, Ord, Enum, Bounded, Read, Show)+++-- | Return the UDP port this Tox instance is bound to.+foreign import ccall tox_self_get_udp_port :: Tox a -> CErr ErrGetPort -> IO Word16+callSelfGetUdpPort :: (Tox a -> CErr ErrGetPort -> IO Word16) ->+                        Tox a -> IO (Either ErrGetPort Word16)+callSelfGetUdpPort f tox = callErrFun $ f tox++toxSelfGetUdpPort :: Tox a -> IO (Either ErrGetPort Word16)+toxSelfGetUdpPort = callSelfGetUdpPort tox_self_get_udp_port++-- | Return the TCP port this Tox instance is bound to. This is only relevant if+-- the instance is acting as a TCP relay.+foreign import ccall tox_self_get_tcp_port :: Tox a -> CErr ErrGetPort -> IO Word16+callSelfGetTcpPort :: (Tox a -> CErr ErrGetPort -> IO Word16) ->+                        Tox a -> IO (Either ErrGetPort Word16)+callSelfGetTcpPort f tox = callErrFun $ f tox++toxSelfGetTcpPort :: Tox a -> IO (Either ErrGetPort Word16)+toxSelfGetTcpPort = callSelfGetTcpPort tox_self_get_udp_port
+ src/Network/Tox/C/Type.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE Safe #-}+module Network.Tox.C.Type where++import           Control.Concurrent.MVar (MVar)+import           Foreign.Ptr             (Ptr)+import           Foreign.StablePtr       (StablePtr)+++-- | The Tox instance type. All the state associated with a connection is held+-- within the instance. Multiple instances can exist and operate concurrently.+-- The maximum number of Tox instances that can exist on a single network device+-- is limited. Note that this is not just a per-process limit, since the+-- limiting factor is the number of usable ports on a device.+data ToxStruct a+type Tox a = Ptr (ToxStruct a)++type UserData a = StablePtr (MVar a)
+ src/Network/Tox/C/Version.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE Trustworthy #-}+module Network.Tox.C.Version where++import           Data.Word (Word32)++--------------------------------------------------------------------------------+--+-- :: API version+--+--------------------------------------------------------------------------------++-- | The major version number. Incremented when the API or ABI changes in an+-- incompatible way.+foreign import ccall tox_version_major :: Word32++-- | The minor version number. Incremented when functionality is added without+-- breaking the API or ABI. Set to 0 when the major version number is+-- incremented.+foreign import ccall tox_version_minor :: Word32++-- | The patch or revision number. Incremented when bugfixes are applied without+-- changing any functionality or API or ABI.+foreign import ccall tox_version_patch :: Word32++-- | Return whether the compiled library version is compatible with the passed+-- version numbers.+foreign import ccall tox_version_is_compatible :: Word32 -> Word32 -> Word32 -> Bool
+ test/Network/Tox/C/ToxSpec.hs view
@@ -0,0 +1,119 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE Trustworthy #-}+module Network.Tox.C.ToxSpec where++import           Control.Applicative               ((<$>), (<*>))+import qualified Crypto.Saltine.Internal.ByteSizes as Sodium (boxPK, boxSK)+import qualified Data.ByteString                   as BS+import           Data.ByteString.Arbitrary         (fromABS)+import           Data.Default.Class                (def)+import           Test.Hspec+import           Test.QuickCheck+import           Test.QuickCheck.Arbitrary         (arbitraryBoundedEnum,+                                                    genericShrink)++import qualified Network.Tox.C                     as C+++instance Arbitrary C.ProxyType where+  shrink = genericShrink+  arbitrary = arbitraryBoundedEnum++instance Arbitrary C.SavedataType where+  shrink = genericShrink+  arbitrary = arbitraryBoundedEnum++instance Arbitrary BS.ByteString where+  shrink bs = if BS.null bs then [] else BS.inits bs+  arbitrary = fromABS <$> arbitrary++-- | Ensure that the hostname has a chance of being valid.+filterHost :: C.Options -> C.Options+filterHost o@C.Options{C.proxyHost = h} = o{C.proxyHost = filter (`elem` hostChars) h}+  where+    hostChars = ".-_" ++ ['0'..'9'] ++ ['a'..'z'] ++ ['A'..'Z']++instance Arbitrary C.Options where+  shrink = map filterHost . genericShrink+  arbitrary = fmap filterHost $ C.Options+    <$> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+++getRight :: (Monad m, Show a) => Either a b -> m b+getRight (Left  l) = fail $ show l+getRight (Right r) = return r+++must :: Show a => IO (Either a b) -> IO b+must = (getRight =<<)+++spec :: Spec+spec = do+  describe "tox_version_is_compatible" $+    it "is compatible with the major/minor/patch of the linked library" $+      C.tox_version_is_compatible+        C.tox_version_major+        C.tox_version_minor+        C.tox_version_patch+      `shouldBe` True++  describe "Constants" $+    it "has constants equal to the hstox expected key size" $ do+      fromIntegral C.tox_public_key_size `shouldBe` Sodium.boxPK+      fromIntegral C.tox_secret_key_size `shouldBe` Sodium.boxSK+      C.tox_address_size `shouldBe` C.tox_public_key_size + 6+      C.tox_max_name_length `shouldBe` 128+      C.tox_max_status_message_length `shouldBe` 1007+      C.tox_max_friend_request_length `shouldBe` 1016+      C.tox_max_message_length `shouldBe` C.tox_max_custom_packet_size - 1+      C.tox_max_custom_packet_size `shouldBe` 1373+      C.tox_max_filename_length `shouldBe` 255+      C.tox_hash_length `shouldBe` C.tox_file_id_length++  describe "Options" $ do+    it "can be marshalled to C and back" $+      property $ \options -> do+        res <- C.withOptions options C.peekToxOptions+        res `shouldBe` Right options++    it "is saved correctly by pokeToxOptions" $+      property $ \options0 options1 -> do+        res <- C.withOptions options0 $ \ptr -> do+          C.pokeToxOptions options1 ptr (return ())+          C.peekToxOptions ptr+        res `shouldBe` Right options0++    it "has a 'def' that is equivalent to the C default options" $ do+      res <- C.withToxOptions C.peekToxOptions+      res `shouldBe` Right def++  describe "nospam" $+    it "can be retrieved after being set" $+      property $ \nospam ->+        must $ C.withDefaultTox $ \tox -> do+          C.tox_self_set_nospam tox nospam+          nospam' <- C.tox_self_get_nospam tox+          nospam' `shouldBe` nospam++  describe "public key" $ do+    it "is a prefix of the address" $+      must $ C.withDefaultTox $ \tox -> do+        pk <- C.toxSelfGetPublicKey tox+        addr <- C.toxSelfGetAddress tox+        BS.unpack addr `shouldStartWith` BS.unpack pk++    it "is not equal to the secret key" $+      must $ C.withDefaultTox $ \tox -> do+        pk <- C.toxSelfGetPublicKey tox+        sk <- C.toxSelfGetSecretKey tox+        pk `shouldNotBe` sk
+ test/Network/Tox/CSpec.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE Trustworthy                #-}+module Network.Tox.CSpec where++import           Control.Applicative     ((<$>))+import           Control.Concurrent      (threadDelay)+import           Control.Concurrent.MVar (MVar, newMVar, readMVar)+import           Control.Exception       (bracket)+import           Control.Monad           (when)+import qualified Data.ByteString         as BS+import qualified Data.ByteString.Base16  as Base16+import           Data.String             (fromString)+import           Foreign.StablePtr       (StablePtr, freeStablePtr,+                                          newStablePtr)+import           Foreign.Storable        (Storable (..))+import           Test.Hspec++import qualified Network.Tox.C           as C+++bootstrapKey :: BS.ByteString+bootstrapKey =+  fst . Base16.decode . fromString $+    "1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976"++bootstrapHost :: String+bootstrapHost = "tox.verdict.gg"+++options :: C.Options+options = C.Options+  { C.ipv6Enabled  = True+  , C.udpEnabled   = True+  , C.proxyType    = C.ProxyTypeNone+  , C.proxyHost    = ""+  , C.proxyPort    = 0+  , C.startPort    = 33445+  , C.endPort      = 33545+  , C.tcpPort      = 3128+  , C.savedataType = C.SavedataTypeNone+  , C.savedataData = BS.empty+  }+++while :: IO Bool -> IO () -> IO ()+while cond io = do+  continue <- cond+  when continue $ io >> while cond io+++getRight :: (Monad m, Show a) => Either a b -> m b+getRight (Left  l) = fail $ show l+getRight (Right r) = return r+++must :: Show a => IO (Either a b) -> IO b+must = (getRight =<<)+++newtype UserData = UserData Int+  deriving (Eq, Storable, Read, Show)++instance C.CHandler UserData where+  cSelfConnectionStatus _ conn ud = do+    print conn+    print ud+    return $ UserData 4321+++withStablePtr :: a -> (StablePtr a -> IO b) -> IO b+withStablePtr x = bracket (newStablePtr x) freeStablePtr+++toxIterate :: MVar a -> C.Tox a -> IO ()+toxIterate ud tox =+  withStablePtr ud (C.tox_iterate tox)+++spec :: Spec+spec =+  describe "toxcore" $+    it "can bootstrap" $+      must $ C.withOptions options $ \optPtr ->+        must $ C.withTox optPtr $ \tox -> do+          must $ C.toxBootstrap   tox bootstrapHost 33445 bootstrapKey+          must $ C.toxAddTcpRelay tox bootstrapHost 33445 bootstrapKey++          C.withCHandler tox $ do+            ud <- newMVar (UserData 1234)+            while ((/= UserData 4321) <$> readMVar ud) $ do+              toxIterate ud tox+              putStrLn "tox_iterate"+              interval <- C.tox_iteration_interval tox+              threadDelay $ fromIntegral $ interval * 10000+            putStrLn "done"
+ test/testsuite.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ tools/groupbot/Main.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase                 #-}+module Main (main) where++import           Control.Concurrent      (threadDelay)+import           Control.Concurrent.MVar (MVar, newMVar)+import           Control.Exception       (AsyncException (UserInterrupt), catch,+                                          throwIO)+import           Control.Monad           (forever)+import qualified Data.ByteString.Base16  as Base16+import qualified Data.ByteString.Char8   as BS+import           Data.String             (fromString)+import           Data.Word               (Word32)+import           Foreign.Storable        (Storable (..))+import           System.Directory        (doesFileExist)+import           System.Exit             (exitSuccess)++import qualified Network.Tox.C           as C+++bootstrapKey :: BS.ByteString+bootstrapKey =+  fst . Base16.decode . fromString $+    "15E9C309CFCB79FDDF0EBA057DABB49FE15F3803B1BFF06536AE2E5BA5E4690E"++isMasterKey :: BS.ByteString -> Bool+isMasterKey key =+  (key ==) . fst . Base16.decode . fromString $+    "040F75B5C8995F9525F9A8692A6C355286BBD3CF248C984560733421274F0365"++botName :: String+botName = "groupbot"++bootstrapHost :: String+bootstrapHost = "tox.ngc.zone"++savedataFilename :: String+savedataFilename = "groupbot.tox"++options :: BS.ByteString -> C.Options+options savedata = C.Options+  { C.ipv6Enabled  = True+  , C.udpEnabled   = True+  , C.proxyType    = C.ProxyTypeNone+  , C.proxyHost    = ""+  , C.proxyPort    = 0+  , C.startPort    = 33445+  , C.endPort      = 33545+  , C.tcpPort      = 3128+  , C.savedataType = if savedata == BS.empty then C.SavedataTypeNone else C.SavedataTypeToxSave+  , C.savedataData = savedata+  }+++getRight :: (Monad m, Show a) => Either a b -> m b+getRight (Left  l) = fail $ show l+getRight (Right r) = return r+++must :: Show a => IO (Either a b) -> IO b+must = (getRight =<<)+++newtype UserData = UserData Word32+  deriving (Eq, Storable, Read, Show)++instance C.CHandler UserData where+  cSelfConnectionStatus _ conn ud = do+    putStrLn "SelfConnectionStatusCb"+    print conn+    return ud++  cFriendRequest tox pk msg ud = do+    putStrLn "FriendRequestCb"+    Right fn <- C.toxFriendAddNorequest tox pk+    putStrLn $ (BS.unpack . Base16.encode) pk+    putStrLn msg+    print fn+    return ud++  cFriendConnectionStatus tox fn status ud@(UserData gn) = do+    putStrLn "FriendConnectionStatusCb"+    print fn+    print status+    if status /= C.ConnectionNone+    then do+      putStrLn "Inviting!"+      _ <- C.toxConferenceInvite tox fn gn+      return ()+    else+      putStrLn "Friend offline"+    return ud++  cFriendMessage tox fn msgType msg ud = do+    putStrLn "FriendMessage"+    print fn+    print msgType+    putStrLn msg+    _ <- C.toxFriendSendMessage tox fn msgType msg+    return ud++  cConferenceInvite tox fn _confType cookie ud = do+    putStrLn "ConferenceInvite"+    print fn+    pk <- getRight =<< C.toxFriendGetPublicKey tox fn+    if isMasterKey pk+    then do+      putStrLn "Joining!"+      gn <- getRight =<< C.toxConferenceJoin tox fn cookie+      return $ UserData gn+    else do+      putStrLn "Not master!"+      return ud+++loop :: MVar ud -> C.Tox ud -> IO a+loop ud tox =+  forever $ do+    C.toxIterate tox ud+    interval <- C.tox_iteration_interval tox+    threadDelay $ fromIntegral $ interval * 10000+++main :: IO ()+main = do+  exists <- doesFileExist savedataFilename+  loadedSavedata <- if exists then BS.readFile savedataFilename else return BS.empty+  must $ C.withOptions (options loadedSavedata) $ \optPtr ->+    must $ C.withTox optPtr $ \tox -> do+      must $ C.toxBootstrap tox bootstrapHost 33445 bootstrapKey++      C.withCHandler tox $ do+        adr <- C.toxSelfGetAddress tox+        putStrLn $ (BS.unpack . Base16.encode) adr+        _ <- C.toxSelfSetName tox botName+        gn <- getRight =<< C.toxConferenceNew tox+        ud <- newMVar (UserData gn)+        catch (loop ud tox) $ \case+          e@UserInterrupt -> throwIO e+          _ -> do+            savedSavedata <- C.toxGetSavedata tox+            BS.writeFile savedataFilename savedSavedata+            exitSuccess
+ toxcore-c.cabal view
@@ -0,0 +1,80 @@+name:                 toxcore-c+synopsis:             Haskell bindings to the C reference implementation of Tox+version:              0.2.11+cabal-version:        >= 1.10+license:              GPL-3+license-file:         LICENSE+build-type:           Simple+author:               iphy+maintainer:           iphy+copyright:            © 2016-2020 The TokTok Team+homepage:             https://toktok.github.io+category:             Network+description:+  Haskell bindings to the C reference implementation of Tox.+  .+  See <https://github.com/TokTok/c-toxcore>.++source-repository head+  type: git+  location: https://github.com/TokTok/hs-toxcore-c++library+  default-language: Haskell2010+  hs-source-dirs:+      src+  ghc-options:+      -Wall+  extra-libraries: toxcore+  exposed-modules:+      Network.Tox.C+      Network.Tox.C.CEnum+      Network.Tox.C.Callbacks+      Network.Tox.C.Constants+      Network.Tox.C.Options+      Network.Tox.C.Tox+      Network.Tox.C.Type+      Network.Tox.C.Version+  build-depends:+      base < 5+    , bytestring+    , data-default-class++executable groupbot+  default-language: Haskell2010+  hs-source-dirs:+      tools/groupbot+  ghc-options:+      -Wall+      -fno-warn-unused-imports+  extra-libraries: toxcore+  main-is: Main.hs+  build-depends:+      base < 5+    , base16-bytestring+    , bytestring+    , directory+    , toxcore-c++test-suite testsuite+  default-language: Haskell2010+  type: exitcode-stdio-1.0+  hs-source-dirs:+      test+  ghc-options:+      -Wall+      -fno-warn-unused-imports+  main-is: testsuite.hs+  other-modules:+      Network.Tox.C.ToxSpec+      Network.Tox.CSpec+  build-depends:+      base < 5+    , QuickCheck >= 2.9.1+    , base16-bytestring+    , bytestring+    , bytestring-arbitrary+    , data-default-class+    , hspec+    , saltine+    , toxcore-c