diff --git a/LICENSE.AGPL.txt b/LICENSE.AGPL.txt new file mode 100644 index 000000000..be3f7b28e --- /dev/null +++ b/LICENSE.AGPL.txt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +. diff --git a/README.md b/README.md index 3d98c2312..2619312a8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Build Status | Platform | Build Status | | :------------- | :------------- | | GNU/Linux, MacOS, (via travis-ci) | [![Build Status](https://travis-ci.org/RetroShare/RetroShare.svg?branch=master)](https://travis-ci.org/RetroShare/RetroShare) | -| Windows, `MSys2` (via appveyor) | [![Build status](https://ci.appveyor.com/api/projects/status/github/RetroShare/RetroShare?svg=true)](https://ci.appveyor.com/project/G10h4ck/retroshare-u4lmn) | +| Windows, `MSys2` (via appveyor) | [![Build status](https://ci.appveyor.com/api/projects/status/github/RetroShare/RetroShare?svg=true)](https://ci.appveyor.com/project/RetroShare58622/retroshare) | Compilation on Windows ---------------------------- diff --git a/appveyor.yml b/appveyor.yml index 42d2f0a58..420e0bb9b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -85,7 +85,8 @@ environment: # - cmd: echo This is batch again # - cmd: set MY_VAR=12345 install: - - git submodule update --init + # We cannot get OBS submodule as it use illegal folder name for windows. + #- git submodule update --init # Configuring MSys2 - set PATH=C:\msys64\usr\bin;%PATH% - set PATH=C:\msys64\mingw32\bin;%PATH% @@ -195,11 +196,13 @@ after_build: - windeployqt %RS_DEPLOY%\retroshare.exe - copy C:\msys64\mingw32\bin\libbz2*.dll %RS_DEPLOY%\ - - copy C:\msys64\mingw32\bin\libeay32.dll %RS_DEPLOY%\ + #- copy C:\msys64\mingw32\bin\libeay32.dll %RS_DEPLOY%\ + - copy C:\OpenSSL-Win32\libeay32.dll %RS_DEPLOY%\ - copy C:\msys64\mingw32\bin\libminiupnpc.dll %RS_DEPLOY%\ - copy C:\msys64\mingw32\bin\libsqlcipher*.dll %RS_DEPLOY%\ - copy C:\msys64\mingw32\bin\libsqlite3*.dll %RS_DEPLOY%\ - - copy C:\msys64\mingw32\bin\ssleay32.dll %RS_DEPLOY%\ + #- copy C:\msys64\mingw32\bin\ssleay32.dll %RS_DEPLOY%\ + - copy C:\OpenSSL-Win32\ssleay32.dll %RS_DEPLOY%\ - copy C:\msys64\mingw32\bin\zlib*.dll %RS_DEPLOY%\ - copy C:\msys64\mingw32\bin\libgcc_s_dw2*.dll %RS_DEPLOY%\ - copy C:\msys64\mingw32\bin\libstdc*.dll %RS_DEPLOY%\ diff --git a/libretroshare/src/file_sharing/hash_cache.cc b/libretroshare/src/file_sharing/hash_cache.cc index 222bb81e0..340391f03 100644 --- a/libretroshare/src/file_sharing/hash_cache.cc +++ b/libretroshare/src/file_sharing/hash_cache.cc @@ -133,12 +133,7 @@ void HashStorage::data_tick() if(!mChanged) // otherwise it might prevent from saving the hash cache { - std::cerr << "Stopping hashing thread." << std::endl; - shutdown(); - mRunning = false ; - mTotalSizeToHash = 0; - mTotalFilesToHash = 0; - std::cerr << "done." << std::endl; + stopHashThread(); } RsServer::notify()->notifyHashingInfo(NOTIFY_HASHTYPE_FINISH, "") ; @@ -262,10 +257,12 @@ bool HashStorage::requestHash(const std::string& full_path,uint64_t size,rstime_ it->second.time_stamp = now ; #ifdef WINDOWS_SYS - if(it->second.time_stamp != (uint64_t)mod_time) + if(it->second.modf_stamp != (uint64_t)mod_time) { std::cerr << "(WW) detected a 1 hour shift in file modification time. This normally happens to many files at once, when daylight saving time shifts (file=\"" << full_path << "\")." << std::endl; - it->second.time_stamp = (uint64_t)mod_time; + it->second.modf_stamp = (uint64_t)mod_time; + mChanged = true; + startHashThread(); } #endif @@ -301,6 +298,13 @@ bool HashStorage::requestHash(const std::string& full_path,uint64_t size,rstime_ mTotalSizeToHash += size ; ++mTotalFilesToHash; + startHashThread(); + + return false; +} + +void HashStorage::startHashThread() +{ if(!mRunning) { mRunning = true ; @@ -308,10 +312,21 @@ bool HashStorage::requestHash(const std::string& full_path,uint64_t size,rstime_ mHashCounter = 0; mTotalHashedSize = 0; - start("fs hash cache") ; + start("fs hash cache") ; } +} - return false; +void HashStorage::stopHashThread() +{ + if (mRunning) + { + std::cerr << "Stopping hashing thread." << std::endl; + shutdown(); + mRunning = false ; + mTotalSizeToHash = 0; + mTotalFilesToHash = 0; + std::cerr << "done." << std::endl; + } } void HashStorage::clean() diff --git a/libretroshare/src/file_sharing/hash_cache.h b/libretroshare/src/file_sharing/hash_cache.h index 3cf8f6c3a..ecf19802a 100644 --- a/libretroshare/src/file_sharing/hash_cache.h +++ b/libretroshare/src/file_sharing/hash_cache.h @@ -97,6 +97,9 @@ private: */ void clean() ; + void startHashThread(); + void stopHashThread(); + // loading/saving the entire hash database to a file void locked_save() ; diff --git a/libretroshare/src/gxstunnel/p3gxstunnel.cc b/libretroshare/src/gxstunnel/p3gxstunnel.cc index 3c92bb312..d5e3b3a90 100644 --- a/libretroshare/src/gxstunnel/p3gxstunnel.cc +++ b/libretroshare/src/gxstunnel/p3gxstunnel.cc @@ -692,22 +692,40 @@ void p3GxsTunnelService::receiveTurtleData(const RsTurtleGenericTunnelItem *gite (void) direction; #endif - const RsTurtleGenericDataItem *item = dynamic_cast(gitem) ; + void *data_bytes; + uint32_t data_size ; + bool accept_fast_items = false; - if(item == NULL) + const RsTurtleGenericFastDataItem *fitem = dynamic_cast(gitem) ; + + if(fitem != NULL) { - std::cerr << "(EE) item is not a data item. That is an error." << std::endl; - return ; + data_bytes = fitem->data_bytes ; + data_size = fitem->data_size ; + accept_fast_items = true; } - // Call the AES crypto module + else + { + const RsTurtleGenericDataItem *item = dynamic_cast(gitem) ; + + if(item == NULL) + { + std::cerr << "(EE) item is not a data item. That is an error." << std::endl; + return ; + } + data_bytes = item->data_bytes ; + data_size = item->data_size ; + } + + // Call the AES crypto module // - the IV is the first 8 bytes of item->data_bytes - if(item->data_size < 8) + if(data_size < 8) { - std::cerr << "(EE) item encrypted data stream is too small: size = " << item->data_size << std::endl; + std::cerr << "(EE) item encrypted data stream is too small: size = " << data_size << std::endl; return ; } - if(*((uint64_t*)item->data_bytes) != 0) // WTF?? we should use flags + if(*((uint64_t*)data_bytes) != 0) // WTF?? we should use flags { #ifdef DEBUG_GXS_TUNNEL std::cerr << " Item is encrypted." << std::endl; @@ -715,7 +733,7 @@ void p3GxsTunnelService::receiveTurtleData(const RsTurtleGenericTunnelItem *gite // if cannot decrypt, it means the key is wrong. We need to re-negociate a new key. - handleEncryptedData((uint8_t*)item->data_bytes,item->data_size,hash,virtual_peer_id) ; + handleEncryptedData((uint8_t*)data_bytes,data_size,hash,virtual_peer_id,accept_fast_items) ; } else { @@ -725,8 +743,8 @@ void p3GxsTunnelService::receiveTurtleData(const RsTurtleGenericTunnelItem *gite // Now try deserialise the decrypted data to make an RsItem out of it. // - uint32_t pktsize = item->data_size-8; - RsItem *citem = RsGxsTunnelSerialiser().deserialise(&((uint8_t*)item->data_bytes)[8],&pktsize) ; + uint32_t pktsize = data_size-8; + RsItem *citem = RsGxsTunnelSerialiser().deserialise(&((uint8_t*)data_bytes)[8],&pktsize) ; if(citem == NULL) { @@ -752,7 +770,11 @@ void p3GxsTunnelService::receiveTurtleData(const RsTurtleGenericTunnelItem *gite // This function encrypts the given data and adds a MAC and an IV into a serialised memory chunk that is then sent through the tunnel. +#ifndef V07_NON_BACKWARD_COMPATIBLE_CHANGE_004 +bool p3GxsTunnelService::handleEncryptedData(const uint8_t *data_bytes,uint32_t data_size,const TurtleFileHash& hash,const RsPeerId& virtual_peer_id,bool accepts_fast_items) +#else bool p3GxsTunnelService::handleEncryptedData(const uint8_t *data_bytes,uint32_t data_size,const TurtleFileHash& hash,const RsPeerId& virtual_peer_id) +#endif { #ifdef DEBUG_GXS_TUNNEL std::cerr << "p3GxsTunnelService::handleEncryptedDataItem()" << std::endl; @@ -795,6 +817,16 @@ bool p3GxsTunnelService::handleEncryptedData(const uint8_t *data_bytes,uint32_t std::cerr << "(EE) no tunnel data for tunnel ID=" << tunnel_id << ". This is a bug." << std::endl; return false ; } +#ifndef V07_NON_BACKWARD_COMPATIBLE_CHANGE_004 + if(accepts_fast_items) + { + if(!it2->second.accepts_fast_turtle_items) + std::cerr << "(II) received probe for Fast track turtle items for tunnel VPID " << it2->second.virtual_peer_id << ": switching to Fast items mode." << std::endl; + + it2->second.accepts_fast_turtle_items = true; + } +#endif + memcpy(aes_key,it2->second.aes_key,GXS_TUNNEL_AES_KEY_SIZE) ; #ifdef DEBUG_GXS_TUNNEL @@ -1291,36 +1323,79 @@ bool p3GxsTunnelService::locked_sendEncryptedTunnelData(RsGxsTunnelItem *item) // make a TurtleGenericData item out of it: // - RsTurtleGenericDataItem *gitem = new RsTurtleGenericDataItem ; - gitem->data_size = encrypted_size + GXS_TUNNEL_ENCRYPTION_IV_SIZE + GXS_TUNNEL_ENCRYPTION_HMAC_SIZE ; - gitem->data_bytes = rs_malloc(gitem->data_size) ; + uint32_t data_size = encrypted_size + GXS_TUNNEL_ENCRYPTION_IV_SIZE + GXS_TUNNEL_ENCRYPTION_HMAC_SIZE ; + void *data_bytes = rs_malloc(data_size) ; - if(gitem->data_bytes == NULL) + if(data_bytes == NULL) return false ; - memcpy(& ((uint8_t*)gitem->data_bytes)[0] ,&IV,8) ; + memcpy(& ((uint8_t*)data_bytes)[0] ,&IV,8) ; unsigned int md_len = GXS_TUNNEL_ENCRYPTION_HMAC_SIZE ; - HMAC(EVP_sha1(),aes_key,GXS_TUNNEL_AES_KEY_SIZE,encrypted_data,encrypted_size,&(((uint8_t*)gitem->data_bytes)[GXS_TUNNEL_ENCRYPTION_IV_SIZE]),&md_len) ; + HMAC(EVP_sha1(),aes_key,GXS_TUNNEL_AES_KEY_SIZE,encrypted_data,encrypted_size,&(((uint8_t*)data_bytes)[GXS_TUNNEL_ENCRYPTION_IV_SIZE]),&md_len) ; - memcpy(& (((uint8_t*)gitem->data_bytes)[GXS_TUNNEL_ENCRYPTION_HMAC_SIZE+GXS_TUNNEL_ENCRYPTION_IV_SIZE]),encrypted_data,encrypted_size) ; + memcpy(& (((uint8_t*)data_bytes)[GXS_TUNNEL_ENCRYPTION_HMAC_SIZE+GXS_TUNNEL_ENCRYPTION_IV_SIZE]),encrypted_data,encrypted_size) ; #ifdef DEBUG_GXS_TUNNEL std::cerr << " Using IV: " << std::hex << IV << std::dec << std::endl; std::cerr << " Using Key: " << RsUtil::BinToHex((char*)aes_key,GXS_TUNNEL_AES_KEY_SIZE) ; std::cerr << std::endl; - std::cerr << " hmac: " << RsUtil::BinToHex((char*)gitem->data_bytes,GXS_TUNNEL_ENCRYPTION_HMAC_SIZE) << std::endl; + std::cerr << " hmac: " << RsUtil::BinToHex((char*)data_bytes,GXS_TUNNEL_ENCRYPTION_HMAC_SIZE) << std::endl; #endif #ifdef DEBUG_GXS_TUNNEL std::cerr << "GxsTunnelService::sendEncryptedTunnelData(): Sending encrypted data to virtual peer: " << virtual_peer_id << std::endl; - std::cerr << " gitem->data_size = " << gitem->data_size << std::endl; - std::cerr << " serialised data = " << RsUtil::BinToHex((unsigned char*)gitem->data_bytes,gitem->data_size,100) ; + std::cerr << " data_size = " << data_size << std::endl; + std::cerr << " serialised data = " << RsUtil::BinToHex((unsigned char*)data_bytes,data_size,100) ; std::cerr << std::endl; #endif - mTurtle->sendTurtleData(virtual_peer_id,gitem) ; - - return true ; + // We send the item through the turtle tunnel. We do that using the new 'fast' item type if the tunnel accepts it, and using the old slow one otherwise. + // Still if the tunnel hasn't been probed, we duplicate the packet using the new fast item format. The packet being received twice on the other side will + // be discarded with a warning. + // Note that because the data is deleted by sendTurtleData(), we need to send all packets at the end, and properly duplicate the data when needed. + +#ifdef V07_NON_BACKWARD_COMPATIBLE_CHANGE_004 + RsTurtleGenericFastDataItem *gitem = new RsTurtleGenericFastDataItem; + gitem->data_bytes = data_bytes; + gitem->data_size = data_size ; +#else + RsTurtleGenericDataItem *gitem = NULL; + RsTurtleGenericFastDataItem *gitem2 = NULL; + + if(!it->second.accepts_fast_turtle_items) + { + std::cerr << "Sending old format (slow) item for packet IV=" << std::hex << IV << std::dec << " in tunnel VPID=" << it->second.virtual_peer_id << std::endl; + gitem = new RsTurtleGenericDataItem ; + + gitem->data_bytes = data_bytes; + gitem->data_size = data_size ; + } + + if(it->second.accepts_fast_turtle_items || !it->second.already_probed_for_fast_items) + { + std::cerr << "Sending new format (fast) item for packet IV=" << std::hex << IV << std::dec << " in tunnel VPID=" << it->second.virtual_peer_id << std::endl; + gitem2 = new RsTurtleGenericFastDataItem ; + + if(gitem != NULL) // duplicate the data because it was already sent in gitem. + { + gitem2->data_bytes = rs_malloc(data_size); + gitem2->data_size = data_size ; + memcpy(gitem2->data_bytes,data_bytes,data_size); + } + else + { + gitem2->data_bytes = data_bytes; + gitem2->data_size = data_size ; + } + + it->second.already_probed_for_fast_items = true; + + } + if(gitem2) mTurtle->sendTurtleData(virtual_peer_id,gitem2) ; +#endif + if(gitem) mTurtle->sendTurtleData(virtual_peer_id,gitem) ; + + return true ; } bool p3GxsTunnelService::requestSecuredTunnel(const RsGxsId& to_gxs_id, const RsGxsId& from_gxs_id, RsGxsTunnelId &tunnel_id, uint32_t service_id, uint32_t& error_code) diff --git a/libretroshare/src/gxstunnel/p3gxstunnel.h b/libretroshare/src/gxstunnel/p3gxstunnel.h index bcbce734a..00bd6d65a 100644 --- a/libretroshare/src/gxstunnel/p3gxstunnel.h +++ b/libretroshare/src/gxstunnel/p3gxstunnel.h @@ -128,14 +128,12 @@ public: // Creates the invite if the public key of the distant peer is available. // Om success, stores the invite in the map above, so that we can respond to tunnel requests. // - virtual bool requestSecuredTunnel(const RsGxsId& to_id,const RsGxsId& from_id,RsGxsTunnelId& tunnel_id,uint32_t service_id,uint32_t& error_code) ; - - virtual bool closeExistingTunnel(const RsGxsTunnelId &tunnel_id,uint32_t service_id) ; - virtual bool getTunnelsInfo(std::vector& infos); - virtual bool getTunnelInfo(const RsGxsTunnelId& tunnel_id,GxsTunnelInfo& info); - virtual bool sendData(const RsGxsTunnelId& tunnel_id,uint32_t service_id,const uint8_t *data,uint32_t size) ; - - virtual bool registerClientService(uint32_t service_id,RsGxsTunnelClientService *service) ; + virtual bool requestSecuredTunnel(const RsGxsId& to_id,const RsGxsId& from_id,RsGxsTunnelId& tunnel_id,uint32_t service_id,uint32_t& error_code) override ; + virtual bool closeExistingTunnel(const RsGxsTunnelId &tunnel_id,uint32_t service_id) override ; + virtual bool getTunnelsInfo(std::vector& infos) override ; + virtual bool getTunnelInfo(const RsGxsTunnelId& tunnel_id,GxsTunnelInfo& info) override ; + virtual bool sendData(const RsGxsTunnelId& tunnel_id,uint32_t service_id,const uint8_t *data,uint32_t size) override ; + virtual bool registerClientService(uint32_t service_id,RsGxsTunnelClientService *service) override ; // derived from p3service @@ -150,6 +148,9 @@ private: { public: GxsTunnelPeerInfo() : last_contact(0), last_keep_alive_sent(0), status(0), direction(0) +#ifndef V07_NON_BACKWARD_COMPATIBLE_CHANGE_004 + ,accepts_fast_turtle_items(false) +#endif { memset(aes_key, 0, GXS_TUNNEL_AES_KEY_SIZE); @@ -158,20 +159,24 @@ private: } rstime_t last_contact ; // used to keep track of working connexion - rstime_t last_keep_alive_sent ; // last time we sent a keep alive packet. + rstime_t last_keep_alive_sent ; // last time we sent a keep alive packet. unsigned char aes_key[GXS_TUNNEL_AES_KEY_SIZE] ; - uint32_t status ; // info: do we have a tunnel ? - RsPeerId virtual_peer_id; // given by the turtle router. Identifies the tunnel. - RsGxsId to_gxs_id; // gxs id we're talking to - RsGxsId own_gxs_id ; // gxs id we're using to talk. - RsTurtleGenericTunnelItem::Direction direction ; // specifiec wether we are client(managing the tunnel) or server. - TurtleFileHash hash ; // hash that is last used. This is necessary for handling tunnel establishment - std::set client_services ;// services that used this tunnel - std::map received_data_prints ; // list of recently received messages, to avoid duplicates. Kept for 20 mins at most. - uint32_t total_sent ; - uint32_t total_received ; + uint32_t status ; // info: do we have a tunnel ? + RsPeerId virtual_peer_id; // given by the turtle router. Identifies the tunnel. + RsGxsId to_gxs_id; // gxs id we're talking to + RsGxsId own_gxs_id ; // gxs id we're using to talk. + RsTurtleGenericTunnelItem::Direction direction ; // specifiec wether we are client(managing the tunnel) or server. + TurtleFileHash hash ; // hash that is last used. This is necessary for handling tunnel establishment + std::set client_services ; // services that used this tunnel + std::map received_data_prints ; // list of recently received messages, to avoid duplicates. Kept for 20 mins at most. + uint32_t total_sent ; // total data sent to this peer + uint32_t total_received ; // total data received by this peer +#ifndef V07_NON_BACKWARD_COMPATIBLE_CHANGE_004 + bool accepts_fast_turtle_items; // does the tunnel accept RsTurtleGenericFastDataItem type? + bool already_probed_for_fast_items; // has the tunnel been probed already? If not, a fast item will be sent +#endif }; class GxsTunnelDHInfo @@ -243,7 +248,11 @@ private: bool locked_sendEncryptedTunnelData(RsGxsTunnelItem *item) ; bool locked_sendClearTunnelData(RsGxsTunnelDHPublicKeyItem *item); // this limits the usage to DH items. Others should be encrypted! - bool handleEncryptedData(const uint8_t *data_bytes,uint32_t data_size,const TurtleFileHash& hash,const RsPeerId& virtual_peer_id) ; +#ifndef V07_NON_BACKWARD_COMPATIBLE_CHANGE_004 + bool handleEncryptedData(const uint8_t *data_bytes, uint32_t data_size, const TurtleFileHash& hash, const RsPeerId& virtual_peer_id, bool accepts_fast_items) ; +#else + bool handleEncryptedData(const uint8_t *data_bytes, uint32_t data_size, const TurtleFileHash& hash, const RsPeerId& virtual_peer_id) ; +#endif // local data diff --git a/libretroshare/src/pqi/pqi_base.h b/libretroshare/src/pqi/pqi_base.h index b7c2746d2..e220a376d 100644 --- a/libretroshare/src/pqi/pqi_base.h +++ b/libretroshare/src/pqi/pqi_base.h @@ -185,9 +185,10 @@ class NetInterface; **/ class PQInterface: public RateInterface { - public: - explicit PQInterface(const RsPeerId &id) :peerId(id) { return; } - virtual ~PQInterface() { return; } +public: + explicit PQInterface(const RsPeerId &id) : + traf_in(0), traf_out(0), peerId(id) {} + virtual ~PQInterface() {} /*! * allows user to send RsItems to a particular facility (file, network) @@ -228,6 +229,22 @@ class PQInterface: public RateInterface const sockaddr_storage & /*remote_peer_address*/) { return 0; } + virtual uint64_t getTraffic(bool in) + { + uint64_t ret = 0; + if (in) + { + ret = traf_in; + traf_in = 0; + return ret; + } + ret = traf_out; + traf_out = 0; + return ret; + } + uint64_t traf_in; + uint64_t traf_out; + private: RsPeerId peerId; diff --git a/libretroshare/src/pqi/pqihandler.cc b/libretroshare/src/pqi/pqihandler.cc index dcaac8f29..1e86fa9d1 100644 --- a/libretroshare/src/pqi/pqihandler.cc +++ b/libretroshare/src/pqi/pqihandler.cc @@ -83,6 +83,8 @@ pqihandler::pqihandler() : coreMtx("pqihandler") rateMax_in = 0.01; rateTotal_in = 0.0 ; rateTotal_out = 0.0 ; + traffInSum = 0; + traffOutSum = 0; last_m = time(NULL) ; nb_ticks = 0 ; mLastRateCapUpdate = 0 ; @@ -371,6 +373,9 @@ int pqihandler::UpdateRates() { SearchModule *mod = (it -> second); float crate_in = mod -> pqi -> getRate(true); + + traffInSum += mod -> pqi -> getTraffic(true); + traffOutSum += mod -> pqi -> getTraffic(false); #ifdef PQI_HDL_DEBUG_UR if(crate_in > 0.0) @@ -540,4 +545,8 @@ void pqihandler::locked_StoreCurrentRates(float in, float out) rateTotal_out = out; } - +void pqihandler::GetTraffic(uint64_t &in, uint64_t &out) +{ + in = traffInSum; + out = traffOutSum; +} diff --git a/libretroshare/src/pqi/pqihandler.h b/libretroshare/src/pqi/pqihandler.h index d78c44752..4bfd4f828 100644 --- a/libretroshare/src/pqi/pqihandler.h +++ b/libretroshare/src/pqi/pqihandler.h @@ -87,6 +87,10 @@ class pqihandler: public P3Interface, public pqiPublisher int ExtractRates(std::map &ratemap, RsBwRates &totals); int ExtractTrafficInfo(std::list &out_lst, std::list &in_lst); + uint64_t traffInSum; + uint64_t traffOutSum; + void GetTraffic(uint64_t &in, uint64_t &out); + protected: /* check to be overloaded by those that can * generates warnings otherwise diff --git a/libretroshare/src/pqi/pqiperson.cc b/libretroshare/src/pqi/pqiperson.cc index c7541ec48..4c7c57e63 100644 --- a/libretroshare/src/pqi/pqiperson.cc +++ b/libretroshare/src/pqi/pqiperson.cc @@ -637,6 +637,13 @@ float pqiperson::getRate(bool in) return activepqi -> getRate(in); } +uint64_t pqiperson::getTraffic(bool in) +{ + if ((!active) || (activepqi == NULL)) + return 0; + return activepqi -> getTraffic(in); +} + void pqiperson::setMaxRate(bool in, float val) { RS_STACK_MUTEX(mPersonMtx); diff --git a/libretroshare/src/pqi/pqiperson.h b/libretroshare/src/pqi/pqiperson.h index 759446def..59bae7834 100644 --- a/libretroshare/src/pqi/pqiperson.h +++ b/libretroshare/src/pqi/pqiperson.h @@ -143,6 +143,7 @@ public: virtual int getQueueSize(bool in); virtual void getRates(RsBwRates &rates); virtual float getRate(bool in); + virtual uint64_t getTraffic(bool in); virtual void setMaxRate(bool in, float val); virtual void setRateCap(float val_in, float val_out); virtual int gatherStatistics(std::list& outqueue_lst, diff --git a/libretroshare/src/pqi/pqistreamer.cc b/libretroshare/src/pqi/pqistreamer.cc index 92afd528c..dc9ceaabc 100644 --- a/libretroshare/src/pqi/pqistreamer.cc +++ b/libretroshare/src/pqi/pqistreamer.cc @@ -216,6 +216,7 @@ float pqistreamer::getRate(bool b) RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ return RateInterface::getRate(b) ; } + void pqistreamer::setMaxRate(bool b,float f) { RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ @@ -1226,7 +1227,7 @@ void pqistreamer::outSentBytes_locked(uint32_t outb) mTotalSent += outb; mCurrSent += outb; mAvgSentCount += outb; - + PQInterface::traf_out += outb; return; } @@ -1243,7 +1244,7 @@ void pqistreamer::inReadBytes_locked(uint32_t inb) mTotalRead += inb; mCurrRead += inb; mAvgReadCount += inb; - + PQInterface::traf_in += inb; return; } diff --git a/libretroshare/src/retroshare/rsconfig.h b/libretroshare/src/retroshare/rsconfig.h index c1b3499b5..88177df2d 100644 --- a/libretroshare/src/retroshare/rsconfig.h +++ b/libretroshare/src/retroshare/rsconfig.h @@ -409,6 +409,7 @@ public: * @return returns 1 on succes and 0 otherwise */ virtual int GetCurrentDataRates( float &inKb, float &outKb ) = 0; + virtual int GetTrafficSum( uint64_t &inb, uint64_t &outb ) = 0; }; #endif diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index 4d28cad22..9ff20cd11 100644 --- a/libretroshare/src/retroshare/rsgxschannels.h +++ b/libretroshare/src/retroshare/rsgxschannels.h @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012 Robert Fernie * - * Copyright (C) 2018 Gioacchino Mazzurco * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -33,6 +33,8 @@ #include "serialiser/rsserializable.h" #include "retroshare/rsturtle.h" #include "util/rsdeprecate.h" +#include "retroshare/rsgxscircles.h" +#include "util/rsmemory.h" class RsGxsChannels; @@ -102,34 +104,114 @@ public: /** * @brief Create channel. Blocking API. * @jsonapi{development} - * @param[inout] channel Channel data (name, description...) - * @return false on error, true otherwise + * @param[in] name Name of the channel + * @param[in] description Description of the channel + * @param[in] thumbnail Optional image to show as channel thumbnail. + * @param[in] authorId Optional id of the author. Leave empty for an + * anonymous channel. + * @param[in] circleType Optional visibility rule, default public. + * @param[in] circleId If the channel is not public specify the id of + * the circle who can see the channel. Depending on + * the value you pass for + * circleType this should be be an external circle + * if EXTERNAL is passed, a local friend group id + * if NODES_GROUP is passed, empty otherwise. + * @param[out] channelId Optional storage for the id of the created + * channel, meaningful only if creations succeeds. + * @param[out] errorMessage Optional storage for error messsage, meaningful + * only if creation fail. + * @return False on error, true otherwise. */ - virtual bool createChannel(RsGxsChannelGroup& channel) = 0; + virtual bool createChannelV2( + const std::string& name, + const std::string& description, + const RsGxsImage& thumbnail = RsGxsImage(), + const RsGxsId& authorId = RsGxsId(), + RsGxsCircleType circleType = RsGxsCircleType::PUBLIC, + const RsGxsCircleId& circleId = RsGxsCircleId(), + RsGxsGroupId& channelId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) = 0; /** - * @brief Add a comment on a post or on another comment + * @brief Add a comment on a post or on another comment. Blocking API. * @jsonapi{development} - * @param[inout] comment + * @param[in] channelId Id of the channel in which the comment is to be + * posted + * @param[in] threadId Id of the post (that is a thread) in the channel + * where the comment is placed + * @param[in] parentId Id of the parent of the comment that is either a + * channel post Id or the Id of another comment. + * @param[in] authorId Id of the author of the comment + * @param[in] comment UTF-8 string containing the comment itself + * @param[out] commentMessageId Optional storage for the id of the comment + * that was created, meaningful only on success. + * @param[out] errorMessage Optional storage for error message, meaningful + * only on failure. * @return false on error, true otherwise */ - virtual bool createComment(RsGxsComment& comment) = 0; + virtual bool createCommentV2( + const RsGxsGroupId& channelId, + const RsGxsMessageId& threadId, + const RsGxsMessageId& parentId, + const RsGxsId& authorId, + const std::string& comment, + RsGxsMessageId& commentMessageId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) = 0; /** * @brief Create channel post. Blocking API. * @jsonapi{development} - * @param[inout] post + * @param[in] channelId Id of the channel where to put the post. Beware + * you need publish rights on that channel to post. + * @param[in] title Title of the post + * @param[in] mBody Text content of the post + * @param[in] files Optional list of attached files. These are + * supposed to be already shared, + * @see ExtraFileHash() below otherwise. + * @param[in] thumbnail Optional thumbnail image for the post. + * @param[in] origPostId If this is supposed to replace an already + * existent post, the id of the old post. If left + * blank a new post will be created. + * @param[out] postId Optional storage for the id of the created post, + * meaningful only on success. + * @param[out] errorMessage Optional storage for the error message, + * meaningful only on failure. * @return false on error, true otherwise */ - virtual bool createPost(RsGxsChannelPost& post) = 0; + virtual bool createPostV2( + const RsGxsGroupId& channelId, const std::string& title, + const std::string& mBody, + const std::list& files = std::list(), + const RsGxsImage& thumbnail = RsGxsImage(), + const RsGxsMessageId& origPostId = RsGxsMessageId(), + RsGxsMessageId& postId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; /** - * @brief createVote + * @brief Create a vote * @jsonapi{development} - * @param[inout] vote + * @param[in] channelId Id of the channel where to vote + * @param[in] postId Id of the channel post of which a comment is + * voted. + * @param[in] commentId Id of the comment that is voted + * @param[in] authorId Id of the author. Needs to be of an owned + * identity. + * @param[in] vote Vote value, either RsGxsVoteType::DOWN or + * RsGxsVoteType::UP + * @param[out] voteId Optional storage for the id of the created vote, + * meaningful only on success. + * @param[out] errorMessage Optional storage for error message, meaningful + * only on failure. * @return false on error, true otherwise */ - virtual bool createVote(RsGxsVote& vote) = 0; + virtual bool createVoteV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& postId, + const RsGxsMessageId& commentId, const RsGxsId& authorId, + RsGxsVoteType vote, + RsGxsMessageId& voteId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; /** * @brief Edit channel details. @@ -315,6 +397,16 @@ public: /* Following functions are deprecated as they expose internal functioning * semantic, instead of a safe to use API */ + /** + * @brief Create channel. Blocking API. + * @jsonapi{development} + * @deprecated { substituted by createChannelV2 } + * @param[inout] channel Channel data (name, description...) + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createChannelV2) + virtual bool createChannel(RsGxsChannelGroup& channel) = 0; + RS_DEPRECATED_FOR(getChannelsInfo) virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; @@ -372,9 +464,29 @@ public: * @param[in] group Channel data (name, description...) * @return false on error, true otherwise */ - RS_DEPRECATED_FOR(createChannel) + RS_DEPRECATED_FOR(createChannelV2) virtual bool createGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; + /** + * @brief Add a comment on a post or on another comment + * @jsonapi{development} + * @deprecated + * @param[inout] comment + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createCommentV2) + virtual bool createComment(RsGxsComment& comment) = 0; + + /** + * @brief Create channel post. Blocking API. + * @jsonapi{development} + * @deprecated + * @param[inout] post + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createPostV2) + virtual bool createPost(RsGxsChannelPost& post) = 0; + /** * @brief Request post creation. * The action is performed asyncronously, so it could fail in a subsequent @@ -385,9 +497,19 @@ public: * @param[in] post * @return false on error, true otherwise */ - RS_DEPRECATED + RS_DEPRECATED_FOR(createPostV2) virtual bool createPost(uint32_t& token, RsGxsChannelPost& post) = 0; + /** + * @brief createVote + * @jsonapi{development} + * @deprecated + * @param[inout] vote + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createVoteV2) + virtual bool createVote(RsGxsVote& vote) = 0; + /** * @brief Request channel change. * The action is performed asyncronously, so it could fail in a subsequent diff --git a/libretroshare/src/retroshare/rsgxscircles.h b/libretroshare/src/retroshare/rsgxscircles.h index d5f3543d0..83f8e840c 100644 --- a/libretroshare/src/retroshare/rsgxscircles.h +++ b/libretroshare/src/retroshare/rsgxscircles.h @@ -42,24 +42,35 @@ class RsGxsCircles; */ extern RsGxsCircles* rsGxsCircles; +enum class RsGxsCircleType : uint32_t // 32 bit overkill, just for retrocompat +{ + UNKNOWN = 0, /// Used to detect uninizialized values. + PUBLIC = 1, /// Public distribution + EXTERNAL = 2, /// Restricted to an external circle -// TODO: convert to enum -/// The meaning of the different circle types is: -static const uint32_t GXS_CIRCLE_TYPE_UNKNOWN = 0x0000 ; /// Used to detect uninizialized values. -static const uint32_t GXS_CIRCLE_TYPE_PUBLIC = 0x0001 ; // not restricted to a circle -static const uint32_t GXS_CIRCLE_TYPE_EXTERNAL = 0x0002 ; // restricted to an external circle, made of RsGxsId -static const uint32_t GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY = 0x0003 ; // restricted to a subset of friend nodes of a given RS node given by a RsPgpId list -static const uint32_t GXS_CIRCLE_TYPE_LOCAL = 0x0004 ; // not distributed at all -static const uint32_t GXS_CIRCLE_TYPE_EXT_SELF = 0x0005 ; // self-restricted. Not used, except at creation time when the circle ID isn't known yet. Set to EXTERNAL afterwards. -static const uint32_t GXS_CIRCLE_TYPE_YOUR_EYES_ONLY = 0x0006 ; // distributed to nodes signed by your own PGP key only. + /** Restricted to a group of friend nodes, the administrator of the circle + * behave as a hub for them */ + NODES_GROUP = 3, + LOCAL = 4, /// not distributed at all + + /** Self-restricted. Used only at creation time of self-restricted circles + * when the circle id isn't known yet. Once the circle id is known the type + * is set to EXTERNAL, and the external circle id is set to the id of the + * circle itself. + */ + EXT_SELF = 5, + + /// distributed to nodes signed by your own PGP key only. + YOUR_EYES_ONLY = 6 +}; + +// TODO: convert to enum class static const uint32_t GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST = 0x0001 ;// user is validated by circle admin static const uint32_t GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED = 0x0002 ;// user has subscribed the group static const uint32_t GXS_EXTERNAL_CIRCLE_FLAGS_KEY_AVAILABLE = 0x0004 ;// key is available, so we can encrypt for this circle static const uint32_t GXS_EXTERNAL_CIRCLE_FLAGS_ALLOWED = 0x0007 ;// user is allowed. Combines all flags above. -static const uint32_t GXS_CIRCLE_FLAGS_IS_EXTERNAL = 0x0008 ;// user is allowed - struct RsGxsCircleGroup : RsSerializable { @@ -110,8 +121,9 @@ struct RsGxsCircleMsg : RsSerializable struct RsGxsCircleDetails : RsSerializable { RsGxsCircleDetails() : - mCircleType(GXS_CIRCLE_TYPE_EXTERNAL), mAmIAllowed(false) {} - ~RsGxsCircleDetails() {} + mCircleType(static_cast(RsGxsCircleType::EXTERNAL)), + mAmIAllowed(false) {} + ~RsGxsCircleDetails() override {} RsGxsCircleId mCircleId; std::string mCircleName; @@ -264,3 +276,34 @@ public: RS_DEPRECATED_FOR("editCircle, inviteIdsToCircle") virtual void updateGroup(uint32_t &token, RsGxsCircleGroup &group) = 0; }; + + +/// @deprecated Used to detect uninizialized values. +RS_DEPRECATED_FOR("RsGxsCircleType::UNKNOWN") +static const uint32_t GXS_CIRCLE_TYPE_UNKNOWN = 0x0000; + +/// @deprecated not restricted to a circle +RS_DEPRECATED_FOR("RsGxsCircleType::PUBLIC") +static const uint32_t GXS_CIRCLE_TYPE_PUBLIC = 0x0001; + +/// @deprecated restricted to an external circle, made of RsGxsId +RS_DEPRECATED_FOR("RsGxsCircleType::EXTERNAL") +static const uint32_t GXS_CIRCLE_TYPE_EXTERNAL = 0x0002; + +/// @deprecated restricted to a subset of friend nodes of a given RS node given +/// by a RsPgpId list +RS_DEPRECATED_FOR("RsGxsCircleType::NODES_GROUP") +static const uint32_t GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY = 0x0003; + +/// @deprecated not distributed at all +RS_DEPRECATED_FOR("RsGxsCircleType::LOCAL") +static const uint32_t GXS_CIRCLE_TYPE_LOCAL = 0x0004; + +/// @deprecated self-restricted. Not used, except at creation time when the +/// circle ID isn't known yet. Set to EXTERNAL afterwards. +RS_DEPRECATED_FOR("RsGxsCircleType::EXT_SELF") +static const uint32_t GXS_CIRCLE_TYPE_EXT_SELF = 0x0005; + +/// @deprecated distributed to nodes signed by your own PGP key only. +RS_DEPRECATED_FOR("RsGxsCircleType::YOUR_EYES_ONLY") +static const uint32_t GXS_CIRCLE_TYPE_YOUR_EYES_ONLY = 0x0006; diff --git a/libretroshare/src/retroshare/rsgxscommon.h b/libretroshare/src/retroshare/rsgxscommon.h index c8b8ec7a4..aded69f83 100644 --- a/libretroshare/src/retroshare/rsgxscommon.h +++ b/libretroshare/src/retroshare/rsgxscommon.h @@ -19,11 +19,9 @@ * along with this program. If not, see . * * * *******************************************************************************/ +#pragma once -#ifndef RETROSHARE_GXS_COMMON_OBJS_INTERFACE_H -#define RETROSHARE_GXS_COMMON_OBJS_INTERFACE_H - -#include +#include #include #include @@ -79,10 +77,12 @@ struct RsGxsImage : RsSerializable } }; - -#define GXS_VOTE_NONE 0x0000 -#define GXS_VOTE_DOWN 0x0001 -#define GXS_VOTE_UP 0x0002 +enum class RsGxsVoteType : uint32_t +{ + NONE = 0, /// Used to detect unset vote? + DOWN = 1, /// Negative vote + UP = 2 /// Positive vote +}; // Status Flags to indicate Voting.... @@ -181,7 +181,11 @@ struct RsGxsCommentService std::pair& msgId ) = 0; }; +/// @deprecated use RsGxsVoteType::NONE instead @see RsGxsVoteType +#define GXS_VOTE_NONE 0x0000 +/// @deprecated use RsGxsVoteType::DOWN instead @see RsGxsVoteType +#define GXS_VOTE_DOWN 0x0001 -#endif - +/// @deprecated use RsGxsVoteType::UP instead @see RsGxsVoteType +#define GXS_VOTE_UP 0x0002 diff --git a/libretroshare/src/retroshare/rsgxsflags.h b/libretroshare/src/retroshare/rsgxsflags.h index 7a737c74c..ff6f164c2 100644 --- a/libretroshare/src/retroshare/rsgxsflags.h +++ b/libretroshare/src/retroshare/rsgxsflags.h @@ -52,14 +52,15 @@ namespace GXS_SERV { static const uint32_t FLAG_AUTHOR_AUTHENTICATION_MASK = 0x0000ff00; static const uint32_t FLAG_AUTHOR_AUTHENTICATION_NONE = 0x00000000; static const uint32_t FLAG_AUTHOR_AUTHENTICATION_GPG = 0x00000100; // Anti-spam feature. Allows to ask higher reputation to anonymous IDs - static const uint32_t FLAG_AUTHOR_AUTHENTICATION_REQUIRED = 0x00000200; - static const uint32_t FLAG_AUTHOR_AUTHENTICATION_IFNOPUBSIGN = 0x00000400; + static const uint32_t FLAG_AUTHOR_AUTHENTICATION_REQUIRED = 0x00000200; // unused + static const uint32_t FLAG_AUTHOR_AUTHENTICATION_IFNOPUBSIGN = 0x00000400; // ??? static const uint32_t FLAG_AUTHOR_AUTHENTICATION_TRACK_MESSAGES = 0x00000800; // not used anymore static const uint32_t FLAG_AUTHOR_AUTHENTICATION_GPG_KNOWN = 0x00001000; // Anti-spam feature. Allows to ask higher reputation to unknown IDs and anonymous IDs + // These are *not used* static const uint32_t FLAG_GROUP_SIGN_PUBLISH_MASK = 0x000000ff; static const uint32_t FLAG_GROUP_SIGN_PUBLISH_ENCRYPTED = 0x00000001; - static const uint32_t FLAG_GROUP_SIGN_PUBLISH_ALLSIGNED = 0x00000002; + static const uint32_t FLAG_GROUP_SIGN_PUBLISH_ALLSIGNED = 0x00000002; // unused static const uint32_t FLAG_GROUP_SIGN_PUBLISH_THREADHEAD = 0x00000004; static const uint32_t FLAG_GROUP_SIGN_PUBLISH_NONEREQ = 0x00000008; diff --git a/libretroshare/src/retroshare/rsgxsifacehelper.h b/libretroshare/src/retroshare/rsgxsifacehelper.h index 09cf40dbf..fce16d8f7 100644 --- a/libretroshare/src/retroshare/rsgxsifacehelper.h +++ b/libretroshare/src/retroshare/rsgxsifacehelper.h @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright 2011 by Christopher Evi-Parker * - * Copyright (C) 2018 Gioacchino Mazzurco * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,9 +20,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ - -#ifndef RSGXSIFACEIMPL_H -#define RSGXSIFACEIMPL_H +#pragma once #include #include @@ -292,19 +290,51 @@ protected: * Useful for blocking API implementation. * @param[in] token token associated to the request caller is waiting for * @param[in] maxWait maximum waiting time in milliseconds + * @param[in] checkEvery time in millisecond between status checks */ RsTokenService::GxsRequestStatus waitToken( uint32_t token, - std::chrono::milliseconds maxWait = std::chrono::milliseconds(500) ) + std::chrono::milliseconds maxWait = std::chrono::milliseconds(500), + std::chrono::milliseconds checkEvery = std::chrono::milliseconds(2)) { +#if defined(__ANDROID__) && (__ANDROID_API__ < 24) + auto wkStartime = std::chrono::steady_clock::now(); + int maxWorkAroundCnt = 10; +LLwaitTokenBeginLabel: +#endif auto timeout = std::chrono::steady_clock::now() + maxWait; auto st = requestStatus(token); while( !(st == RsTokenService::FAILED || st >= RsTokenService::COMPLETE) && std::chrono::steady_clock::now() < timeout ) { - std::this_thread::sleep_for(std::chrono::milliseconds(2)); + std::this_thread::sleep_for(checkEvery); st = requestStatus(token); } + +#if defined(__ANDROID__) && (__ANDROID_API__ < 24) + /* Work around for very slow/old android devices, we don't expect this + * to be necessary on newer devices. If it take unreasonably long + * something worser is already happening elsewere and we return anyway. + */ + if( st > RsTokenService::FAILED && st < RsTokenService::COMPLETE + && maxWorkAroundCnt-- > 0 ) + { + maxWait *= 10; + checkEvery *= 3; + std::cerr << __PRETTY_FUNCTION__ << " Slow Android device " + << " workaround st: " << st + << " maxWorkAroundCnt: " << maxWorkAroundCnt + << " maxWait: " << maxWait.count() + << " checkEvery: " << checkEvery.count() << std::endl; + goto LLwaitTokenBeginLabel; + } + std::cerr << __PRETTY_FUNCTION__ << " lasted: " + << std::chrono::duration_cast( + std::chrono::steady_clock::now() - wkStartime ).count() + << "ms" << std::endl; + +#endif + return st; } @@ -312,5 +342,3 @@ private: RsGxsIface& mGxs; RsTokenService& mTokenService; }; - -#endif // RSGXSIFACEIMPL_H diff --git a/libretroshare/src/retroshare/rsmsgs.h b/libretroshare/src/retroshare/rsmsgs.h index 6658a53bc..f1e42c4c3 100644 --- a/libretroshare/src/retroshare/rsmsgs.h +++ b/libretroshare/src/retroshare/rsmsgs.h @@ -563,8 +563,8 @@ public: /** * @brief MessageToTrash * @jsonapi{development} - * @param[in] msgId - * @param[in] bTrash + * @param[in] msgId Id of the message to mode to trash box + * @param[in] bTrash Move to trash if true, otherwise remove from trash * @return true on success */ virtual bool MessageToTrash(const std::string &msgId, bool bTrash) = 0; diff --git a/libretroshare/src/retroshare/rsposted.h b/libretroshare/src/retroshare/rsposted.h index 02edb47c8..a42f06c40 100644 --- a/libretroshare/src/retroshare/rsposted.h +++ b/libretroshare/src/retroshare/rsposted.h @@ -44,6 +44,7 @@ class RsPostedGroup RsGroupMetaData mMeta; std::string mDescription; + RsGxsImage mGroupImage; }; diff --git a/libretroshare/src/retroshare/rstokenservice.h b/libretroshare/src/retroshare/rstokenservice.h index 58314c62f..515579a07 100644 --- a/libretroshare/src/retroshare/rstokenservice.h +++ b/libretroshare/src/retroshare/rstokenservice.h @@ -121,15 +121,14 @@ public: enum GxsRequestStatus : uint8_t { - FAILED, - PENDING, - PARTIAL, - COMPLETE, - DONE, /// Once all data has been retrived - CANCELLED + FAILED = 0, + PENDING = 1, + PARTIAL = 2, + COMPLETE = 3, + DONE = 4, /// Once all data has been retrived + CANCELLED = 5 }; - RsTokenService() {} virtual ~RsTokenService() {} diff --git a/libretroshare/src/rsitems/itempriorities.h b/libretroshare/src/rsitems/itempriorities.h index dc346b7c3..b7f29babc 100644 --- a/libretroshare/src/rsitems/itempriorities.h +++ b/libretroshare/src/rsitems/itempriorities.h @@ -31,21 +31,22 @@ const uint8_t QOS_PRIORITY_TOP = 9 ; // Turtle traffic // -const uint8_t QOS_PRIORITY_RS_TURTLE_OPEN_TUNNEL = 6 ; -const uint8_t QOS_PRIORITY_RS_TURTLE_TUNNEL_OK = 6 ; +const uint8_t QOS_PRIORITY_RS_TURTLE_OPEN_TUNNEL = 6 ; +const uint8_t QOS_PRIORITY_RS_TURTLE_TUNNEL_OK = 6 ; const uint8_t QOS_PRIORITY_RS_TURTLE_SEARCH_REQUEST = 6 ; -const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_REQUEST = 5 ; -const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_CRC_REQUEST = 5 ; -const uint8_t QOS_PRIORITY_RS_TURTLE_CHUNK_CRC_REQUEST= 5 ; +const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_REQUEST = 5 ; +const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_CRC_REQUEST = 5 ; +const uint8_t QOS_PRIORITY_RS_TURTLE_CHUNK_CRC_REQUEST= 5 ; const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_MAP_REQUEST = 5 ; -const uint8_t QOS_PRIORITY_RS_TURTLE_SEARCH_RESULT = 3 ; -const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_DATA = 3 ; +const uint8_t QOS_PRIORITY_RS_TURTLE_SEARCH_RESULT = 3 ; +const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_DATA = 3 ; const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_CRC = 3 ; const uint8_t QOS_PRIORITY_RS_TURTLE_CHUNK_CRC = 5 ; const uint8_t QOS_PRIORITY_RS_TURTLE_FILE_MAP = 3 ; const uint8_t QOS_PRIORITY_RS_TURTLE_GENERIC_ITEM = 3 ; const uint8_t QOS_PRIORITY_RS_TURTLE_FORWARD_FILE_DATA= 3 ; const uint8_t QOS_PRIORITY_RS_TURTLE_GENERIC_DATA = 5 ; +const uint8_t QOS_PRIORITY_RS_TURTLE_GENERIC_FAST_DATA= 7 ; // File transfer // diff --git a/libretroshare/src/rsitems/rsposteditems.cc b/libretroshare/src/rsitems/rsposteditems.cc index 7f7adac4d..23fb6633b 100644 --- a/libretroshare/src/rsitems/rsposteditems.cc +++ b/libretroshare/src/rsitems/rsposteditems.cc @@ -44,7 +44,15 @@ void RsGxsPostedPostItem::serial_process(RsGenericSerializer::SerializeJob j,RsG void RsGxsPostedGroupItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR ,mGroup.mDescription,"mGroup.mDescription") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR ,mDescription,"mDescription") ; + + if(j == RsGenericSerializer::DESERIALIZE && ctx.mOffset == ctx.mSize) + return ; + + if((j == RsGenericSerializer::SIZE_ESTIMATE || j == RsGenericSerializer::SERIALIZE) && mGroupImage.empty()) + return ; + + RsTypeSerializer::serial_process(j,ctx,mGroupImage,"mGroupImage") ; } RsItem *RsGxsPostedSerialiser::create_item(uint16_t service_id,uint8_t item_subtype) const @@ -109,6 +117,42 @@ void RsGxsPostedPostItem::clear() } void RsGxsPostedGroupItem::clear() { - mGroup.mDescription.clear(); + mDescription.clear(); + mGroupImage.TlvClear(); } +bool RsGxsPostedGroupItem::fromPostedGroup(RsPostedGroup &group, bool moveImage) +{ + clear(); + meta = group.mMeta; + mDescription = group.mDescription; + + if (moveImage) + { + mGroupImage.binData.bin_data = group.mGroupImage.mData; + mGroupImage.binData.bin_len = group.mGroupImage.mSize; + group.mGroupImage.shallowClear(); + } + else + { + mGroupImage.binData.setBinData(group.mGroupImage.mData, group.mGroupImage.mSize); + } + return true; +} + +bool RsGxsPostedGroupItem::toPostedGroup(RsPostedGroup &group, bool moveImage) +{ + group.mMeta = meta; + group.mDescription = mDescription; + if (moveImage) + { + group.mGroupImage.take((uint8_t *) mGroupImage.binData.bin_data, mGroupImage.binData.bin_len); + // mGroupImage doesn't have a ShallowClear at the moment! + mGroupImage.binData.TlvShallowClear(); + } + else + { + group.mGroupImage.copy((uint8_t *) mGroupImage.binData.bin_data, mGroupImage.binData.bin_len); + } + return true; +} diff --git a/libretroshare/src/rsitems/rsposteditems.h b/libretroshare/src/rsitems/rsposteditems.h index ef88289b5..910471c19 100644 --- a/libretroshare/src/rsitems/rsposteditems.h +++ b/libretroshare/src/rsitems/rsposteditems.h @@ -42,8 +42,12 @@ public: virtual void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx); - RsPostedGroup mGroup; + // use conversion functions to transform: + bool fromPostedGroup(RsPostedGroup &group, bool moveImage); + bool toPostedGroup(RsPostedGroup &group, bool moveImage); + std::string mDescription; + RsTlvImage mGroupImage; }; diff --git a/libretroshare/src/rsserver/p3serverconfig.cc b/libretroshare/src/rsserver/p3serverconfig.cc index 21d43cbae..4e22c3a4d 100644 --- a/libretroshare/src/rsserver/p3serverconfig.cc +++ b/libretroshare/src/rsserver/p3serverconfig.cc @@ -560,4 +560,8 @@ int p3ServerConfig::GetCurrentDataRates( float &inKb, float &outKb ) return 1; } - +int p3ServerConfig::GetTrafficSum(uint64_t &inb, uint64_t &outb ) +{ + mPqiHandler->GetTraffic(inb, outb); + return 1; +} diff --git a/libretroshare/src/rsserver/p3serverconfig.h b/libretroshare/src/rsserver/p3serverconfig.h index 1a806d713..bcc0e587e 100644 --- a/libretroshare/src/rsserver/p3serverconfig.h +++ b/libretroshare/src/rsserver/p3serverconfig.h @@ -91,6 +91,7 @@ virtual bool setOperatingMode(const std::string &opModeStr); virtual int SetMaxDataRates( int downKb, int upKb ); virtual int GetMaxDataRates( int &downKb, int &upKb ); virtual int GetCurrentDataRates( float &inKb, float &outKb ); +virtual int GetTrafficSum( uint64_t &inb, uint64_t &outb ); /********************* ABOVE is RsConfig Interface *******/ diff --git a/libretroshare/src/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index 3f34b6438..367023cad 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012 Robert Fernie * - * Copyright (C) 2018 Gioacchino Mazzurco * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -31,12 +31,14 @@ #include "retroshare/rsgxsflags.h" #include "retroshare/rsfiles.h" +#include "retroshare/rspeers.h" #include "rsserver/p3face.h" #include "retroshare/rsnotify.h" #include #include +#include // For Dummy Msgs. #include "util/rsrandom.h" @@ -1055,26 +1057,145 @@ bool p3GxsChannels::getChannelContent( const RsGxsGroupId& channelId, return getPostData(token, posts, comments); } -bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) +bool p3GxsChannels::createChannelV2( + const std::string& name, const std::string& description, + const RsGxsImage& thumbnail, const RsGxsId& authorId, + RsGxsCircleType circleType, const RsGxsCircleId& circleId, + RsGxsGroupId& channelId, std::string& errorMessage ) { + // do some checks + + if( circleType != RsGxsCircleType::PUBLIC + && circleType != RsGxsCircleType::EXTERNAL + && circleType != RsGxsCircleType::NODES_GROUP + && circleType != RsGxsCircleType::LOCAL + && circleType != RsGxsCircleType::YOUR_EYES_ONLY) + { + errorMessage = "circleType has invalid value"; + return false; + } + + switch(circleType) + { + case RsGxsCircleType::EXTERNAL: + if(circleId.isNull()) + { + errorMessage = "circleType is EXTERNAL but circleId is null"; + return false; + } + break; + + case RsGxsCircleType::NODES_GROUP: + { + RsGroupInfo ginfo; + + if(!rsPeers->getGroupInfo(RsNodeGroupId(circleId), ginfo)) + { + errorMessage = "circleType is NODES_GROUP but circleId does not " + "correspond to an actual group of friends"; + return false; + } + break; + } + default: + if(!circleId.isNull()) + { + errorMessage = "circleType requires a null circleId, but a non " + "null circleId ("; + errorMessage += circleId.toStdString(); + errorMessage += ") was supplied"; + return false; + } + break; + } + + // Create a consistent channel group meta from the information supplied + RsGxsChannelGroup channel; + + channel.mMeta.mGroupName = name; + channel.mMeta.mAuthorId = authorId; + channel.mMeta.mCircleType = static_cast(circleType); + + channel.mMeta.mSignFlags = GXS_SERV::FLAG_GROUP_SIGN_PUBLISH_NONEREQ + | GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_REQUIRED; + + channel.mMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC; + + channel.mMeta.mCircleId.clear(); + channel.mMeta.mInternalCircle.clear(); + + switch(circleType) + { + case RsGxsCircleType::NODES_GROUP: + channel.mMeta.mInternalCircle = circleId; break; + case RsGxsCircleType::EXTERNAL: + channel.mMeta.mCircleId = circleId; break; + default: break; + } + + // Create the channel + channel.mDescription = description; + channel.mImage = thumbnail; + uint32_t token; if(!createGroup(token, channel)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating group." + errorMessage = "Failed creating GXS group."; + std::cerr << __PRETTY_FUNCTION__ << " Error! " << errorMessage << std::endl; return false; } - if(waitToken(token) != RsTokenService::COMPLETE) + // wait for the group creation to complete. + RsTokenService::GxsRequestStatus wSt = + waitToken( token, std::chrono::milliseconds(5000), + std::chrono::milliseconds(20) ); + if(wSt != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." + errorMessage = "GXS operation waitToken failed with: " + + std::to_string(wSt); + std::cerr << __PRETTY_FUNCTION__ << " Error! " << errorMessage << std::endl; return false; } if(!RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting updated " + errorMessage = "Failure getting updated group data."; + std::cerr << __PRETTY_FUNCTION__ << " Error! " << errorMessage + << std::endl; + return false; + } + + channelId = channel.mMeta.mGroupId; + +#ifdef RS_DEEP_SEARCH + DeepSearch::indexChannelGroup(channel); +#endif // RS_DEEP_SEARCH + + return true; +} + +bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) +{ + uint32_t token; + if(!createGroup(token, channel)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed creating group." + << std::endl; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." + << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting updated " << " group data." << std::endl; return false; } @@ -1086,53 +1207,121 @@ bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) return true; } -bool p3GxsChannels::createComment(RsGxsComment& comment) +bool p3GxsChannels::createVoteV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& postId, + const RsGxsMessageId& commentId, const RsGxsId& authorId, + RsGxsVoteType tVote, RsGxsMessageId& voteId, std::string& errorMessage ) { - uint32_t token; - if(!createNewComment(token, comment)) + std::vector channelsInfo; + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating comment." - << std::endl; + errorMessage = "Channel with Id " + channelId.toStdString() + + " does not exist."; + return false; + } + + if(commentId.isNull()) + { + errorMessage = "You cannot vote on null comment " + + commentId.toStdString(); + return false; + } + + std::set s({commentId}); + std::vector posts; + std::vector comments; + + if(!getChannelContent(channelId, s, posts, comments)) + { + errorMessage = "You cannot vote on comment " + + commentId.toStdString() + " of channel with Id " + + channelId.toStdString() + + ": this comment does not exists locally!"; + return false; + } + + // is the ID a comment ID or a post ID? + // It should be comment => should have a parent ID + if(posts.front().mMeta.mParentId.isNull()) + { + errorMessage = "You cannot vote on channel message " + + commentId.toStdString() + " of channel with Id " + + channelId.toStdString() + + ": given id refers to a post, not a comment!"; + return false; + } + + if( tVote != RsGxsVoteType::NONE + && tVote != RsGxsVoteType::UP + && tVote != RsGxsVoteType::DOWN ) + { + errorMessage = "Your vote to channel with Id " + + channelId.toStdString() + " has wrong vote type. " + + " Only RsGxsVoteType::NONE, RsGxsVoteType::UP, " + + "RsGxsVoteType::DOWN are accepted."; + return false; + } + + if(!rsIdentity->isOwnId(authorId)) + { + errorMessage = "You cannot vote to channel with Id " + + channelId.toStdString() + " with identity " + + authorId.toStdString() + " because it is not yours."; + return false; + } + + // Create the vote + RsGxsVote vote; + vote.mMeta.mGroupId = channelId; + vote.mMeta.mThreadId = postId; + vote.mMeta.mParentId = commentId; + vote.mMeta.mAuthorId = authorId; + vote.mVoteType = static_cast(tVote); + + uint32_t token; + if(!createNewVote(token, vote)) + { + errorMessage = "Error! Failed creating vote."; return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." - << std::endl; + errorMessage = "GXS operation failed."; return false; } - if(!RsGenExchange::getPublishedMsgMeta(token, comment.mMeta)) + if(!RsGenExchange::getPublishedMsgMeta(token, vote.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting generated " - << " comment data." << std::endl; + errorMessage = "Failure getting generated vote data."; return false; } + voteId = vote.mMeta.mMsgId; return true; } +/// @deprecated use createVoteV2 instead bool p3GxsChannels::createVote(RsGxsVote& vote) { uint32_t token; if(!createNewVote(token, vote)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating vote." + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed creating vote." << std::endl; return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." << std::endl; return false; } if(!RsGenExchange::getPublishedMsgMeta(token, vote.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting generated " + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting generated " << " vote data." << std::endl; return false; } @@ -1145,21 +1334,21 @@ bool p3GxsChannels::editChannel(RsGxsChannelGroup& channel) uint32_t token; if(!updateGroup(token, channel)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failed updating group." + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed updating group." << std::endl; return false; } if(waitToken(token) != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." << std::endl; return false; } if(!RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting updated " + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting updated " << " group data." << std::endl; return false; } @@ -1171,11 +1360,64 @@ bool p3GxsChannels::editChannel(RsGxsChannelGroup& channel) return true; } -bool p3GxsChannels::createPost(RsGxsChannelPost& post) +bool p3GxsChannels::createPostV2( + const RsGxsGroupId& channelId, const std::string& title, + const std::string& body, const std::list& files, + const RsGxsImage& thumbnail, const RsGxsMessageId& origPostId, + RsGxsMessageId& postId, std::string& errorMessage ) { + // Do some checks + + std::vector channelsInfo; + + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) + { + errorMessage = "Channel with Id " + channelId.toStdString() + + " does not exist."; + return false; + } + + const RsGxsChannelGroup& cg(*channelsInfo.begin()); + + if(!(cg.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH)) + { + errorMessage = "You cannot post to channel with Id " + + channelId.toStdString() + ": missing publish rights!"; + return false; + } + + if(!origPostId.isNull()) + { + std::set s({origPostId}); + std::vector posts; + std::vector comments; + + if(!getChannelContent(channelId,s,posts,comments)) + { + errorMessage = "You cannot edit post " + origPostId.toStdString() + + " of channel with Id " + channelId.toStdString() + + ": this post does not exist locally!"; + return false; + } + } + + // Create the post + RsGxsChannelPost post; + + post.mMeta.mGroupId = channelId; + post.mMeta.mOrigMsgId = origPostId; + post.mMeta.mMsgName = title; + + post.mMsg = body; + post.mFiles = files; + post.mThumbnail = thumbnail; + uint32_t token; - if( !createPost(token, post) - || waitToken(token) != RsTokenService::COMPLETE ) return false; + if(!createPost(token, post) || waitToken(token) != RsTokenService::COMPLETE) + { + errorMessage = "GXS operation failed"; + return false; + } if(RsGenExchange::getPublishedMsgMeta(token,post.mMeta)) { @@ -1183,12 +1425,150 @@ bool p3GxsChannels::createPost(RsGxsChannelPost& post) DeepSearch::indexChannelPost(post); #endif // RS_DEEP_SEARCH + postId = post.mMeta.mMsgId; return true; } + errorMessage = "Failed to retrive created post metadata"; return false; } +bool p3GxsChannels::createCommentV2(const RsGxsGroupId& channelId, + const RsGxsMessageId& threadId, + const RsGxsMessageId& parentId, + const RsGxsId& authorId, + const std::string& comment, + RsGxsMessageId& commentMessageId, + std::string& errorMessage) +{ + std::vector channelsInfo; + if(!getChannelsInfo(std::list({channelId}),channelsInfo)) + { + errorMessage = "Channel with Id " + channelId.toStdString() + + " does not exist."; + return false; + } + + std::vector posts; + std::vector comments; + + if(!getChannelContent( // does the post thread exist? + channelId,std::set({threadId}), posts, comments )) + { + errorMessage = "You cannot comment post " + threadId.toStdString() + + " of channel with Id " + channelId.toStdString() + + ": this post does not exists locally!"; + std::cerr << __PRETTY_FUNCTION__ << " Error: " << errorMessage + << std::endl; + return false; + } + + // check that the post thread Id is actually that of a post thread + if(posts.size() != 1 || !posts[0].mMeta.mParentId.isNull()) + { + errorMessage = "You cannot comment post " + threadId.toStdString() + + " of channel with Id " + channelId.toStdString() + + ": supplied threadId is not a thread, or parentMsgId is not a" + + " comment!"; + std::cerr << __PRETTY_FUNCTION__ << " Error: " << errorMessage + << std::endl; + return false; + } + + if(!parentId.isNull()) + if(!getChannelContent( // does the post thread exist? + channelId,std::set({parentId}),posts,comments )) + { + errorMessage = "You cannot comment post " + parentId.toStdString() + + ": supplied parent comment Id is not a comment!"; + std::cerr << __PRETTY_FUNCTION__ << " Error: " << errorMessage + << std::endl; + return false; + } + else if(comments.size() != 1 || comments[0].mMeta.mParentId.isNull()) + { // is the comment parent actually a comment? + errorMessage = "You cannot comment post " + parentId.toStdString() + + " of channel with Id " + channelId.toStdString() + + ": supplied mParentMsgId is not a comment Id!"; + std::cerr << __PRETTY_FUNCTION__ << " Error: " << errorMessage + << std::endl; + return false; + } + + if(!rsIdentity->isOwnId(authorId)) // is the voter ID actually ours? + { + errorMessage = "You cannot comment to channel with Id " + + channelId.toStdString() + " with identity " + + authorId.toStdString() + " because it is not yours."; + std::cerr << __PRETTY_FUNCTION__ << " Error: " << errorMessage + << std::endl; + return false; + } + + // Now create the comment + RsGxsComment cmt; + cmt.mMeta.mGroupId = channelId; + cmt.mMeta.mThreadId = threadId; + cmt.mMeta.mParentId = parentId; + cmt.mMeta.mAuthorId = authorId; + cmt.mComment = comment; + + uint32_t token; + if(!createNewComment(token, cmt)) + { + errorMessage = "Failed creating comment."; + std::cerr << __PRETTY_FUNCTION__ << " Error: " << errorMessage + << std::endl; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + errorMessage = "GXS operation failed."; + std::cerr << __PRETTY_FUNCTION__ << " Error: " << errorMessage + << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedMsgMeta(token, cmt.mMeta)) + { + errorMessage = "Failure getting created comment data."; + std::cerr << __PRETTY_FUNCTION__ << " Error: " << errorMessage + << std::endl; + return false; + } + + commentMessageId = cmt.mMeta.mMsgId; + return true; +} + +bool p3GxsChannels::createComment(RsGxsComment& comment) // deprecated +{ + uint32_t token; + if(!createNewComment(token, comment)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed creating comment." + << std::endl; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." + << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedMsgMeta(token, comment.mMeta)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting generated " + << " comment data." << std::endl; + return false; + } + + return true; +} + bool p3GxsChannels::subscribeToChannel( const RsGxsGroupId& groupId, bool subscribe ) { @@ -1428,13 +1808,31 @@ bool p3GxsChannels::updateGroup(uint32_t &token, RsGxsChannelGroup &group) return true; } +/// @deprecated use createPostV2 instead +bool p3GxsChannels::createPost(RsGxsChannelPost& post) +{ + uint32_t token; + if( !createPost(token, post) + || waitToken(token) != RsTokenService::COMPLETE ) return false; + + if(RsGenExchange::getPublishedMsgMeta(token,post.mMeta)) + { +#ifdef RS_DEEP_SEARCH + DeepSearch::indexChannelPost(post); +#endif // RS_DEEP_SEARCH + + return true; + } + + return false; +} bool p3GxsChannels::createPost(uint32_t &token, RsGxsChannelPost &msg) { #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::createChannelPost() GroupId: " << msg.mMeta.mGroupId; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " GroupId: " << msg.mMeta.mGroupId + << std::endl; #endif RsGxsChannelPostItem* msgItem = new RsGxsChannelPostItem(); @@ -1976,7 +2374,7 @@ bool p3GxsChannels::turtleChannelRequest( { if(channelId.isNull()) { - std::cerr << __PRETTY_FUNCTION__ << "Error! channelId can't be null!" + std::cerr << __PRETTY_FUNCTION__ << " Error! channelId can't be null!" << std::endl; return false; } diff --git a/libretroshare/src/services/p3gxschannels.h b/libretroshare/src/services/p3gxschannels.h index 7ac63f8e7..dffc73c32 100644 --- a/libretroshare/src/services/p3gxschannels.h +++ b/libretroshare/src/services/p3gxschannels.h @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012 Robert Fernie * - * Copyright (C) 2018 Gioacchino Mazzurco * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,14 +20,14 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef P3_GXSCHANNELS_SERVICE_HEADER -#define P3_GXSCHANNELS_SERVICE_HEADER +#pragma once #include "retroshare/rsgxschannels.h" #include "services/p3gxscommon.h" #include "gxs/rsgenexchange.h" #include "gxs/gxstokenqueue.h" +#include "util/rsmemory.h" #include "util/rstickevent.h" @@ -35,6 +35,7 @@ #include + class SSGxsChannelGroup { public: @@ -191,23 +192,64 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); std::vector& comments ); /// Implementation of @see RsGxsChannels::getContentSummaries - virtual bool getContentSummaries( const RsGxsGroupId& channelId, - std::vector& summaries ); + virtual bool getContentSummaries( + const RsGxsGroupId& channelId, + std::vector& summaries ) override; - /// Implementation of @see RsGxsChannels::createChannel - virtual bool createChannel(RsGxsChannelGroup& channel); + /// Implementation of @see RsGxsChannels::createChannelV2 + virtual bool createChannelV2( + const std::string& name, const std::string& description, + const RsGxsImage& thumbnail = RsGxsImage(), + const RsGxsId& authorId = RsGxsId(), + RsGxsCircleType circleType = RsGxsCircleType::PUBLIC, + const RsGxsCircleId& circleId = RsGxsCircleId(), + RsGxsGroupId& channelId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @deprecated Implementation of @see RsGxsChannels::createComment + RS_DEPRECATED_FOR(createCommentV2) + virtual bool createComment(RsGxsComment& comment) override; /// Implementation of @see RsGxsChannels::createComment - virtual bool createComment(RsGxsComment& comment); + virtual bool createCommentV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& threadId, + const RsGxsMessageId& parentId, const RsGxsId& authorId, + const std::string& comment, + RsGxsMessageId& commentMessageId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; /// Implementation of @see RsGxsChannels::editChannel - virtual bool editChannel(RsGxsChannelGroup& channel); + virtual bool editChannel(RsGxsChannelGroup& channel) override; - /// Implementation of @see RsGxsChannels::createPost - virtual bool createPost(RsGxsChannelPost& post); + /// @deprecated Implementation of @see RsGxsChannels::createPost + RS_DEPRECATED_FOR(createPostV2) + virtual bool createPost(RsGxsChannelPost& post) override; - /// Implementation of @see RsGxsChannels::createVote - virtual bool createVote(RsGxsVote& vote); + /// Implementation of @see RsGxsChannels::createPostV2 + bool createPostV2( + const RsGxsGroupId& channelId, const std::string& title, + const std::string& body, + const std::list& files = std::list(), + const RsGxsImage& thumbnail = RsGxsImage(), + const RsGxsMessageId& origPostId = RsGxsMessageId(), + RsGxsMessageId& postId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @deprecated Implementation of @see RsGxsChannels::createVote + RS_DEPRECATED_FOR(createVoteV2) + virtual bool createVote(RsGxsVote& vote) override; + + /// Implementation of @see RsGxsChannels::createVoteV2 + virtual bool createVoteV2( + const RsGxsGroupId& channelId, const RsGxsMessageId& postId, + const RsGxsMessageId& commentId, const RsGxsId& authorId, + RsGxsVoteType vote, + RsGxsMessageId& voteId = RS_DEFAULT_STORAGE_PARAM(RsGxsMessageId), + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; /// Implementation of @see RsGxsChannels::subscribeToChannel virtual bool subscribeToChannel( const RsGxsGroupId &groupId, @@ -219,6 +261,10 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); virtual bool shareChannelKeys( const RsGxsGroupId& channelId, const std::set& peers ); + /// Implementation of @see RsGxsChannels::createChannel + RS_DEPRECATED_FOR(createChannelV2) + virtual bool createChannel(RsGxsChannelGroup& channel) override; + protected: // Overloaded from GxsTokenQueue for Request callbacks. virtual void handleResponse(uint32_t token, uint32_t req_type); @@ -263,9 +309,11 @@ bool generateGroup(uint32_t &token, std::string groupName); class ChannelDummyRef { public: - ChannelDummyRef() { return; } - ChannelDummyRef(const RsGxsGroupId &grpId, const RsGxsMessageId &threadId, const RsGxsMessageId &msgId) - :mGroupId(grpId), mThreadId(threadId), mMsgId(msgId) { return; } + ChannelDummyRef() {} + ChannelDummyRef( + const RsGxsGroupId &grpId, const RsGxsMessageId &threadId, + const RsGxsMessageId &msgId ) : + mGroupId(grpId), mThreadId(threadId), mMsgId(msgId) {} RsGxsGroupId mGroupId; RsGxsMessageId mThreadId; @@ -309,5 +357,3 @@ bool generateGroup(uint32_t &token, std::string groupName); /// Cleanup mSearchCallbacksMap and mDistantChannelsCallbacksMap void cleanTimedOutCallbacks(); }; - -#endif diff --git a/libretroshare/src/services/p3posted.cc b/libretroshare/src/services/p3posted.cc index d50c0624c..1a1efe0b6 100644 --- a/libretroshare/src/services/p3posted.cc +++ b/libretroshare/src/services/p3posted.cc @@ -79,9 +79,8 @@ bool p3Posted::getGroupData(const uint32_t &token, std::vector &g RsGxsPostedGroupItem* item = dynamic_cast(*vit); if (item) { - RsPostedGroup grp = item->mGroup; - item->mGroup.mMeta = item->meta; - grp.mMeta = item->mGroup.mMeta; + RsPostedGroup grp; + item->toPostedGroup(grp, true); delete item; groups.push_back(grp); } @@ -265,8 +264,8 @@ bool p3Posted::createGroup(uint32_t &token, RsPostedGroup &group) std::cerr << "p3Posted::createGroup()" << std::endl; RsGxsPostedGroupItem* grpItem = new RsGxsPostedGroupItem(); - grpItem->mGroup = group; - grpItem->meta = group.mMeta; + grpItem->fromPostedGroup(group, true); + RsGenExchange::publishGroup(token, grpItem); return true; @@ -278,8 +277,8 @@ bool p3Posted::updateGroup(uint32_t &token, RsPostedGroup &group) std::cerr << "p3Posted::updateGroup()" << std::endl; RsGxsPostedGroupItem* grpItem = new RsGxsPostedGroupItem(); - grpItem->mGroup = group; - grpItem->meta = group.mMeta; + grpItem->fromPostedGroup(group, true); + RsGenExchange::updateGroup(token, grpItem); return true; diff --git a/libretroshare/src/turtle/p3turtle.cc b/libretroshare/src/turtle/p3turtle.cc index 3abc216dc..f1e96a8a4 100644 --- a/libretroshare/src/turtle/p3turtle.cc +++ b/libretroshare/src/turtle/p3turtle.cc @@ -77,18 +77,18 @@ void TS_dumpState() ; // - The total number of TR per second emmited from self will be MAX_TUNNEL_REQS_PER_SECOND / TIME_BETWEEN_TUNNEL_MANAGEMENT_CALLS = 0.5 // - I updated forward probabilities to higher values, and min them to 1/nb_connected_friends to prevent blocking tunnels. // -static const rstime_t TUNNEL_REQUESTS_LIFE_TIME = 240 ; /// life time for tunnel requests in the cache. -static const rstime_t SEARCH_REQUESTS_LIFE_TIME = 240 ; /// life time for search requests in the cache -static const rstime_t REGULAR_TUNNEL_DIGGING_TIME = 300 ; /// maximum interval between two tunnel digging campaigns. -static const rstime_t MAXIMUM_TUNNEL_IDLE_TIME = 60 ; /// maximum life time of an unused tunnel. -static const rstime_t EMPTY_TUNNELS_DIGGING_TIME = 50 ; /// look into tunnels regularly every 50 sec. -static const rstime_t TUNNEL_SPEED_ESTIMATE_LAPSE = 5 ; /// estimate tunnel speed every 5 seconds -static const rstime_t TUNNEL_CLEANING_LAPS_TIME = 10 ; /// clean tunnels every 10 secs -static const rstime_t TIME_BETWEEN_TUNNEL_MANAGEMENT_CALLS = 2 ; /// Tunnel management calls every 2 secs. -static const uint32_t MAX_TUNNEL_REQS_PER_SECOND = 1 ; /// maximum number of tunnel requests issued per second. Was 0.5 before -static const uint32_t MAX_ALLOWED_SR_IN_CACHE = 120 ; /// maximum number of search requests allowed in cache. That makes 2 per sec. -static const uint32_t TURTLE_SEARCH_RESULT_MAX_HITS_FILES =5000 ; /// maximum number of search results forwarded back to the source. -static const uint32_t TURTLE_SEARCH_RESULT_MAX_HITS_DEFAULT= 100 ; /// default maximum number of search results forwarded back source. +static const rstime_t TUNNEL_REQUESTS_LIFE_TIME = 240 ; /// life time for tunnel requests in the cache. +static const rstime_t SEARCH_REQUESTS_LIFE_TIME = 240 ; /// life time for search requests in the cache +static const rstime_t REGULAR_TUNNEL_DIGGING_TIME = 300 ; /// maximum interval between two tunnel digging campaigns. +static const rstime_t MAXIMUM_TUNNEL_IDLE_TIME = 60 ; /// maximum life time of an unused tunnel. +static const rstime_t EMPTY_TUNNELS_DIGGING_TIME = 50 ; /// look into tunnels regularly every 50 sec. +static const rstime_t TUNNEL_SPEED_ESTIMATE_LAPSE = 5 ; /// estimate tunnel speed every 5 seconds +static const rstime_t TUNNEL_CLEANING_LAPS_TIME = 10 ; /// clean tunnels every 10 secs +static const rstime_t TIME_BETWEEN_TUNNEL_MANAGEMENT_CALLS = 2 ; /// Tunnel management calls every 2 secs. +static const uint32_t MAX_TUNNEL_REQS_PER_SECOND = 1 ; /// maximum number of tunnel requests issued per second. Was 0.5 before +static const uint32_t MAX_ALLOWED_SR_IN_CACHE = 120 ; /// maximum number of search requests allowed in cache. That makes 2 per sec. +static const uint32_t TURTLE_SEARCH_RESULT_MAX_HITS_FILES =5000 ; /// maximum number of search results forwarded back to the source. +static const uint32_t TURTLE_SEARCH_RESULT_MAX_HITS_DEFAULT = 100 ; /// default maximum number of search results forwarded back source. static const float depth_peer_probability[7] = { 1.0f,0.99f,0.9f,0.7f,0.6f,0.5,0.4f } ; diff --git a/libretroshare/src/turtle/rsturtleitem.cc b/libretroshare/src/turtle/rsturtleitem.cc index be9a38ce2..f0128d04a 100644 --- a/libretroshare/src/turtle/rsturtleitem.cc +++ b/libretroshare/src/turtle/rsturtleitem.cc @@ -53,6 +53,7 @@ RsItem *RsTurtleSerialiser::create_item(uint16_t service,uint8_t item_subtype) c case RS_TURTLE_SUBTYPE_OPEN_TUNNEL : return new RsTurtleOpenTunnelItem(); case RS_TURTLE_SUBTYPE_TUNNEL_OK : return new RsTurtleTunnelOkItem(); case RS_TURTLE_SUBTYPE_GENERIC_DATA : return new RsTurtleGenericDataItem(); + case RS_TURTLE_SUBTYPE_GENERIC_FAST_DATA : return new RsTurtleGenericFastDataItem(); case RS_TURTLE_SUBTYPE_GENERIC_SEARCH_REQUEST : return new RsTurtleGenericSearchRequestItem(); case RS_TURTLE_SUBTYPE_GENERIC_SEARCH_RESULT : return new RsTurtleGenericSearchResultItem(); @@ -244,8 +245,12 @@ void RsTurtleTunnelOkItem::serial_process(RsGenericSerializer::SerializeJob j,Rs void RsTurtleGenericDataItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { RsTypeSerializer::serial_process(j,ctx,tunnel_id ,"tunnel_id") ; - RsTypeSerializer::TlvMemBlock_proxy prox(data_bytes,data_size) ; - + RsTypeSerializer::serial_process(j,ctx,prox,"data bytes") ; +} +void RsTurtleGenericFastDataItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) +{ + RsTypeSerializer::serial_process(j,ctx,tunnel_id ,"tunnel_id") ; + RsTypeSerializer::TlvMemBlock_proxy prox(data_bytes,data_size) ; RsTypeSerializer::serial_process(j,ctx,prox,"data bytes") ; } diff --git a/libretroshare/src/turtle/rsturtleitem.h b/libretroshare/src/turtle/rsturtleitem.h index 5f29ca0ea..842a32aa9 100644 --- a/libretroshare/src/turtle/rsturtleitem.h +++ b/libretroshare/src/turtle/rsturtleitem.h @@ -52,6 +52,7 @@ const uint8_t RS_TURTLE_SUBTYPE_FILE_MAP_REQUEST = 0x11 ; // const uint8_t RS_TURTLE_SUBTYPE_FILE_CRC_REQUEST = 0x13 ; const uint8_t RS_TURTLE_SUBTYPE_CHUNK_CRC = 0x14 ; const uint8_t RS_TURTLE_SUBTYPE_CHUNK_CRC_REQUEST = 0x15 ; +const uint8_t RS_TURTLE_SUBTYPE_GENERIC_FAST_DATA = 0x16 ; class TurtleSearchRequestInfo ; @@ -331,6 +332,28 @@ class RsTurtleGenericDataItem: public RsTurtleGenericTunnelItem void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx); }; +// Same, but with a fact priority. Can rather be used for e.g. distant chat. +// +class RsTurtleGenericFastDataItem: public RsTurtleGenericTunnelItem +{ + public: + RsTurtleGenericFastDataItem() : RsTurtleGenericTunnelItem(RS_TURTLE_SUBTYPE_GENERIC_FAST_DATA), data_size(0), data_bytes(0) { setPriorityLevel(QOS_PRIORITY_RS_TURTLE_GENERIC_FAST_DATA);} + virtual ~RsTurtleGenericFastDataItem() { if(data_bytes != NULL) free(data_bytes) ; } + + virtual bool shouldStampTunnel() const { return true ; } + + uint32_t data_size ; + void *data_bytes ; + + void clear() + { + free(data_bytes) ; + data_bytes = NULL ; + data_size = 0; + } + protected: + void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx); +}; /***********************************************************************************/ /* Turtle Serialiser class */ /***********************************************************************************/ diff --git a/libretroshare/src/util/rsmemory.h b/libretroshare/src/util/rsmemory.h index b46e12ad1..7b475bcaf 100644 --- a/libretroshare/src/util/rsmemory.h +++ b/libretroshare/src/util/rsmemory.h @@ -3,7 +3,8 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012-2012 by Cyril Soler * + * Copyright 2012 Cyril Soler * + * Copyright 2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -21,9 +22,45 @@ *******************************************************************************/ #pragma once -#include +#include #include -#include +#include + +#include "util/stacktrace.h" + +/** + * @brief Shorthand macro to declare optional functions output parameters + * To define an optional output paramether use the following syntax + * +\code{.cpp} +bool myFunnyFunction( + int mandatoryParamether, + BigType& myOptionalOutput = RS_DEFAULT_STORAGE_PARAM(BigType) ) +\endcode + * + * The function caller then can call myFunnyFunction either passing + * myOptionalOutput parameter or not. + * @see RsGxsChannels methods for real usage examples. + * + * @details + * When const references are used to pass function parameters it is easy do make + * those params optional by defining a default value in the function + * declaration, because a temp is accepted as default parameter in those cases. + * It is not as simple when one want to make optional a non-const reference + * parameter that is usually used as output, in that case as a temp is in theory + * not acceptable. + * Yet it is possible to overcome that limitation with the following trick: + * If not passed as parameter the storage for the output parameter can be + * dinamically allocated directly by the function call, to avoid leaking memory + * on each function call the pointer to that storage is made unique so once the + * function returns it goes out of scope and is automatically deleted. + * About performance overhead: std::unique_ptr have very good performance and + * modern compilers may be even able to avoid the dynamic allocation in this + * case, any way the allocation would only happen if the parameter is not + * passed, so any effect on performace would happen only in case where the + * function is called without the parameter. + */ +#define RS_DEFAULT_STORAGE_PARAM(Type) *std::unique_ptr(new Type) void *rs_malloc(size_t size) ; diff --git a/libretroshare/src/util/rsthreads.cc b/libretroshare/src/util/rsthreads.cc index 1e3196036..4d6189d12 100644 --- a/libretroshare/src/util/rsthreads.cc +++ b/libretroshare/src/util/rsthreads.cc @@ -201,7 +201,7 @@ void RsThread::start(const std::string &threadName) if(threadName.length() > 15) { #ifdef DEBUG_THREADS - THREAD_DEBUG << "RsThread::start called with to long name '" << name << "' truncating..." << std::endl; + THREAD_DEBUG << "RsThread::start called with to long name '" << threadName << "' truncating..." << std::endl; #endif RS_pthread_setname_np(mTid, threadName.substr(0, 15).c_str()); } else { diff --git a/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp index b94dc2b6e..eba42e4ff 100644 --- a/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp @@ -329,13 +329,15 @@ void SearchDialog::searchResultWidgetCustomPopupMenu( QPoint /*point*/ ) QMenu contextMnu(this) ; contextMnu.addAction(QIcon(IMAGE_START), tr("Download"), this, SLOT(download())) ; - contextMnu.addAction(QIcon(IMAGE_BANFILE), tr("Mark as bad"), this, SLOT(ban())) ; contextMnu.addSeparator();//-------------------------------------- contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyResultLink())) ; contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Send RetroShare Link"), this, SLOT(sendLinkTo())) ; contextMnu.addSeparator();//-------------------------------------- + contextMnu.addAction(QIcon(IMAGE_BANFILE), tr("Mark as bad"), this, SLOT(ban())) ; + contextMnu.addSeparator();//-------------------------------------- + QMenu collectionMenu(tr("Collection"), this); collectionMenu.setIcon(QIcon(IMAGE_LIBRARY)); collectionMenu.addAction(collCreateAct); diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 703405bb0..d9e06df09 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -1878,6 +1878,7 @@ void IdDialog::insertIdDetails(uint32_t token) if (isOwnId) { mStateHelper->setWidgetEnabled(ui->ownOpinion_CB, false); + mStateHelper->setWidgetEnabled(ui->autoBanIdentities_CB, false); ui->editIdentity->setEnabled(true); ui->removeIdentity->setEnabled(true); ui->chatIdentity->setEnabled(false); @@ -1887,6 +1888,7 @@ void IdDialog::insertIdDetails(uint32_t token) { // No Reputation yet! mStateHelper->setWidgetEnabled(ui->ownOpinion_CB, true); + mStateHelper->setWidgetEnabled(ui->autoBanIdentities_CB, true); ui->editIdentity->setEnabled(false); ui->removeIdentity->setEnabled(false); ui->chatIdentity->setEnabled(true); @@ -2086,7 +2088,7 @@ void IdDialog::modifyReputation() case 2: op = RsOpinion::POSITIVE; break; default: std::cerr << "Wrong value from opinion combobox. Bug??" << std::endl; - break; + return; } rsReputations->setOwnOpinion(id,op); diff --git a/retroshare-gui/src/gui/Identity/IdDialog.ui b/retroshare-gui/src/gui/Identity/IdDialog.ui index e571555a1..c940f763f 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.ui +++ b/retroshare-gui/src/gui/Identity/IdDialog.ui @@ -117,7 +117,7 @@ Qt::NoFocus - + :/icons/help_64.png:/icons/help_64.png @@ -284,7 +284,7 @@ 0 0 1372 - 1000 + 999 @@ -562,7 +562,11 @@ border-image: url(:/images/closepressed.png) - + + + true + + @@ -646,7 +650,11 @@ p, li { white-space: pre-wrap; } - + + + true + + @@ -891,7 +899,11 @@ p, li { white-space: pre-wrap; } - + + + true + + @@ -1087,8 +1099,8 @@ p, li { white-space: pre-wrap; } idTreeWidget - + diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index 55a907fbb..3a81877ce 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -40,7 +40,7 @@ #include "gui/FileTransfer/SearchDialog.h" #include "gui/FileTransfer/SharedFilesDialog.h" #include "gui/FileTransfer/TransfersDialog.h" -#include "MessagesDialog.h" +#include "gui/msgs/MessagesDialog.h" #include "PluginsPage.h" #include "NewsFeed.h" #include "ShareManager.h" @@ -730,9 +730,12 @@ void MainWindow::updateStatus() float downKb = 0; float upKb = 0; rsConfig->GetCurrentDataRates(downKb, upKb); + uint64_t down = 0; + uint64_t up = 0; + rsConfig->GetTrafficSum(down, up); if (ratesstatus) - ratesstatus->getRatesStatus(downKb, upKb); + ratesstatus->getRatesStatus(downKb, down, upKb, up); if(torstatus) torstatus->getTorStatus(); diff --git a/retroshare-gui/src/gui/Posted/PostedDialog.cpp b/retroshare-gui/src/gui/Posted/PostedDialog.cpp index 419339bcc..f90ed4466 100644 --- a/retroshare-gui/src/gui/Posted/PostedDialog.cpp +++ b/retroshare-gui/src/gui/Posted/PostedDialog.cpp @@ -35,6 +35,7 @@ public: PostedGroupInfoData() : RsUserdata() {} public: + QMap mIcon; QMap mDescription; }; @@ -102,17 +103,17 @@ QString PostedDialog::icon(IconType type) case ICON_NEW: return ":/icons/png/add.png"; case ICON_YOUR_GROUP: - return ":/icons/png/feedreader.png"; + return ""; case ICON_SUBSCRIBED_GROUP: - return ":/icons/png/feed-subscribed.png"; + return ""; case ICON_POPULAR_GROUP: - return ":/icons/png/feed-popular.png"; + return ""; case ICON_OTHER_GROUP: return ":/icons/png/feed-other.png"; case ICON_SEARCH: return ":/images/find.png"; case ICON_DEFAULT: - return ""; + return ":/icons/png/posted.png"; } return ""; @@ -161,6 +162,12 @@ void PostedDialog::loadGroupSummaryToken(const uint32_t &token, std::listmIcon[group.mMeta.mGroupId] = image; + } if (!group.mDescription.empty()) { postedData->mDescription[group.mMeta.mGroupId] = QString::fromUtf8(group.mDescription.c_str()); @@ -183,4 +190,9 @@ void PostedDialog::groupInfoToGroupItemInfo(const RsGroupMetaData &groupInfo, Gr if (descriptionIt != postedData->mDescription.end()) { groupItemInfo.description = descriptionIt.value(); } + + QMap::const_iterator iconIt = postedData->mIcon.find(groupInfo.mGroupId); + if (iconIt != postedData->mIcon.end()) { + groupItemInfo.icon = iconIt.value(); + } } diff --git a/retroshare-gui/src/gui/Posted/PostedGroupDialog.cpp b/retroshare-gui/src/gui/Posted/PostedGroupDialog.cpp index 031ce7da0..6fcb14024 100644 --- a/retroshare-gui/src/gui/Posted/PostedGroupDialog.cpp +++ b/retroshare-gui/src/gui/Posted/PostedGroupDialog.cpp @@ -17,6 +17,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ +#include #include "PostedGroupDialog.h" @@ -25,7 +26,7 @@ const uint32_t PostedCreateEnabledFlags = ( GXS_GROUP_FLAGS_NAME | - // GXS_GROUP_FLAGS_ICON | + GXS_GROUP_FLAGS_ICON | GXS_GROUP_FLAGS_DESCRIPTION | GXS_GROUP_FLAGS_DISTRIBUTION | // GXS_GROUP_FLAGS_PUBLISHSIGN | @@ -90,14 +91,31 @@ QPixmap PostedGroupDialog::serviceImage() return QPixmap(":/icons/png/posted.png"); } +void PostedGroupDialog::preparePostedGroup(RsPostedGroup &group, const RsGroupMetaData &meta) +{ + group.mMeta = meta; + group.mDescription = getDescription().toUtf8().constData(); + + QPixmap pixmap = getLogo(); + + if (!pixmap.isNull()) { + QByteArray ba; + QBuffer buffer(&ba); + + buffer.open(QIODevice::WriteOnly); + pixmap.save(&buffer, "PNG"); // writes image into ba in PNG format + + group.mGroupImage.copy((uint8_t *) ba.data(), ba.size()); + } else { + group.mGroupImage.clear(); + } +} + bool PostedGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta) { // Specific Function. RsPostedGroup grp; - grp.mMeta = meta; - grp.mDescription = getDescription().toStdString(); - std::cerr << "PostedGroupDialog::service_CreateGroup() storing to Queue"; - std::cerr << std::endl; + preparePostedGroup(grp, meta); rsPosted->createGroup(token, grp); @@ -107,8 +125,7 @@ bool PostedGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMetaDa bool PostedGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta) { RsPostedGroup grp; - grp.mMeta = editedMeta; - grp.mDescription = getDescription().toUtf8().constData(); + preparePostedGroup(grp, editedMeta); std::cerr << "PostedGroupDialog::service_EditGroup() submitting changes"; std::cerr << std::endl; @@ -140,8 +157,18 @@ bool PostedGroupDialog::service_loadGroup(uint32_t token, Mode /*mode*/, RsGroup std::cerr << "PostedGroupDialog::service_loadGroup() Unfinished Loading"; std::cerr << std::endl; - groupMetaData = groups[0].mMeta; - description = QString::fromUtf8(groups[0].mDescription.c_str()); + const RsPostedGroup &group = groups[0]; + groupMetaData = group.mMeta; + description = QString::fromUtf8(group.mDescription.c_str()); + + if (group.mGroupImage.mData) { + QPixmap pixmap; + if (pixmap.loadFromData(group.mGroupImage.mData, group.mGroupImage.mSize, "PNG")) { + setLogo(pixmap); + } + } else { + setLogo(QPixmap(":/icons/png/posted.png")); + } return true; } diff --git a/retroshare-gui/src/gui/Posted/PostedGroupDialog.h b/retroshare-gui/src/gui/Posted/PostedGroupDialog.h index c96da3ebf..c0d860b96 100644 --- a/retroshare-gui/src/gui/Posted/PostedGroupDialog.h +++ b/retroshare-gui/src/gui/Posted/PostedGroupDialog.h @@ -39,6 +39,9 @@ protected: virtual bool service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta); virtual bool service_loadGroup(uint32_t token, Mode mode, RsGroupMetaData& groupMetaData, QString &description); virtual bool service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta); + +private: + void preparePostedGroup(RsPostedGroup &group, const RsGroupMetaData &meta); }; #endif diff --git a/retroshare-gui/src/gui/Posted/PostedItem.cpp b/retroshare-gui/src/gui/Posted/PostedItem.cpp index 4a38c2a5a..9ad0aa839 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.cpp +++ b/retroshare-gui/src/gui/Posted/PostedItem.cpp @@ -109,7 +109,18 @@ void PostedItem::setup() QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this); connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(copyMessageLink())); - + + + int S = QFontMetricsF(font()).height() ; + + ui->voteUpButton->setIconSize(QSize(S*1.5,S*1.5)); + ui->voteDownButton->setIconSize(QSize(S*1.5,S*1.5)); + ui->commentButton->setIconSize(QSize(S*1.5,S*1.5)); + ui->expandButton->setIconSize(QSize(S*1.5,S*1.5)); + ui->notesButton->setIconSize(QSize(S*1.5,S*1.5)); + ui->readButton->setIconSize(QSize(S*1.5,S*1.5)); + ui->shareButton->setIconSize(QSize(S*1.5,S*1.5)); + QMenu *menu = new QMenu(); menu->addAction(CopyLinkAction); ui->shareButton->setMenu(menu); @@ -235,20 +246,25 @@ void PostedItem::fill() return; } + QPixmap sqpixmap2 = QPixmap(":/images/thumb-default.png"); + mInFill = true; - + int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height()); + int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height(); + if(mPost.mImage.mData != NULL) { QPixmap pixmap; pixmap.loadFromData(mPost.mImage.mData, mPost.mImage.mSize, "PNG"); // Wiping data - as its been passed to thumbnail. - QPixmap sqpixmap = pixmap.scaled(800, 600, Qt::KeepAspectRatio, Qt::SmoothTransformation); - ui->pictureLabel->setPixmap(sqpixmap); - - ui->thumbnailLabel->setPixmap(pixmap); - }else + QPixmap sqpixmap = pixmap.scaled(desired_width,desired_height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + ui->thumbnailLabel->setPixmap(sqpixmap); + ui->pictureLabel->setPixmap(pixmap); + } + else { + //ui->thumbnailLabel->setFixedSize(desired_width,desired_height); ui->expandButton->setDisabled(true); } @@ -290,7 +306,7 @@ void PostedItem::fill() urlstr += QString(" "); QString siteurl = url.scheme() + "://" + url.host(); - sitestr = QString(" %2 ").arg(siteurl).arg(siteurl); + sitestr = QString(" %2 ").arg(siteurl).arg(siteurl); ui->titleLabel->setText(urlstr); }else diff --git a/retroshare-gui/src/gui/Posted/PostedItem.ui b/retroshare-gui/src/gui/Posted/PostedItem.ui index af284a7a0..250f43007 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.ui +++ b/retroshare-gui/src/gui/Posted/PostedItem.ui @@ -6,8 +6,8 @@ 0 0 - 617 - 190 + 825 + 337 @@ -67,6 +67,12 @@ + + + 37 + 0 + + @@ -100,12 +106,6 @@ 0 - - - 24 - 24 - - Vote up @@ -116,12 +116,6 @@ :/images/up-arrow.png:/images/up-arrow.png - - - 16 - 16 - - true @@ -161,12 +155,6 @@ :/images/down-arrow.png:/images/down-arrow.png - - - 16 - 16 - - true @@ -198,6 +186,12 @@ + + + 0 + 0 + + 100 @@ -312,7 +306,7 @@ - 3 + 5 0 @@ -371,25 +365,6 @@ - - - - - 0 - 0 - - - - - 75 - true - - - - Site - - - @@ -426,6 +401,9 @@ + + 6 + 0 @@ -705,8 +683,8 @@ - + diff --git a/retroshare-gui/src/gui/Posted/PostedListWidget.cpp b/retroshare-gui/src/gui/Posted/PostedListWidget.cpp index be8e0ad69..2d8d2cfd9 100644 --- a/retroshare-gui/src/gui/Posted/PostedListWidget.cpp +++ b/retroshare-gui/src/gui/Posted/PostedListWidget.cpp @@ -26,8 +26,12 @@ #include "PostedCreatePostDialog.h" #include "PostedItem.h" #include "gui/common/UIStateHelper.h" +#include "gui/RetroShareLink.h" +#include "util/HandleRichText.h" +#include "util/DateTime.h" #include +#include "retroshare/rsgxscircles.h" #define POSTED_DEFAULT_LISTING_LENGTH 10 #define POSTED_MAX_INDEX 10000 @@ -65,12 +69,19 @@ PostedListWidget::PostedListWidget(const RsGxsGroupId &postedId, QWidget *parent /* fill in the available OwnIds for signing */ ui->idChooser->loadIds(IDCHOOSER_ID_REQUIRED, RsGxsId()); + + int S = QFontMetricsF(font()).height() ; + + ui->submitPostButton->setIconSize(QSize(S*1.5,S*1.5)); + ui->comboBox->setIconSize(QSize(S*1.5,S*1.5)); connect(ui->submitPostButton, SIGNAL(clicked()), this, SLOT(newPost())); ui->subscribeToolButton->setToolTip(tr( "

Subscribing to the links will gather \ available posts from your subscribed friends, and make the \ links visible to all other friends.

Afterwards you can unsubscribe from the context menu of the links list at left.

")); + + ui->infoframe->hide(); /* load settings */ processSettings(true); @@ -293,6 +304,67 @@ void PostedListWidget::insertPostedDetails(const RsPostedGroup &group) mStateHelper->setWidgetEnabled(ui->submitPostButton, IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)); ui->subscribeToolButton->setSubscribed(IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)); ui->subscribeToolButton->setHidden(IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) ; + + RetroShareLink link; + + if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) { + + ui->infoframe->hide(); + + } else { + + ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); + + if(group.mMeta.mLastPost==0) + ui->infoLastPost->setText(tr("Never")); + else + + ui->infoLastPost->setText(DateTime::formatLongDateTime(group.mMeta.mLastPost)); + + QString formatDescription = QString::fromUtf8(group.mDescription.c_str()); + + unsigned int formatFlag = RSHTML_FORMATTEXT_EMBED_LINKS; + + formatDescription = RsHtml().formatText(NULL, formatDescription, formatFlag); + + ui->infoDescription->setText(formatDescription); + + ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; + + link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); + ui->infoAdministrator->setText(link.toHtml()); + + QString distrib_string ( "[unknown]" ); + + switch(group.mMeta.mCircleType) + { + case GXS_CIRCLE_TYPE_PUBLIC: distrib_string = tr("Public") ; + break ; + case GXS_CIRCLE_TYPE_EXTERNAL: + { + RsGxsCircleDetails det ; + + // !! What we need here is some sort of CircleLabel, which loads the circle and updates the label when done. + + if(rsGxsCircles->getCircleDetails(group.mMeta.mCircleId,det)) + distrib_string = tr("Restricted to members of circle \"")+QString::fromUtf8(det.mCircleName.c_str()) +"\""; + else + distrib_string = tr("Restricted to members of circle ")+QString::fromStdString(group.mMeta.mCircleId.toStdString()) ; + } + break ; + case GXS_CIRCLE_TYPE_YOUR_EYES_ONLY: distrib_string = tr("Your eyes only"); + break ; + case GXS_CIRCLE_TYPE_LOCAL: distrib_string = tr("You and your friend nodes"); + break ; + default: + std::cerr << "(EE) badly initialised group distribution ID = " << group.mMeta.mCircleType << std::endl; + } + + ui->infoDistribution->setText(distrib_string); + + ui->infoframe->show(); + + } } /*********************** **** **** **** ***********************/ diff --git a/retroshare-gui/src/gui/Posted/PostedListWidget.ui b/retroshare-gui/src/gui/Posted/PostedListWidget.ui index c96951823..13b3e208d 100644 --- a/retroshare-gui/src/gui/Posted/PostedListWidget.ui +++ b/retroshare-gui/src/gui/Posted/PostedListWidget.ui @@ -205,6 +205,186 @@
+ + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + false + + + + + + Topic Details + + + false + + + false + + + + + + 6 + + + + + + 75 + true + + + + Administrator: + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Posts: + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Last Post: + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Description</span></p></body></html> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + true + + + true + + + + + + + + 75 + true + + + + Description: + + + + + + + unknown + + + true + + + + + + + 0 + + + + + + + unknown + + + + + + + + 75 + true + + + + Distribution: + + + + + + + unknown + + + + + + + + + + + @@ -258,6 +438,11 @@ QComboBox
gui/gxs/GxsIdChooser.h
+ + GxsIdLabel + QLabel +
gui/gxs/GxsIdLabel.h
+
diff --git a/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.cpp b/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.cpp index 0b989ee2c..81cb47846 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.cpp +++ b/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.cpp @@ -291,7 +291,7 @@ bool ChatLobbyUserNotify::checkWord(QString message, QString word) && (!word.isEmpty())) { QString eow=" ~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="; // end of word bool bFirstCharEOW = (nFound==0)?true:(eow.indexOf(message.at(nFound-1)) != -1); - bool bLastCharEOW = ((nFound+word.length()-1) < message.length()) + bool bLastCharEOW = (nFound+word.length() < message.length()) ?(eow.indexOf(message.at(nFound+word.length())) != -1) :true; bFound = (bFirstCharEOW && bLastCharEOW); diff --git a/retroshare-gui/src/gui/feeds/PostedGroupItem.cpp b/retroshare-gui/src/gui/feeds/PostedGroupItem.cpp index 97c97c195..542e97a11 100644 --- a/retroshare-gui/src/gui/feeds/PostedGroupItem.cpp +++ b/retroshare-gui/src/gui/feeds/PostedGroupItem.cpp @@ -134,14 +134,23 @@ void PostedGroupItem::fill() // ui->nameLabel->setText(groupName()); ui->descLabel->setText(QString::fromUtf8(mGroup.mDescription.c_str())); - - //TODO - nice icon for subscribed group - if (IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) { - ui->logoLabel->setPixmap(QPixmap(":/images/posted_64.png")); + + if (mGroup.mGroupImage.mData != NULL) { + QPixmap postedImage; + postedImage.loadFromData(mGroup.mGroupImage.mData, mGroup.mGroupImage.mSize, "PNG"); + ui->logoLabel->setPixmap(QPixmap(postedImage)); } else { ui->logoLabel->setPixmap(QPixmap(":/images/posted_64.png")); } + + //TODO - nice icon for subscribed group +// if (IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) { +// ui->logoLabel->setPixmap(QPixmap(":/images/posted_64.png")); +// } else { +// ui->logoLabel->setPixmap(QPixmap(":/images/posted_64.png")); +// } + if (IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { ui->subscribeButton->setEnabled(false); } else { diff --git a/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp b/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp index 99f363f8d..57fc7031b 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp @@ -49,6 +49,15 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic connect(ui->refreshButton, SIGNAL(clicked()), this, SLOT(refresh())); connect(ui->idChooser, SIGNAL(currentIndexChanged( int )), this, SLOT(voterSelectionChanged( int ))); connect(ui->idChooser, SIGNAL(idsLoaded()), this, SLOT(idChooserReady())); + + connect(ui->sortBox, SIGNAL(currentIndexChanged(int)), this, SLOT(sortComments(int))); + + // default sort method "HOT". + ui->treeWidget->sortByColumn(4, Qt::DescendingOrder); + + int S = QFontMetricsF(font()).height() ; + + ui->sortBox->setIconSize(QSize(S*1.5,S*1.5)); } GxsCommentDialog::~GxsCommentDialog() @@ -141,3 +150,22 @@ void GxsCommentDialog::setCommentHeader(QWidget *header) ui->notesBrowser->setPlainText(QString::fromStdString(mCurrentPost.mNotes)); #endif } + +void GxsCommentDialog::sortComments(int i) +{ + + switch(i) + { + default: + case 0: + ui->treeWidget->sortByColumn(4, Qt::DescendingOrder); + break; + case 1: + ui->treeWidget->sortByColumn(2, Qt::DescendingOrder); + break; + case 2: + ui->treeWidget->sortByColumn(3, Qt::DescendingOrder); + break; + } + +} diff --git a/retroshare-gui/src/gui/gxs/GxsCommentDialog.h b/retroshare-gui/src/gui/gxs/GxsCommentDialog.h index 1a6999880..82d2fdbd1 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsCommentDialog.h @@ -45,6 +45,7 @@ private slots: void refresh(); void idChooserReady(); void voterSelectionChanged( int index ); + void sortComments(int); private: RsGxsGroupId mGrpId; diff --git a/retroshare-gui/src/gui/gxs/GxsCommentDialog.ui b/retroshare-gui/src/gui/gxs/GxsCommentDialog.ui index 7961136c0..6bf6b1112 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentDialog.ui +++ b/retroshare-gui/src/gui/gxs/GxsCommentDialog.ui @@ -13,8 +13,8 @@ Form - - + + @@ -24,59 +24,63 @@ - 9 + 1 1 + + 1 + 1 - + - - - Hot + + + <html><head/><body><p><span style=" font-family:'-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol'; font-size:14px; color:#24292e; background-color:#ffffff;">sort by</span></p></body></html> - - true + + - - true - - - - - - - New - - - true - - - true - - - - - - - Top - - - true - - - false - - - true + + + 24 + 24 + + + + Hot + + + + :/icons/png/flame.png:/icons/png/flame.png + + + + + New + + + + :/icons/png/new.png:/icons/png/new.png + + + + + Top + + + + :/icons/png/top.png:/icons/png/top.png + + @@ -111,8 +115,11 @@ - + + + true + Comment @@ -164,6 +171,8 @@
gui/gxs/GxsCommentTreeWidget.h
- + + + diff --git a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp index dc1734556..296ce68c8 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp +++ b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp @@ -31,6 +31,7 @@ #include "gui/gxs/GxsCommentTreeWidget.h" #include "gui/gxs/GxsCreateCommentDialog.h" #include "gui/gxs/GxsIdTreeWidgetItem.h" +#include "gui/common/RSTreeWidgetItem.h" #include @@ -45,6 +46,7 @@ #define PCITEM_COLUMN_PARENTID 8 #define PCITEM_COLUMN_AUTHORID 9 +#define ROLE_SORT Qt::UserRole + 1 #define GXSCOMMENTS_LOADTHREAD 1 @@ -139,6 +141,9 @@ GxsCommentTreeWidget::GxsCommentTreeWidget(QWidget *parent) setWordWrap(true); setItemDelegateForColumn(PCITEM_COLUMN_COMMENT,new MultiLinesCommentDelegate(QFontMetricsF(font()))) ; + + commentsRole = new RSTreeWidgetItemCompareRole; + commentsRole->setRole(PCITEM_COLUMN_DATE, ROLE_SORT); // QFont font = QFont("ARIAL", 10); // font.setBold(true); @@ -537,6 +542,8 @@ void GxsCommentTreeWidget::service_loadThread(const uint32_t &token) text = qtime.toString("yyyy-MM-dd hh:mm:ss") ; item->setText(PCITEM_COLUMN_DATE, text) ; item->setToolTip(PCITEM_COLUMN_DATE, text) ; + item->setData(PCITEM_COLUMN_DATE, ROLE_SORT, QVariant(qlonglong(comment.mMeta.mPublishTs))); + } text = QString::fromUtf8(comment.mComment.c_str()); diff --git a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.h b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.h index a7e25df6e..a1977ab93 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.h +++ b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.h @@ -27,6 +27,8 @@ #include #include +class RSTreeWidgetItemCompareRole; + class GxsCommentTreeWidget : public QTreeWidget, public TokenResponse { Q_OBJECT @@ -96,6 +98,8 @@ protected: std::map mLoadingMap; std::multimap mPendingInsertMap; + + RSTreeWidgetItemCompareRole *commentsRole; TokenQueue *mTokenQueue; RsTokenService *mRsTokenService; diff --git a/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp index 4bf950a5e..fb50f9acc 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp @@ -755,12 +755,12 @@ void GxsGroupDialog::setGroupSignFlags(uint32_t signFlags) // (cyril) very weird piece of code. Need to clear this up. ui.comments_allowed->setChecked(true); - ui.commentsValueLabel->setText("Allowed") ; + ui.commentsValueLabel->setText("Allowed") ; } else { ui.comments_no->setChecked(true); - ui.commentsValueLabel->setText("Allowed") ; + ui.commentsValueLabel->setText("Forbidden") ; } } diff --git a/retroshare-gui/src/gui/gxs/GxsGroupDialog.ui b/retroshare-gui/src/gui/gxs/GxsGroupDialog.ui index 3cd4a50c6..9c6a9b662 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupDialog.ui +++ b/retroshare-gui/src/gui/gxs/GxsGroupDialog.ui @@ -7,7 +7,7 @@ 0 0 600 - 736 + 633
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp index b33c49b60..6fb832a69 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp @@ -61,11 +61,15 @@ const uint32_t ChannelEditDefaultsFlags = ChannelCreateDefaultsFlags; GxsChannelGroupDialog::GxsChannelGroupDialog(TokenQueue *tokenQueue, QWidget *parent) : GxsGroupDialog(tokenQueue, ChannelCreateEnabledFlags, ChannelCreateDefaultsFlags, parent) { + ui.commentGroupBox->setEnabled(false); // These are here because comments_allowed are actually not used yet, so the group will not be changed by the setting and when + ui.comments_allowed->setChecked(true); // the group info is displayed it will therefore be set to "disabled" in all cases although it is enabled. } GxsChannelGroupDialog::GxsChannelGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent) : GxsGroupDialog(tokenExternalQueue, tokenService, mode, groupId, ChannelEditEnabledFlags, ChannelEditDefaultsFlags, parent) { + ui.commentGroupBox->setEnabled(false); // These are here because comments_allowed are actually not used yet, so the group will not be changed by the setting and when + ui.comments_allowed->setChecked(true); // the group info is displayed it will therefore be set to "disabled" in all cases although it is enabled. } void GxsChannelGroupDialog::initUi() diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui index c175d95d8..850f81c8f 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui @@ -294,7 +294,7 @@ 3 - 3 + 0 3 @@ -304,211 +304,176 @@ - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 178 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 178 - - - - - - - - - 255 - 255 - 178 - - - - - - - 255 - 255 - 178 - - - - - - - true + false - QFrame::Box + QFrame::NoFrame QFrame::Plain + + 6 + + + 6 + + + 6 + + + 6 + - - - - - - 75 - true - - - - Administrator: - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Posts (at neighbor nodes): - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Last Post: - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + Channel details + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 75 + true + + + + Administrator: + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Posts: + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Last Post: + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Description</span></p></body></html> - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - true - - - true - - - - - - - - 75 - true - - - - Description: - - - - - - - unknown - - - true - - - - - - - 0 - - - - - - - unknown - - - - - - - - 75 - true - - - - Distribution: - - - - - - - unknown - - - - + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + true + + + true + + + + + + + + 75 + true + + + + Description: + + + + + + + unknown + + + true + + + + + + + 0 + + + + + + + unknown + + + + + + + + 75 + true + + + + Distribution: + + + + + + + unknown + + + + +
diff --git a/retroshare-gui/src/gui/msgs/MessageModel.cpp b/retroshare-gui/src/gui/msgs/MessageModel.cpp index 3f30b2342..441b02bbc 100644 --- a/retroshare-gui/src/gui/msgs/MessageModel.cpp +++ b/retroshare-gui/src/gui/msgs/MessageModel.cpp @@ -336,11 +336,11 @@ bool RsMessageModel::passesFilter(const Rs::Msgs::MsgInfoSummary& fmpe,int colum || (std::find(fmpe.msgtags.begin(),fmpe.msgtags.end(),mQuickViewFilter) != fmpe.msgtags.end()) || (mQuickViewFilter==QUICK_VIEW_STARRED && (fmpe.msgflags & RS_MSG_STAR)) || (mQuickViewFilter==QUICK_VIEW_SYSTEM && (fmpe.msgflags & RS_MSG_SYSTEM)); - - std::cerr << "Passes filter: type=" << mFilterType << " s=\"" << s.toStdString() - << "MsgFlags=" << fmpe.msgflags << " msgtags=" ; +#ifdef DEBUG_MESSAGE_MODEL + std::cerr << "Passes filter: type=" << mFilterType << " s=\"" << s.toStdString() << "MsgFlags=" << fmpe.msgflags << " msgtags=" ; foreach(uint32_t i,fmpe.msgtags) std::cerr << i << " " ; std::cerr << "\" strings:" << passes_strings << " quick_view:" << passes_quick_view << std::endl; +#endif return passes_quick_view && passes_strings; } @@ -621,10 +621,10 @@ void RsMessageModel::getMessageSummaries(BoxName box,std::listmsgflags & RS_MSG_BOXMASK) == RS_MSG_INBOX ; break ; - case BOX_SENT : ok = (it->msgflags & RS_MSG_BOXMASK) == RS_MSG_SENTBOX; break ; - case BOX_OUTBOX : ok = (it->msgflags & RS_MSG_BOXMASK) == RS_MSG_OUTBOX ; break ; - case BOX_DRAFTS : ok = (it->msgflags & RS_MSG_BOXMASK) == RS_MSG_DRAFTBOX ; break ; + case BOX_INBOX : ok = (it->msgflags & RS_MSG_BOXMASK) == RS_MSG_INBOX && !(it->msgflags & RS_MSG_TRASH); break ; + case BOX_SENT : ok = (it->msgflags & RS_MSG_BOXMASK) == RS_MSG_SENTBOX && !(it->msgflags & RS_MSG_TRASH); break ; + case BOX_OUTBOX : ok = (it->msgflags & RS_MSG_BOXMASK) == RS_MSG_OUTBOX && !(it->msgflags & RS_MSG_TRASH); break ; + case BOX_DRAFTS : ok = (it->msgflags & RS_MSG_BOXMASK) == RS_MSG_DRAFTBOX && !(it->msgflags & RS_MSG_TRASH); break ; case BOX_TRASH : ok = (it->msgflags & RS_MSG_TRASH) ; break ; default: ++it; diff --git a/retroshare-gui/src/gui/MessagesDialog.cpp b/retroshare-gui/src/gui/msgs/MessagesDialog.cpp similarity index 99% rename from retroshare-gui/src/gui/MessagesDialog.cpp rename to retroshare-gui/src/gui/msgs/MessagesDialog.cpp index e8c130e88..2837b5b3a 100644 --- a/retroshare-gui/src/gui/MessagesDialog.cpp +++ b/retroshare-gui/src/gui/msgs/MessagesDialog.cpp @@ -27,21 +27,21 @@ #include "MessagesDialog.h" -#include "notifyqt.h" -#include "common/TagDefs.h" -#include "common/PeerDefs.h" -#include "common/RSElidedItemDelegate.h" -#include "gxs/GxsIdTreeWidgetItem.h" -#include "gxs/GxsIdDetails.h" +#include "gui/notifyqt.h" +#include "gui/common/TagDefs.h" +#include "gui/common/PeerDefs.h" +#include "gui/common/RSElidedItemDelegate.h" +#include "gui/gxs/GxsIdTreeWidgetItem.h" +#include "gui/gxs/GxsIdDetails.h" #include "gui/Identity/IdDialog.h" #include "gui/MainWindow.h" -#include "msgs/MessageComposer.h" -#include "msgs/MessageInterface.h" -#include "msgs/MessageUserNotify.h" -#include "msgs/MessageWidget.h" -#include "msgs/TagsMenu.h" -#include "msgs/MessageModel.h" -#include "settings/rsharesettings.h" +#include "gui/msgs/MessageComposer.h" +#include "gui/msgs/MessageInterface.h" +#include "gui/msgs/MessageUserNotify.h" +#include "gui/msgs/MessageWidget.h" +#include "gui/msgs/TagsMenu.h" +#include "gui/msgs/MessageModel.h" +#include "gui/settings/rsharesettings.h" #include "util/DateTime.h" #include "util/RsProtectedTimer.h" diff --git a/retroshare-gui/src/gui/MessagesDialog.h b/retroshare-gui/src/gui/msgs/MessagesDialog.h similarity index 100% rename from retroshare-gui/src/gui/MessagesDialog.h rename to retroshare-gui/src/gui/msgs/MessagesDialog.h diff --git a/retroshare-gui/src/gui/MessagesDialog.ui b/retroshare-gui/src/gui/msgs/MessagesDialog.ui similarity index 100% rename from retroshare-gui/src/gui/MessagesDialog.ui rename to retroshare-gui/src/gui/msgs/MessagesDialog.ui diff --git a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss index a6089ab59..f1420a0a3 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss +++ b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss @@ -786,13 +786,11 @@ GenCertDialog QFrame#profileframe{ PostedListWidget QComboBox#comboBox { font: bold; - font-size: 15px; color: #0099cc; } PostedListWidget QToolButton#submitPostButton { font: bold; - font-size: 15px; } PostedListWidget QToolButton#subscribeToolButton { @@ -830,10 +828,7 @@ GxsForumThreadWidget QToolButton#subscribeToolButton:hover { GxsChannelPostsWidget QFrame#infoFrame { - border: 1px solid #DCDC41; - border-radius: 6px; - background: #FFFFD7; - background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFFFD7, stop:1 #FFFFB2); + } GxsChannelPostsWidget QToolButton#subscribeToolButton { @@ -889,10 +884,14 @@ PostedItem QFrame#voteFrame { background: #f8f9fa; } -PostedItem QFrame#mainFrame{ +PostedItem QFrame#mainFrame [new=false]{ background: white; } +PostedItem > QFrame#mainFrame[new=true] { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #F0F8FD, stop:0.8 #E6F2FD, stop: 0.81 #E6F2FD, stop: 1 #D2E7FD); +} + PostedItem QFrame#frame_picture{ background: white; } @@ -906,8 +905,7 @@ PostedItem QLabel#fromBoldLabel, QLabel#fromLabel, QLabel#dateLabel, QLabel#site color: #787c7e; } -PostedItem QToolButton#commentButton, QPushButton#shareButton, QToolButton#notesButton{ - font-size: 12px; - color: #878a8c; - font-weight: bold; -} \ No newline at end of file +GxsCommentDialog QComboBox#sortBox { + font: bold; + color: #0099cc; +} diff --git a/retroshare-gui/src/gui/statusbar/ratesstatus.cpp b/retroshare-gui/src/gui/statusbar/ratesstatus.cpp index 6d867aadc..edec6608e 100644 --- a/retroshare-gui/src/gui/statusbar/ratesstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/ratesstatus.cpp @@ -23,6 +23,7 @@ #include "ratesstatus.h" #include +#include "util/misc.h" #include @@ -47,13 +48,13 @@ RatesStatus::RatesStatus(QWidget *parent) setLayout(hbox); } -void RatesStatus::getRatesStatus(float downKb, float upKb) +void RatesStatus::getRatesStatus(float downKb, uint64_t down, float upKb, uint64_t upl) { /* set users/friends/network */ - QString normalText = QString("%1: %2 (kB/s) | %3: %4 (kB/s) ") - .arg(tr("Down")).arg(downKb, 0, 'f', 2) - .arg(tr("Up")).arg(upKb, 0, 'f', 2); + QString normalText = QString("%1: %2 kB/s (%3) | %4: %5 kB/s (%6)") + .arg(tr("Down")).arg(downKb, 0, 'f', 2).arg(misc::friendlyUnit(down)) + .arg(tr("Up")).arg(upKb, 0, 'f', 2).arg(misc::friendlyUnit(upl)); QString compactText = QString("%1|%2").arg(downKb, 0, 'f', 2).arg(upKb, 0, 'f', 2); if (statusRates) { diff --git a/retroshare-gui/src/gui/statusbar/ratesstatus.h b/retroshare-gui/src/gui/statusbar/ratesstatus.h index ade9b636c..063bc030a 100644 --- a/retroshare-gui/src/gui/statusbar/ratesstatus.h +++ b/retroshare-gui/src/gui/statusbar/ratesstatus.h @@ -32,7 +32,7 @@ class RatesStatus : public QWidget public: RatesStatus(QWidget *parent = 0); - void getRatesStatus(float downKb, float upKb); + void getRatesStatus(float downKb, uint64_t down, float upKb, uint64_t upl); void setCompactMode(bool compact) {_compactMode = compact; } private: diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index 5769274b8..70050a727 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -443,6 +443,7 @@ HEADERS += rshare.h \ gui/connect/ConfCertDialog.h \ gui/connect/PGPKeyDialog.h \ gui/connect/FriendRecommendDialog.h \ + gui/msgs/MessagesDialog.h \ gui/msgs/MessageInterface.h \ gui/msgs/MessageComposer.h \ gui/msgs/MessageWindow.h \ @@ -541,7 +542,6 @@ HEADERS += rshare.h \ gui/common/ToasterNotify.h \ gui/style/RSStyle.h \ gui/style/StyleDialog.h \ - gui/MessagesDialog.h \ gui/help/browser/helpbrowser.h \ gui/help/browser/helptextbrowser.h \ gui/statusbar/peerstatus.h \ @@ -621,7 +621,6 @@ FORMS += gui/StartDialog.ui \ gui/FriendsDialog.ui \ gui/ShareManager.ui \ # gui/ShareDialog.ui \ - gui/MessagesDialog.ui \ gui/help/browser/helpbrowser.ui \ gui/HelpDialog.ui \ gui/ServicePermissionDialog.ui \ @@ -640,6 +639,7 @@ FORMS += gui/StartDialog.ui \ gui/connect/ConnectFriendWizard.ui \ gui/connect/ConnectProgressDialog.ui \ gui/connect/FriendRecommendDialog.ui \ + gui/msgs/MessagesDialog.ui \ gui/msgs/MessageComposer.ui \ gui/msgs/MessageWindow.ui\ gui/msgs/MessageWidget.ui\ @@ -745,7 +745,6 @@ SOURCES += main.cpp \ # gui/ShareDialog.cpp \ # gui/SFListDelegate.cpp \ gui/SoundManager.cpp \ - gui/MessagesDialog.cpp \ gui/im_history/ImHistoryBrowser.cpp \ gui/im_history/IMHistoryItemDelegate.cpp \ gui/im_history/IMHistoryItemPainter.cpp \ @@ -800,6 +799,7 @@ SOURCES += main.cpp \ gui/chat/ChatLobbyUserNotify.cpp \ gui/connect/ConfCertDialog.cpp \ gui/connect/PGPKeyDialog.cpp \ + gui/msgs/MessagesDialog.cpp \ gui/msgs/MessageComposer.cpp \ gui/msgs/MessageWidget.cpp \ gui/msgs/MessageWindow.cpp \ diff --git a/retroshare.pri b/retroshare.pri index 7f687d766..2f2dac230 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -230,11 +230,18 @@ isEmpty(RS_UPNP_LIB):RS_UPNP_LIB = upnp ixml threadutil # # V07_NON_BACKWARD_COMPATIBLE_CHANGE_003: # -# What: Do not hash PGP certificate twice when signing +# What: Do not hash PGP certificate twice when signing # # Why: hasing twice is not per se a security issue, but it makes it harder to change the settings for hashing. # # Backward compat: patched peers cannot connect to non patched peers older than Nov 2017. +# +# V07_NON_BACKWARD_COMPATIBLE_CHANGE_004: +# +# What: Do not probe that GXS tunnels accept fast items. Just assume they do. +# Why: Avoids sending probe packets +# BackwardCompat: old RS before Mai 2019 will not be able to distant chat. +# ########################################################################################################################################################### #CONFIG += rs_v07_changes @@ -242,6 +249,7 @@ rs_v07_changes { DEFINES += V07_NON_BACKWARD_COMPATIBLE_CHANGE_001 DEFINES += V07_NON_BACKWARD_COMPATIBLE_CHANGE_002 DEFINES += V07_NON_BACKWARD_COMPATIBLE_CHANGE_003 + DEFINES += V07_NON_BACKWARD_COMPATIBLE_CHANGE_004 DEFINES += V07_NON_BACKWARD_COMPATIBLE_CHANGE_UNNAMED }